From 8a13820cc01fd32baa8dddc5e5d1739a06aa5c10 Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Fri, 31 Jul 2026 07:41:38 +0300 Subject: [PATCH 01/11] typst initial --- .github/workflows/ci-typ.yml | 66 + .github/workflows/ci.yml | 2 +- .vscode/settings.json | 8 + typ/appendix/glossary.typ | 159 ++ typ/assets/crates.png | Bin 0 -> 11522 bytes typ/assets/embedded-hal.svg | 2 + typ/assets/f3.jpg | Bin 0 -> 259494 bytes typ/assets/nrf52-memory-map.png | Bin 0 -> 143848 bytes typ/assets/nrf52-spi-frequency-register.png | Bin 0 -> 70351 bytes typ/assets/rust_layers.svg | 1 + typ/assets/verify.jpeg | Bin 0 -> 100961 bytes typ/book.typ | 350 +++++ typ/c-tips/index.typ | 864 +++++++++++ typ/collections/index.typ | 526 +++++++ typ/concurrency/index.typ | 1291 +++++++++++++++++ typ/config.typ | 42 + typ/design-patterns/hal/checklist.typ | 36 + typ/design-patterns/hal/gpio.typ | 293 ++++ typ/design-patterns/hal/index.typ | 32 + typ/design-patterns/hal/interoperability.typ | 116 ++ typ/design-patterns/hal/naming.typ | 25 + typ/design-patterns/hal/predictability.typ | 65 + typ/design-patterns/index.typ | 13 + typ/interoperability/c-with-rust.typ | 426 ++++++ typ/interoperability/index.typ | 149 ++ typ/interoperability/rust-with-c.typ | 229 +++ typ/intro/hardware.typ | 159 ++ typ/intro/index.typ | 563 +++++++ typ/intro/install.typ | 134 ++ typ/intro/install/linux.typ | 192 +++ typ/intro/install/macos.typ | 64 + typ/intro/install/verify.typ | 152 ++ typ/intro/install/windows.typ | 91 ++ typ/intro/no-std.typ | 267 ++++ typ/intro/tooling.typ | 501 +++++++ typ/mdbook/assets/ayu-highlight-3fdfc3ac.css | 77 + typ/mdbook/assets/book-609e4cb8.js | 870 +++++++++++ typ/mdbook/assets/clipboard-1626706a.min.js | 7 + typ/mdbook/assets/css/chrome-d279d366.css | 770 ++++++++++ typ/mdbook/assets/css/general-e96d0476.css | 464 ++++++ typ/mdbook/assets/css/print-9e4910d8.css | 50 + typ/mdbook/assets/css/variables-8adf115d.css | 383 +++++ typ/mdbook/assets/elasticlunr-ef4e11c1.min.js | 10 + typ/mdbook/assets/favicon-8114d1fc.png | Bin 0 -> 5679 bytes typ/mdbook/assets/favicon-de23e50b.svg | 22 + typ/mdbook/assets/fonts/OPEN-SANS-LICENSE.txt | 202 +++ .../assets/fonts/SOURCE-CODE-PRO-LICENSE.txt | 93 ++ typ/mdbook/assets/fonts/fonts-9644e21d.css | 100 ++ ...n-sans-v17-all-charsets-300-7736aa35.woff2 | Bin 0 -> 44352 bytes ...-v17-all-charsets-300italic-2c7b95c0.woff2 | Bin 0 -> 40656 bytes ...n-sans-v17-all-charsets-600-486c6759.woff2 | Bin 0 -> 44936 bytes ...-v17-all-charsets-600italic-1a3e8659.woff2 | Bin 0 -> 42120 bytes ...n-sans-v17-all-charsets-700-c22fe8c7.woff2 | Bin 0 -> 44988 bytes ...-v17-all-charsets-700italic-238ae959.woff2 | Bin 0 -> 40800 bytes ...n-sans-v17-all-charsets-800-3d2c812a.woff2 | Bin 0 -> 44536 bytes ...-v17-all-charsets-800italic-ba1521ec.woff2 | Bin 0 -> 40812 bytes ...ans-v17-all-charsets-italic-6c9463f7.woff2 | Bin 0 -> 41076 bytes ...ns-v17-all-charsets-regular-2e3b1d34.woff2 | Bin 0 -> 43236 bytes ...de-pro-v11-all-charsets-500-2bdd9410.woff2 | Bin 0 -> 59140 bytes typ/mdbook/assets/highlight-493f70e1.css | 83 ++ typ/mdbook/assets/highlight-abc7f01d.js | 54 + typ/mdbook/assets/mark-09e88c2c.min.js | 7 + typ/mdbook/assets/searcher-09f2665d.js | 558 +++++++ typ/mdbook/assets/searchindex-8ec871e3.js | 1 + typ/mdbook/assets/toc-7ac66f26.js | 455 ++++++ typ/mdbook/assets/tomorrow-night-4c0ae647.css | 104 ++ typ/mdbook/icons.typ | 74 + typ/mdbook/lib.typ | 418 ++++++ typ/mdbook/scripts.typ | 72 + typ/mdbook/typst.toml | 4 + typ/peripherals/a-first-attempt.typ | 309 ++++ typ/peripherals/borrowck.typ | 76 + typ/peripherals/index.typ | 221 +++ typ/peripherals/singletons.typ | 338 +++++ typ/portability/index.typ | 281 ++++ typ/start/exceptions.typ | 529 +++++++ typ/start/hardware.typ | 619 ++++++++ typ/start/index.typ | 27 + typ/start/interrupts.typ | 166 +++ typ/start/panicking.typ | 219 +++ typ/start/qemu.typ | 1159 +++++++++++++++ typ/start/registers.typ | 571 ++++++++ typ/start/semihosting.typ | 227 +++ typ/static-guarantees/design-contracts.typ | 416 ++++++ typ/static-guarantees/gpio-table.typ | 89 ++ typ/static-guarantees/index.typ | 65 + typ/static-guarantees/state-machines.typ | 184 +++ .../typestate-programming.typ | 132 ++ .../zero-cost-abstractions.typ | 117 ++ typ/unsorted/index.typ | 7 + typ/unsorted/math.typ | 113 ++ typ/unsorted/speed-vs-size.typ | 381 +++++ 92 files changed, 17941 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/ci-typ.yml create mode 100644 .vscode/settings.json create mode 100644 typ/appendix/glossary.typ create mode 100644 typ/assets/crates.png create mode 100644 typ/assets/embedded-hal.svg create mode 100644 typ/assets/f3.jpg create mode 100644 typ/assets/nrf52-memory-map.png create mode 100644 typ/assets/nrf52-spi-frequency-register.png create mode 100644 typ/assets/rust_layers.svg create mode 100644 typ/assets/verify.jpeg create mode 100644 typ/book.typ create mode 100644 typ/c-tips/index.typ create mode 100644 typ/collections/index.typ create mode 100644 typ/concurrency/index.typ create mode 100644 typ/config.typ create mode 100644 typ/design-patterns/hal/checklist.typ create mode 100644 typ/design-patterns/hal/gpio.typ create mode 100644 typ/design-patterns/hal/index.typ create mode 100644 typ/design-patterns/hal/interoperability.typ create mode 100644 typ/design-patterns/hal/naming.typ create mode 100644 typ/design-patterns/hal/predictability.typ create mode 100644 typ/design-patterns/index.typ create mode 100644 typ/interoperability/c-with-rust.typ create mode 100644 typ/interoperability/index.typ create mode 100644 typ/interoperability/rust-with-c.typ create mode 100644 typ/intro/hardware.typ create mode 100644 typ/intro/index.typ create mode 100644 typ/intro/install.typ create mode 100644 typ/intro/install/linux.typ create mode 100644 typ/intro/install/macos.typ create mode 100644 typ/intro/install/verify.typ create mode 100644 typ/intro/install/windows.typ create mode 100644 typ/intro/no-std.typ create mode 100644 typ/intro/tooling.typ create mode 100644 typ/mdbook/assets/ayu-highlight-3fdfc3ac.css create mode 100644 typ/mdbook/assets/book-609e4cb8.js create mode 100644 typ/mdbook/assets/clipboard-1626706a.min.js create mode 100644 typ/mdbook/assets/css/chrome-d279d366.css create mode 100644 typ/mdbook/assets/css/general-e96d0476.css create mode 100644 typ/mdbook/assets/css/print-9e4910d8.css create mode 100644 typ/mdbook/assets/css/variables-8adf115d.css create mode 100644 typ/mdbook/assets/elasticlunr-ef4e11c1.min.js create mode 100644 typ/mdbook/assets/favicon-8114d1fc.png create mode 100644 typ/mdbook/assets/favicon-de23e50b.svg create mode 100644 typ/mdbook/assets/fonts/OPEN-SANS-LICENSE.txt create mode 100644 typ/mdbook/assets/fonts/SOURCE-CODE-PRO-LICENSE.txt create mode 100644 typ/mdbook/assets/fonts/fonts-9644e21d.css create mode 100644 typ/mdbook/assets/fonts/open-sans-v17-all-charsets-300-7736aa35.woff2 create mode 100644 typ/mdbook/assets/fonts/open-sans-v17-all-charsets-300italic-2c7b95c0.woff2 create mode 100644 typ/mdbook/assets/fonts/open-sans-v17-all-charsets-600-486c6759.woff2 create mode 100644 typ/mdbook/assets/fonts/open-sans-v17-all-charsets-600italic-1a3e8659.woff2 create mode 100644 typ/mdbook/assets/fonts/open-sans-v17-all-charsets-700-c22fe8c7.woff2 create mode 100644 typ/mdbook/assets/fonts/open-sans-v17-all-charsets-700italic-238ae959.woff2 create mode 100644 typ/mdbook/assets/fonts/open-sans-v17-all-charsets-800-3d2c812a.woff2 create mode 100644 typ/mdbook/assets/fonts/open-sans-v17-all-charsets-800italic-ba1521ec.woff2 create mode 100644 typ/mdbook/assets/fonts/open-sans-v17-all-charsets-italic-6c9463f7.woff2 create mode 100644 typ/mdbook/assets/fonts/open-sans-v17-all-charsets-regular-2e3b1d34.woff2 create mode 100644 typ/mdbook/assets/fonts/source-code-pro-v11-all-charsets-500-2bdd9410.woff2 create mode 100644 typ/mdbook/assets/highlight-493f70e1.css create mode 100644 typ/mdbook/assets/highlight-abc7f01d.js create mode 100644 typ/mdbook/assets/mark-09e88c2c.min.js create mode 100644 typ/mdbook/assets/searcher-09f2665d.js create mode 100644 typ/mdbook/assets/searchindex-8ec871e3.js create mode 100644 typ/mdbook/assets/toc-7ac66f26.js create mode 100644 typ/mdbook/assets/tomorrow-night-4c0ae647.css create mode 100644 typ/mdbook/icons.typ create mode 100644 typ/mdbook/lib.typ create mode 100644 typ/mdbook/scripts.typ create mode 100644 typ/mdbook/typst.toml create mode 100644 typ/peripherals/a-first-attempt.typ create mode 100644 typ/peripherals/borrowck.typ create mode 100644 typ/peripherals/index.typ create mode 100644 typ/peripherals/singletons.typ create mode 100644 typ/portability/index.typ create mode 100644 typ/start/exceptions.typ create mode 100644 typ/start/hardware.typ create mode 100644 typ/start/index.typ create mode 100644 typ/start/interrupts.typ create mode 100644 typ/start/panicking.typ create mode 100644 typ/start/qemu.typ create mode 100644 typ/start/registers.typ create mode 100644 typ/start/semihosting.typ create mode 100644 typ/static-guarantees/design-contracts.typ create mode 100644 typ/static-guarantees/gpio-table.typ create mode 100644 typ/static-guarantees/index.typ create mode 100644 typ/static-guarantees/state-machines.typ create mode 100644 typ/static-guarantees/typestate-programming.typ create mode 100644 typ/static-guarantees/zero-cost-abstractions.typ create mode 100644 typ/unsorted/index.typ create mode 100644 typ/unsorted/math.typ create mode 100644 typ/unsorted/speed-vs-size.typ diff --git a/.github/workflows/ci-typ.yml b/.github/workflows/ci-typ.yml new file mode 100644 index 00000000..aa73791a --- /dev/null +++ b/.github/workflows/ci-typ.yml @@ -0,0 +1,66 @@ +name: Typst CI + +on: + push: # Run CI for all branches except GitHub merge queue tmp branches + branches-ignore: + - "gh-readonly-queue/**" + pull_request: # Run CI for PRs on any branch + merge_group: # Run CI for the GitHub merge queue + +jobs: + build: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + + - name: Install Python dependencies + run: | + pip3 install --user python-dateutil linkchecker + - name: Put pip binary directory into path + run: echo "~/.local/bin" >> $GITHUB_PATH + + - name: Cache Cargo installed binaries + uses: actions/cache@v4 + id: cache-cargo-typst + with: + path: ~/cargo-bin + key: cache-cargo + - name: Install Typst + if: steps.cache-cargo.outputs.cache-hit != 'true' + uses: actions-rs/install@v0.1 + with: + crate: typst-cli + version: latest + - name: Copy typst to cache directory + if: steps.cache-cargo.outputs.cache-hit != 'true' + run: | + mkdir -p ~/cargo-bin + cp ~/.cargo/bin/typst ~/cargo-bin + - name: Put new cargo binary directory into path + run: echo "~/cargo-bin" >> $GITHUB_PATH + + - name: Build original (English) book + run: | + typst compile typ/book.typ --input lang=en --input target=html book --features bundle,html -f bundle + typst compile typ/book.typ --input lang=en --input target=pdf book/book_en.pdf + + - name: Build translated book + run: | + typst compile typ/book.typ --input lang=zh --input target=html book --features bundle,html -f bundle + typst compile typ/book.typ --input lang=zh --input target=pdf book/book_zh.pdf + typst compile typ/book.typ --input lang=de --input target=html book --features bundle,html -f bundle + typst compile typ/book.typ --input lang=de --input target=pdf book/book_de.pdf + typst compile typ/book.typ --input lang=uk --input target=html book --features bundle,html -f bundle + typst compile typ/book.typ --input lang=uk --input target=pdf book/book_uk.pdf + + - name: Check links + run: linkchecker book + + - name: Deploy book + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: book + force_orphan: true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8356fe1c..d3b98047 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,7 @@ jobs: - name: Copy mdbook to cache directory if: steps.cache-cargo.outputs.cache-hit != 'true' run: | - mkdir ~/cargo-bin + mkdir -p ~/cargo-bin cp ~/.cargo/bin/mdbook ~/cargo-bin - name: Put new cargo binary directory into path run: echo "~/cargo-bin" >> $GITHUB_PATH diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..7f993f80 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "tinymist.exportTarget": "paged", + "tinymist.typstExtraArgs": [ + "--features=html,bundle", + "--input=lang=en", + "--input=target=pdf" + ] +} diff --git a/typ/appendix/glossary.typ b/typ/appendix/glossary.typ new file mode 100644 index 00000000..23af5d34 --- /dev/null +++ b/typ/appendix/glossary.typ @@ -0,0 +1,159 @@ +#import "../config.typ": * + +#h1(if lang == "en" [Appendix A: Glossary] + else if lang == "de" [Anhang A: Glossar] + else { todo }) +#set heading(offset: whole) + +#if lang == "en" [ + The embedded ecosystem is full of different protocols, hardware + components and vendor-specific things that use their own terms and + abbreviations. This Glossary attempts to list them with pointers for + understanding them better. +] else if lang == "de" [ + Das Embedded-Ökosystem ist geprägt von einer Vielzahl unterschiedlicher + Protokolle, Hardwarekomponenten und herstellerspezifischer Elemente, für + die jeweils eigene Begriffe und Abkürzungen verwendet werden. Dieses + Glossar soll diese auflisten und Hinweise zum besseren Verständnis + geben. +] else { todo } + +*BSP* + +#let url_bspvideo = "https://youtu.be/vLYit_HHPaY" +#if lang == "en" [ + A Board Support Crate provides a high level interface configured for a + specific board. It usually depends on a #link()[HAL] crate. There + is a more detailed description on the + #link()[memory-mapped registers page] or for a + broader overview see #link(url_bspvideo)[this video]. +] else if lang == "de" [ + Ein „Board Support Crate" stellt eine auf eine bestimmte Platine + zugeschnittene Schnittstelle auf hoher Ebene bereit. Es hängt in der + Regel von einem #link()[HAL]-Crate ab. Eine ausführlichere + Beschreibung finden Sie auf der + #link()[Seite zu den im Speicher abgebildeten Registern] + oder für einen umfassenderen Überblick sehen Sie sich + #link(url_bspvideo)[dieses Video] an. +] else { todo } + +*FPU* + +#if lang == "en" [ + Floating-point Unit. A 'math processor' running only operations on + floating-point numbers. +] else if lang == "de" [ + Gleitkommaeinheit. Ein „Mathematikprozessor", der ausschließlich + Operationen mit Gleitkommazahlen ausführt. +] else { todo } + +*HAL* + +#let ln_hal = link("https://crates.io/crates/embedded-hal")[`embedded-hal`] +#let url_hal_video = "https://youtu.be/vLYit_HHPaY" +#if lang == "en" [ + A Hardware Abstraction Layer crate provides a developer friendly + interface to a microcontroller's features and peripherals. It is usually + implemented on top of a #link()[Peripheral Access Crate (PAC)]. It + may also implement traits from the #ln_hal crate. + There is a more detailed description on the + #link()[memory-mapped registers page] or for a + broader overview see #link(url_hal_video)[this video]. +] else if lang == "de" [ + Ein „Hardware Abstraction Layer"-Crate bietet eine entwicklerfreundliche + Schnittstelle zu den Funktionen und Peripheriegeräten eines + Mikrocontrollers. Es wird in der Regel auf Basis eines + #link()[Peripheral Access Crate (PAC)] implementiert. + Möglicherweise implementiert es auch Traits aus dem #ln_hal;-Crate. + Eine ausführlichere Beschreibung finden Sie auf der + #link()[Seite zu den im Speicher abgebildeten Registern] + oder für einen umfassenderen Überblick in #link(url_hal_video)[diesem Video]. +] else { todo } + +*I2C* + +#let wiki_i2c = "https://en.wikipedia.org/wiki/I2c" +#if lang == "en" [ + Sometimes referred to as `I²C` or Inter-IC. It is a protocol meant for + hardware communication within a single integrated circuit. + See #link(wiki_i2c)[here] for more details +] else if lang == "de" [ + Wird manchmal auch als „I²C" oder „Inter-IC" bezeichnet. Es handelt sich + um ein Protokoll für die Hardware-Kommunikation innerhalb eines + einzelnen integrierten Schaltkreises. Weitere Informationen finden Sie + #link(wiki_i2c)[hier]. +] else { todo } + +*PAC* + +#let ln_svd2rust = link("https://github.com/rust-embedded/svd2rust/")[svd2rust] +#let url_pacvideo = "https://youtu.be/vLYit_HHPaY" +#if lang == "en" [ + A Peripheral Access Crate provides access to a microcontroller's + peripherals. It is one of the lower level crates and is usually + generated directly from the provided #link()[SVD], + often using #ln_svd2rust. + The #link()[Hardware Abstraction Layer] would usually + depend on this crate. There is a more detailed description on the + #link()[memory-mapped registers page] or for a + broader overview see #link(url_pacvideo)[this video]. +] else if lang == "de" [ + Ein „Peripheral Access Crate" ermöglicht den Zugriff auf die + Peripheriegeräte eines Mikrocontrollers. Es handelt sich um eines der + Crates auf niedrigerer Ebene, das in der Regel direkt aus der + bereitgestellten #link()[SVD] generiert wird, häufig unter + Verwendung von + #ln_svd2rust. Die + #link()[Hardware-Abstraktionsschicht] hängt in der Regel von diesem + Crate ab. Eine ausführlichere Beschreibung finden Sie auf der + #link()[Seite zu den im Speicher abgebildeten Registern] + oder für einen umfassenderen Überblick in + #link(url_pacvideo)[diesem Video]. +] else { todo } + +*SPI* + +#let wiki_spi = "https://en.wikipedia.org/wiki/Serial_peripheral_interface" +#if lang == "en" [ + Serial Peripheral Interface. + See #link(wiki_spi)[here] for more information. +] else if lang == "de" [ + Serial Peripheral Interface. + Weitere Informationen finden Sie #link(wiki_spi)[hier]. +] else { todo } + +*SVD* + +#let url_svd = "https://www.keil.com/pack/doc/CMSIS/SVD/html/index.html" +#if lang == "en" [ + System View Description is an XML file format used to describe the + programmers view of a microcontroller device. You can read more about it + on #link(url_svd)[the ARM CMSIS documentation site]. +] else if lang == "de" [ + „System View Description" ist ein XML-Dateiformat, das dazu dient, die + Programmierersicht eines Mikrocontrollers zu beschreiben. Weitere + Informationen hierzu finden Sie auf + #link(url_svd)[der ARM CMSIS-Dokumentationsseite]. +] else { todo } + +*UART* + +#let wiki_uart = "https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter" +#if lang == "en" [ + Universal asynchronous receiver-transmitter. + See #link(wiki_uart)[here] for more information. +] else if lang == "de" [ + Universeller asynchroner Empfänger-Sender. + Weitere Informationen finden Sie #link(wiki_uart)[hier]. +] else { todo } + +*USART* + +#let wiki_usart = "https://en.wikipedia.org/wiki/Universal_synchronous_and_asynchronous_receiver-transmitter" +#if lang == "en" [ + Universal synchronous and asynchronous receiver-transmitter. + See #link(wiki_usart)[here] for more information. +] else if lang == "de" [ + Universeller synchroner und asynchroner Empfänger-Sender. + Weitere Informationen finden Sie #link(wiki_usart)[hier]. +] else { todo } diff --git a/typ/assets/crates.png b/typ/assets/crates.png new file mode 100644 index 0000000000000000000000000000000000000000..d5898301de7bf69a9ad75a311f5a2cbb0c17bb95 GIT binary patch literal 11522 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⪻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 mNdVdq)DF>7@aw+- +
HAL Implementation Crate
nrf52-hal
[Not supported by viewer]
Device Crate
nrf52
[Not supported by viewer]
Device Hardware
<font style="font-size: 16px">Device Hardware</font>
Embedded HAL Traits
embedded-hal
[Not supported by viewer]
Accelerometer Driver
LSM303DLHC
[Not supported by viewer]
Gas Sensor Driver
SGP30
[Not supported by viewer]
Light Intensity Driver
TSL256X
[Not supported by viewer]
\ No newline at end of file diff --git a/typ/assets/f3.jpg b/typ/assets/f3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7a122aa04889095880427794827695aaa206d86f GIT binary patch literal 259494 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 z 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

}BX}UhntOdJ+ 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

L6JwO(RG z5~ag>O5AOJV}4aDzB2U3-un922=1{ zr2TC9R5`Z0jJXz>uJ?Wvmkzeq{3>%vjV?sR$}PrxYhE_xwK+cBK6^JCdntD7Z+X=e zA3Y|YO~q2=##OhBj5xEURW;Pz?iTx}8D`v-y0Y7e8{mV=pvQS~RO+q*BkU#RbUV|9 z5^grQsb@%ihnyI9jRFpkE_TO+FCZjUd+FaXg z?3j`i`oh5-4)u1Ob9!@*E?0K7>5}I`x!C#BS7;ZhvKva0-O3BEt5a${Qhcf5aFaFL zL^vhsN4zhY_*kr z)~37M^_dFYK0>D4dpmcOhT{1BnEQ*=qz-2%tw6Uyxyf78Plr;uC#^RE4`}2^yRwp* zCPT!wr#!JwFyzTi%#8m4bapsj*QS+wzDjEGa0y&ClIhZfpS4fhWnElTjiZen_Eq6^ zuQ5&E@Ap|u+gx!?#zSiHsQLVyL_HAv%V9bEJmk5aOQh z{0;&$#O01&bljWe?J-=C$XhX5VFcw0t$^!wB2oX`0ulCdBuj}Bpig~5Ng5`)AiRYq-1vno3o0f{Kc@}}fFl%`SQ_)jnL zuVhA6wtgk*s%sW=*Q(v@g99rCHW2|Y=rqIl|)3BO`=42{0oFP@;rWjrJ7-O#)-GXV74VXk~oS!S?Npc z-TB8YcPP%09$H6**7K3#>GG!RouAb+i7ZH$HA(QGXFDFIt}(Bytx#?)j_Eh*vaX=> z{AzNo(ww{9Cop3%9SO)8fWiW5h~wuk3+d z+Tj+KtEy)um2N{|{{RYZLVHY@31zgGpgNBRepK`Da$rS{V#AQO^y@uNJgVPmopAPD z;tYQ6F%KgFKyuvls1`dajJ9G)?^9;z@DCslTDsVKK&K#yukr1-RHGp|=khgog=x5Z za$VaDqDm5-IO$)zK;Qr)-y5EnBV&d2dB}K^%_Y@7AZ=AweQ-UYg3PVXK{hlIE`r=>r6X5f3Pjm2Unf~D%@HmfB0V^eV-Q`Z5} zv@iFH@6hj&Ua~s+WT7R*XJPzmZOS`(Q!ZnD$+9##-BlRt(ty``(De)dlu}o*<=nHWR8h;9H$fPDcw%y%J z$LlzHebsBWDQJy%w*aRQ=-1cmjp`km8EyG*+(hXLXkZ`@KPtP~ZsifU^7zPcc<~^Q zUs}2;axLobG2Dj|JPLGX_=|RC6#d&_+IKAQ;xZ->+krZx7vt~J+~o+*LY4GQixMhTYDNju~K`u zFU*f&l9!%G!m=AbuQhE`Uz|*|g|vBtN`DGSQgX^k*nh#Ir#D$}>fd@_2!%Mxvb-LY zoRi>Ao^_3A)Dhr9tAIHL>d2;+=E>GIYBaDp>Clil1ko$hm2(-U-a$%+oafe@Qi;jm zofT)smpohaqUb`nEr8ild1kyM6)k=&8tYJl+3w%Uh~4KOXB6GKA{>_nd^TG`nQQ=^ zKNsJcxy0j2ahUy=dDSx|SyEJg%B{DA$O&#UU`&jL@}(TC;x>TS4skW}A2T`G0G*~_=7w)oLr zb{4kC^)%I*=%$8BREd%P6}6sXp~8tN{^KQKj`!WTxL?c4qN&&7TrG24;jum=d_5}i zP^P4|^N!pyOtuE#C(u$aR{1fm5K_RADkpW}NNFdj^Q()~WGG2W5@0fuvCo|<;RalB zF}D<+;Vv$ndU@$pH3~$8+uAB|x7<+!N|DTOR|xSgk=k}H7a7U%GKUlM9I5ME*zLrV z6oBUsWGN@>Cao~w&&ikIks-+ZM4?~8n~N~Rafu+96XE<0dU_i6lH?br2g-YpNInp8 zns$3`M4xYvfvr(nEx5r~En=mr<0HGX3IZ1&Bz#BGq~0Ml3Q{9Jv^fm3q3jN$?Nz=c zg>a+Ho>M5EpFfpKiD@ttBk6USa@+8_KFBotE=N>JFyol+x1L8mD$U*9j;S*mPZ_6J z{MjQuBT%if;xgK`@mh8{_ilWue5s1z&1OQ7ggTI-;S^*`jUC9-M-7>9{-Uw)taLTp zrmYM`MZaHqbv_h-nX84yBv{iSM^f7=LUg^PeSCRT7^!V;rbZ7913H#ZUzI3Y(F*8T zYrHKbvX8iW)U|NjjI=x$TVGitsPw286(lBn)eScHd^k@+qw7rD&q0 zN1aTWD2Ut-5+8hLjK3t%ZfrC)-x8A^RCAQ5c^@NAK@E5i2B*mIwB1dbBPL`f4-8Fj z;zzCzN{b1GT$HD9q%z+%xP0lCxFX>3kKSK~-pXtB?rKWWttnK1!5>&7%9D3~C$p0F zp63QalpJ^-N1oZP=ImD?y#)&5Ms+-w=j%(9)F&guZ)a|@R-l!+Zns+=nzoxNdDILB)8woczUbqefvGl%q(Q zabbQf*K6V_Yi}I#E0R{ikP0^FY02-#e$xks$7#7QDLg=YlTI|xfUtDB(uN96ajlUg zA(E~elf$ZQAFG`u4xSQNcsf+t^>LN4#!u^~7`66Z>|S}rKJp=6UP?h%Gvq2EyV|Fo z^WhAl{OLa>W}Y!uEw)T{IB01hKn@yu)K=zOu8&BJ(wXmZ<(5j4RjY6*F1=~(5DWa; zA1IlMso;pWN`O|YFs%#j!;rCq#aY z#r^8yT_#;$alaF2j?xmJzbAT9g~@H|S4bT{y;|;9nafB>P9nidR7c)vn9%MIwG4O| z4@Am|^}($BU3?XMOKukb0Jej6%apVwUMX+PiWGkA8g+*6c|wTe4&th7xjgIbhK9Ae z-khth)v%{+v(+J$Dr#l6;|zuL$68x#_NwW^&Qr#=Hlm-A%}jD-;^8gXuvH1yoQ2}w zsos{%PSKw3q=aylXxm8T^{Iv2E(lr~mfXbmdrpOFNzdA;N^NTmIU!D^8S^x>sYN;0 z;s%nG>EH8vrz2L_ z%_3_vBB6vjRD#kGkG~Zz;z8iKc4!iMp%}@ee93LO7w&oFBfdwpp&B+04FW6rozQqu0pP##D=8?IcTr_zcUyC9_wi zCCFqsKMk=F8>vom!1NU+EzrXet`^|TV)BRQQaNMiPPny4ixIS|jlvm5k1C&YyT zim}IkN@Osn5Ol9N^feh!BSd=hXq@ULi60k;^B;9w;>u30@g)jt8QET6s;}0Yh>9J0 z*4xqh(y^-pkg2ZRn@mUoBaEP>c}-bWA)X{xnsEtpZ75QK!9OaJ-MTb(T0@$eeU;~0 zYHa5M9zw2Y*MSS}|U0Dg6IdTU+Y8tvCB94(0u5~Lt$z%obO6U$C2=l!lcY(PVV4)<8V*Hy+cll z(pFr=W;Z|V(@?wb^iK4i@%^0Cwxp&iqADQ_d_3{t(^AuNw7YVUg@FcHjm1Mr@=&O4 zIV#aE8XhHxQ5)bXA22AB11c*1rS%`cP-#mlZEx_Ck>X0c#Wd6?uFHSB_lMBRa&UvX zzX~?|a9k1yQkD+e^%UEyTT=V;f-_CW)vGESa}*G~ASI7tdr zXBg71e7WpuE7BuA*jws#u(rs`PbxB4kre=LrkzrPhw7&oU&PCWwczA!N)*aCYtq3E zI_8~#G+S*d)Y`O=N1Z<7u^&Q1c`I8a3V4{VxR*i{l7Z<-`W&P83gI~0tSNt+;*E_Y z{q&^Lep}J&x0N9B^P(w1T)bSXIMk!?fnG1wOpQBn6W7*?ki7~%EY-a0$TEKJRLph3 zi6tzksfQM&YDqsMLAM3Fy)7I>+t#=7=ZxnWrMBm1tyoI88FTp4uC;Hlu%MEsm|EY| z6HY-LpS2R4DNAj~uygm)+iKfpg!=FTT$1{ApG+ERO01T79q6BW)9vD#wq zw1;1K0r(nR$E`M5g_48el=)I~$nQyM?##d`$lts>x9g-h-MY;tK7}~a6LOM4)6R=4 z?A%R120PQg#*cM@YeZ%Kb%kBu3Qm0K+j;ENKX9!o(J|f4Er{?ULOIU>-5yC-uGEFU zVTmifBZ`NbxH@t^bQ(pHG$#nib=$M8{n->Ql5BBgxQ93eLhkd0B>d` z7iUqYJ$)(ZE4DQyu^CQi{!zN1QP!d$_El`~^4_%FobWH#=jUGL6j2@352Al6T^J{j zI2#{InQoWaC(z(uc74|GD_0}(qzf!Em~q08+Y0SMnpTcyY;vyac}VH+!*Mv_C$z{Le7>zeZo77R_KC$L@m;+kS=2o7 zy*(DoYraBptZ`a${L%sYkHt($zS}!W`d@KPwCZn6xH378TU0nU>wjpYN!EA{i=#tt zB5*dNKJ^E*jzNwBoZ;Og=^kDhV}%E_NpLcb8j~@dw%esQ-dOD+hFjOQ;yK9jIOj#9 z3$zU_nH&`cpKId+gSO+^qi!fEZ9VokE?Z>nQ<=F(?K^W4$>XJKV4Q7J6WMn=S)gfc z_Q8+g`DTQh_wtF!FDET7LYgEGN=qG@d$kCjgz0oc;OS8EIog|-e{Z#_j)y__tudn~ z(KO1Uz1n!wT3GJSu@SJKuS1$~>Cg7D#*#I-dxJWN`GZ~93-VHdrya!~$DJ~k=XRb< zRcCs_C@_N%#P3Se1K;uH9tsY}0J`$*h^ zjX+Da1bBTnUvZOZb7#|ygcy} zAvJW?0Q2;yQZwOg!siMW6i$(xWc8~|$ZBbPNzJTjj?z{EPu9`~H`=Y2C|32)n%rBd zZKiXQMxJ=wj-P#Yid@S=ph;cA5{xV;DJN37YTRb0I=Hi*dNtNE%t_11ZCph7P^0JP zOsY+Q+rk$uDdOuqQH5jU{naf}tS&Xhc(&nrz*_5t0q0gLV||~H+blG*TyS5T%yZau?dQr)?QH;qFjv~;H-%8=BLqM_E-%ZD7P zI0`@d(@lhHTW_3jcO)M%N`oMt^3lRV(n7IFjWMw6J>Ay|6s!V5eCRh1MyOA;_0NYS zsUe~cq=WMmrX)4Q?<#&=Ovxc3gn^Cxx2GaP3Jivj=8~-7k)4lGMwOiXZdIp2LJ_{a z&^g-~1X4A8AdS6q- z)RzT8pTKqbQMFH7NB}m`(q8= zV?zI8_9Zf~yO&Llq>X)UDZs3WCK!q)P5>cfjWn(w7@TbCM0 zPq=j{2u?mDo|MVssl{*xw$!8@AKluP%!22Eg6m0TU@1x+25K{1;pYk;2@Sfmc(&)^ z@~DhFy$!tbTX8`06)q5dQ!JS71fk~|D)|g=gH1;#DOm_%)qQW!SMj39w4`yJYC5E) z)ikv0U&D;`Nvnx!TPpfWv&L`*XEhdlyQ<^Lb?4e`2yvx05|P(FbrsaP3R)h9hZ47( z2vSq!gzZzLTpaGqLP8x!aeXH%)G^Pfs9nzE#oeZ!hWzkiK(T@-%M+V!%L~sl9D=f7^m+S zS*9v1A-DU)H6*yRowNLE&2WPwO|DN9gp`!Dc$x7N=TVUR(v*VskD^L#OQlEeFdero z2x0dmHHXL_2=x?&ARZ%lZ%>%=`Tp8V_E{lOA8At%)#$I70a zY?H1*9m?Tux-fgX{;Kx|~lxh1W5RV}y^*q1H7?qw~u2wQ{doz4c-(|VQ^ z>cSGD6U@}>GZ$8FSNO4=TV>XkojZ7v4vg(V5Sm3{>RAn-(l? zC^8@x0+jCE2GoHb!u%KbguL{Y@E7tF&Fey2?rY){pMhU7@}`@K1&HzDNkhu+=sSdr z57klo;||SYG$~BLj4+%&_f;RWS7)1aw5jVu&m`}<3Dbgpbq$%a9g%MD>@H(K4P^*W zR>#k3V&`t+LrPZXcWFx1p|x$-t|{4!$#GIsm87j&xA|3$4Yj#!yACrfOX45SJe(CA zd1ke&Ms;dmc?Iae=vn8d%8RZNryW^aiuI%AQ)5m-)~NPZ7_yP~u zTo_&YKMG4uF!peHkB6NuYdxlmKe)o~AuO=wLn_-FrkXF@8$)qnlnG8ZoFN@H+?q&_ zITLj!Dk)`X$M>mcuZ#9lmRkW`6iozx750_O6v*e7Q=i6^yr~OP$#JzD)wehv`BlJNNW7{D|7a3czfsI6SIg zS|+_YvK(XC{{Wl_>rHnyW;Ww(@Z|T;h^x+*-r0TD+lsevz&gPkh{b7$)!oM%k&gK} zS;y$qaNx^D^oIwgYIeBYp?wdUb-? z64@d$8j_~_?3co|9-}^W5;B&V8ITgV=@}~Y9(8dk?EU=_Bor$lKm%GTKXIqOho+`BxBqe!0U z?~SERr9837e%f2Y3_CD&ge@@4ekU=p$Q^3)8T)HDMxKTW3nVf!4QkJnJ*v2wLTlJteSa!thx%Y&wl zna4Vs{h`IGBcv}{B_lysT_JmlkZ)7a#A6t?V@oJgZ~XN6R8Qpf#n};tb;3fC!)X47 zzJ{SYDf1naYs-supfjgnNaQn8&>d&m4K&n@wQ(*2qH%=%)ZUe8A8d|W{?FM_;!f=| zXquRTNR-3-DP24@`NX*>WFH5C^#1@Fu|Q+AUd5H6OOnFf(AxY*DOn#1zIAy;t^V9lvD0u8I6f?FvG#=} z6Pin`2(F$1PIW6AXXa_oWVP8W!$ROR%Nhbxlh4yT*A)*eOHs<7La~oQ zLNv#bB`8)~b;idN``qiD5sCpGvi**5r*0Ecc0w zr3-T^@*J^B&Uu`E6|&ZmpQvlZd_1-^+BezM^rdlIQ%Yr8l0vzPw*LUVLmpmr-MieL z?~HIu%G3CMO}4C5hXfX+E%a$asw5Qu0F@#4B1ewmQ>98^d|x_pQz+Kr^tY^ld*BvQ|C`cuTgPrKR!YPLs0UR0$ewhLv~ zF}eBGw&MaIYrCzt)5gJXZ!Wvkh|sL8!g}QD;!AtJIK1`P9$%4&tO$(;QKMR8Cvi@^ zxv%jaUbCessF0XmrXwlBw%?1bF9rRub~I)`A=Xw>5R^w_N>T?P1x$-33l})E+YPV1 zAmMo21CB>MY9uD@BHs!gRMcjp1&p7WujtGF0KlX8(Iz>^tOS4cSBam(3V)4kk;BpD zkeu}%Y4qt|qp;`8$P?w{X!9iQr2Hp8xqr5XONR$yTVk=}KPFhNOaB1FF+(9k=F4f{ z=AKkj<^KR2zi6O0&U60&2mUib*Wt)%mmA_C#j>D+lh@_@D@o3CwmF0Re{FcQnHa`C zq=f$f^;7&K;C-UfQod&bhkYF zuPeg4boDX{e3UxWH|_A+TX4HRw4yYh9)*LjK78xFA$^{C8o~$`Z9Dzujm__1p`kEC zU-MpZw>^zmP?liFa#V%KnEZ5MDo+yuK!0Yh(v;|WMXE?c5s=Tu)~sZoKS~ZZf3}0n z8hqU$mmX5Vk28m?H}_O`J-Z(38&Tmf(>ygd^Aze(fcg^&mexFxpj62UxG`;;Dq5Ok zy4lpcbV(eI68>F0GIez(k*x~Sr6V~M2a6oqQcnK>Dq0=!Xxu2)F--pe=d1mdj+O8$ z@T}&Zk8zUpW}hvR>t$H*oRD<+(N(=_ae&f7m6hXHTx#DH3KhMTyDm($rauhe>*-D+ z_R_oLxZFon;)bqUllRk&Tiy4G)KH!(5|9Th=gynsMPrYSUFnm*<3*VWOL9^?Lm;2k zTwENKN$(vC)lj17{Jq&DDG(}gJYB+sR8Goa@5-zM@%UlDR&Dq zPBR$@I@H_vR^ux5xv9P^6KRgS~lq(+Kid@qP8-(1N|* zS30x=9|n~WKQ7;p=gP6Z2T#e4zm)C1Hlv@(`{_)uRF*y$9!E8LCW2DF57bJMomliw zne#P~E=U~#Vank5_p}|sR8{Tw^roVtillKkI4%VxXvf4p%`vCN^!Jsuc(bR^@>}|j z_Kyi3wI=4S*4?4VRM(p^HK&Gzg%F&5)o+U*wD%b=xb~NLr8)|K4t1XV?s@JLP7yg- zN)Pod{{RY)eRs8QvmxcFG8j}gbfX9RK2>UEyV|Y{IW@K}EXj=@hotA0+30FXX*>sb z>QWqR@;Rcz*=^+RbankS{_$QCG-D(U!1W$zK3yx~#-aYTApXCIRy7^@d**+IA0ET> z*r0WP?<3G!vaLT4!T$i|=klbTUl)=5H~ogU^7}uI-@2{nd6u4Q*=^;urzs;yr)bzv|a8b-!Ht1Mfjwyasu|R6+_3F@z zEy-(Dyg0WXdMHj*SEyib%AQ5WQ*vO=@JeyG=pT;ME;jslhF)nrmMTezSS?GrwG6Pf zGVc@T(xWM-V_XZ*Dk$#1QPIpR)WWq;>acsBmpfhvh@LJt5Z0@NjA@S-O~g zm%~ntvXe<%M?w%*w9Ss=tt-LiD+@?a^{8QOj_w`>#Ocjkd-rsEQz|HSIupxD(wt+@ z9I3QvR~ZiH3FoUj{n)6E$pEn2gsstx?NeS=%`?*EMR!KpjCADeGq*v#9D)z1p;#F; zB4C8M?nS#>+-@sG1-N*h2q(_2w+oGmMV04!FL29J^pbWQzEx$r$b$hZxI@gT4kQ&V zLwZ%sxsjzwW?7RJv}th~Ht?yl$bx@WE&dD`uE)6WIPEo{xRLdC+K{$T7Pe#|?K$Uz znoLWiuFlxo;_Fmm^3veb%O@3Mi)^|zJ*MM`t|6E8f_KM3=R(-)1;jj+cajtV!A70D zjX@FFeWMN_ilXzOTEF8pwx5Xf^sU$EaY00s{{X#AVCo!H@0xTFk~M9C(ye-Nq&9pb zru1?8{{SW{6ntGX%DUH;ZOb*E$=5Z@9V>svgE7>5wsbnBA{E8oo%?B5lU)n z3rUwb=UY?90~*!m=TMPvZO7xaG^x%CG7?7B;|08Zy{X{Ksc`5cz^v357gh@|>DHt^ zjW`?bb5^B7l!e1ZjKJi|By8avf!%%tcYezHs6(`~?C*F_XAAw|OPc#pS||*2(T}yU-`1x+78_BUTdF96pUt?} zp!1}lolz96C@SXKzR9h(>t8AXY5=I=H6zs(Xt>!fA&a-MQ0R$noS>)NDIXS-P@nB1 zmxmvBh#YI$j%f-9!lDnQTvKzK$Bx@5ODU5efRd%-Y-7%?ti1&?mi_a(=vw!2m1i60 z#g9FzTtG1h$yy^JNAqW(`BbUi+O}BWy7E&nPG^XY@r{Z+jw!eQ0F?2rNi$xU+lg-p zb!z9Qu6}hP#y}!9t}2u>^>ik?-Lh{0g_P*inr`KBR<)`~c0#oN>Tg8n47OY=N$#3; z=aNlq4PWObZNd!OLE^@Jr&|qUs5-jTxoPcGyPtN$W&#~X4o(j$b>`Oc(y|vVMF3hs*XG< z!-?5ExzI+nwl`i9dDIPu=K0ab{_Z;^!HY=Pt}@68{`PmrjCnoO7Q4H}5jM-}$n;;jJe6Xs7!hWb?J zcT2)Nek!t5oaZFipXu3nx4`@PI>7)YUa0CEO*%a zBEI&?@)_HF?b5Xg%5xofXf-*vl>Db=6eXm#*69OQP6s}9XF{Z0GjEis5?^8?iOYES zPI+(TQl%{{SOc&aL@@{{SC&sA(Vl+JKM; z=u)otrhH-LE*vEdhW`K&AG)F1l)TKCaN9^3)ARDC<|_f{l0Ztx=zQwi%<8hnAxU0$ z)6>`X(2FHr`yTDijt}EbC9Q6}-$W$iK3SyPFCCv!;mU>|i`{N1O1Wo!k4h*`D<3cuL1;O1ArjBK-!_h(_M2rq?Cl z?}$00Whhf*f63o?g0+XxxVIKrSK^9dE38e z9*UcH$SBaIV?3%J-`2=A-J@Yrl$@zY;cR)DMb=C!o#FYi z)`wAVcTP0@uR~B;VjPGJHtKZ7l5(N7Ejaq$mOx6&Yti9YJgKSkE{b!2?|57!4twpr zNYbq#6Ljt6O-s5$OxVq>NmO}0FtB>lD~QZkn#!`?ybqy2N&ZKu4u2{^x6CTW$@h!pZNApb$C?^Q+r4?-%H-M3LRvQ*v{}Nys~C-kt7u zNpCPtTT8_{>GmB`l&I<)DFc`^rerz7azBk)Q_LT&{6#EkPOcztb>qd;f7ofOHPuED zY6B}~O8iJyfM=H?DPq*}ZZg!Lcf?_)moeo$`_hTlT*pKDy(!N=_THZsD%uv} zO1+5;E5VP6J7?4OQOb_ka49^-`f@#UN!FD->}S?CUIMe{=TPMKVmjx`k7`RYW3@UO9y}Du6M~M6k+OeF zCZKyd1ZZtZZP_jc$ydDP%-0m21Q>;-I{uz#9|-lQj7C0>5nLkTk-N@%A0b4x+el9= zA+fb(MJgUz1#(Bl)v;bCSU)yXM^re6(6F8B{TT;VIrn7Oj@cX^w9wO9*;!O)PCz`< zStkGu&1Gb_4I8WU{{YIf5*ur1cu-ZfMY_w9h)z2rL+DdUC`xtd=jYay8Z0jJ8$fZH z4(J-kn8tRflAN@MTaP6U@Y1~UIj#gaFs%!H{wBBPwucUnLUt7b-tv_wm`z1I2?%v7 z;r+cQ)|+iVnRuxsrF}&T=R^Bg$K6T6KOtG_JgPrWc#?MYqNVMrdIdq!tzSp|^r{qS zenk4wp-SgklyID6lUf@jK?=r^vYtQvGy@E+{ep1P4nAIp_3KJ?5!Sp4DS3QFkA)=juIi}-EqQTiQSrU%PfwLYml>($ab0k{ z$`ZePzKPVHX-!2Tx0%*bryGA($4>nEQCQsz@dNo}0!CDgBCW;O6qcTGOgXfI+3isgC6ZckrWBta&phjoGHtKBx>TTv^H9@(=hV`LrRy`{pAy)Q zb7-qiQ&Th)uS`IbkL^>uhtp$4ELZz$9aqGHL0L_QHFuL1VnQ(c4fRDh;F3jUN3VBqQ(c*1RveFmktn{k~ z1xza2>`$ddY2N_IQOkqYyDD5Rq_qc#+M~KsLba;i+ilq9IV?QXr(7sYfDeaIt4*a>X76(%scT%;mR2;LnvXg+t9^wx8(dkg zr0Sc&Sy!iARnpgN?8?yO1n?!KUYx=XbPjy?tCSnUf}VpPNE}T-0yABnh}wKoV3}^{ z8gsK-;BAHI&+O$>GET&pKz&{jDfv__tren!GlID@4CsX$i0FpsLI zaG^#yIwP=JaVP3RmCfRXj zFk~{@^Agy{mdNQ=d!@y8c^2!kk?tfk=Ml6M>5c2VE(eVx#8UATl43fYL&AI2TrT#d z`#Mw<_on4OvZQn0KRQ&27dQbiL&<^ge%jH)lq}%Pc z7}`Ti+1 zjGq%R)#dh={3vAjRpZp5{{Y4NlSx;zW+J1j#S`_Azpp8_f@SwwM(T`eZ z2lGcL6tb<=#035nr8bRvkHC5wWtf)NU69s>`{Ek_s3)aIk1D{X9d_P`ig|Ftiqr-V zkC>q*Yit)04QAg3YQi=21e#O#D?~l#Aqi>{#<9PPJ}y<+DE8QGNm}MMw_xcSO8l|C zDK}`b<2lFKvxMMO8I~Z+l3biwd^e?eZyjpwAxyU)Ty$j(HV`?AaGYS=7*PBw-QC=3 zU7q6&4=~Hk?Wcx56%SfzGfM3eU2k5QJGajt80GiWd!td|zWYSE9%Dfy?mWk(MILu% zzhS09IK`saKlTM~u1%-^0Gm#ohL@B^dhbyfxywa`h*5i^KF^G|0)QClze;pJ+Q%n0 z`&=#?*7+b~?&nBK18m!2EVUfP3Q2d+1HB=r>Co~Mg(!RsC-?AdhBFq(Tta6!%7=$FCSde@`LtIDzz#*MQM!c+f4?ETgXyT%nRjS*`)%RZ7sibRDaJY-s!RweyuI0o@}5+OEleRx4lHsd zUskUvi3a-;WaWG5k3eMwE^`2=EA3U>-;$(UJBvZ`wt@*7b|=>zYRNT_hosrtNO_^m zr9Tk<;mtJ59`y~Yy~Ta8;%_{`^rDB}#`vN1lymc>fhC;(0B*D1nf~={A+PV&&o%4f zqN4gX@>MMj1Hz7z;`!7vM#_9t=DB7#8LM^P3?}Xp;ymDwv3y`OZ$ZPsIpn&Yd-=5xyx zEe{nptl%jlepL-Ckg%L+K_i`Zkuh6w?Ax&0Ort70jzWNWqQh<6l!L{9mrC=G59peE z4e)a*SZw`YO4pR|)3Wk;8py!wnrAvszD_bdYt=(F8^xcZrj<;YVU-tHOHwg~AKOjbCCXHpD^ss(DFZ7hJD)Q_ z==TE8y!UYvf)(gRF)b06+U77MsQ~;7BlcE_t-JvnI^RP=5>^VQItVNCG~5>gWImVE z;X$XhCrY@SzUmm)&Y&T*NAIl`miDrNvCet(`|9Xh?oK-e88s>fbZ6z2k1>rijrwVX&PiOX1R$kITRniMmUrY%ts3rKL z^iNUgTu#Z}*_-qwCA(bwH79fHv8WcwLVPwKkXn8_!9fZEqxW+Z%CqoY8c_Xoo?Z)G zw5!l)6^wZF=)wCzda?J4RFt1|w=%4a(tM3gZ5IfZsjjIeJjsdh=>+N=j{K^{CgXRr zT{Q8P#n6=d#S8|()3EEGI=Z=UaS|EhsckzQttf3LqDP%H5XoSuYFZWXXV$!2faPEE zUYP_Xl{TI#)OJd8tk-z7N|i0SE$7{(93&so)l=WQ;#=HIE;4X%ocKtmmeMpK%L!OX z=d`RdYWvg-aVSkFJgyKGqr{*K~-AKOhZ!&2ln(k3BI zwuguS=8-G3zTbp}C_Ky7#`1!kb0k%vi&E#x5X+AyeZ=_izGLNAdkRwxn9^TVg->!a zopUOqm##mQx+Z)vZ1(a>qJd&i8jrd!^<&FvOItWH;~y6@Kc!M`Gm5 z>9wUPnFzL|@mmQ4P*)_K_NPUzQWeMdh7+!YJd^2~_rB0@ka@>Suzq#US`REL-A-`a zS`OMm;3|`ejSO=ug1QMr$Xu{;%7CNvyJ1faJc$&pBeC5`0OBU2Ts=HE>0VZT(W`j> z0KEZIf-|RrjB2Z8;oEq-LW?xXR78v`@vBOb&7RL0mwVb;7gPs`S1_+ym)Y5Nwq`Ws zR#KPa$x5&h^|dGE%DXpfW!Jr-Z%b;D%SbBFJo8Qku3n(85`x=g=mDPgGNj+{7h0nT~UW`YNMM3fB*P{yiQZPz5XA>_Fu zizi}r%K>Q8VWx= z_HrBkE2OrvX%kEI-X{LqI~&O19h^uDa%}L?98tE zv>b2HeCk(n?BuZ%fTU(s-NokvAv^NTIcK}}Mp6uKbo4de7Myh)g$r@)vVDu}({An_ z(pG;t#LldF)HqPP6*1>n0jTY@cUH(Bbw#y3+1tU4@EzIVdRGG{au2Ah=dhv2KzV@F z**?=Qd`cNOBhIbQ88KxyHPpD$<0$e66x`cWi+T!VrW=e&0mjs&a!!?g>W}=Y?9yLupm;qBQn9+2JY%?hmvh>V>r$-Jt>IpxnrJ% z#V?@@!^vO`!+AX_N*;@C{&snl%UFwoPMm{Tn z;Jkt5ONh+E^R29>4LVM0PrsVbZc9iYt$k`LB}|gi7KN!nm=Ph4&FzWSTM zbG$}Vr$W{Bg++}4(*@a%21R@13e$t;Dm1H<>5?Q!lpB!|_jM!<@wc66LPAH76rB0~>T$Ir8~8G=bd)a|lhZk< z%GRWWzLFAyja%}hsIN*w$Uan~*1iX*s`9VK^%6yB#odIS$z; z&I(pAbMmOHhO*<%1jUSbCp}f`QtnSU)Y++Tye=A&+lf$0xuIQE>N!$fwnS7+H;J-N zJ}$hr)$3d)k%WpAsucRXt@2O)?Nw^HMw2DgTv|lw2ywxyDjhS@r8gnsw4!5(2t9w! zq9rN+0Hro6dXwQI+p!-yw=Umsn;|n}B4fb1key0E&N);D9gy?RYnIfYwPiaYSj9+F zvlkUn+|z_1Jg|2;Bjr+=ZdiE@q-b>G;>Rth%i}Fl2~L8A9P7^DkCkFRhCx_Dd%_Qh z{_14`z=;H4w0!C2l7uN^Bm$na+HLmqQq#e#T{3;3WY?Qq&Zh{hdB|DlQNoWyb zT+Ec8hh9HjPH;&^w5Aif5|w;Q$0{tP#@GJqcxBFPCS^vmtV=D^G{*YRRUM$~cZKT0==2=gOhYxf`8{**I{%)v&mr zJoX@uzN1NxbiG`p#lt{*5qu&-df`|Z6?B3uLL%j|8(VJiP(aRezpX@4(uUB=nU2e7 ziC3miwce zZ4BpgiweEaNouxza3i1Yt@}fsCpX6#oDkycA1BhCB5kb_) zwjEZOM@B0PgCha(`|&}T~WmM9YDf)eQ&yu^~34XM*2_Y$KBR~8bTXD(Kkf`lUwY_db z=;FrKGpRqNO-Nzu ze0i%}fcF*U5E2Kk(Xik*^|m7r(pl=&Z8d5~04mI9tim#8JrLr9ekX=X^-T8D*W*1K_PeTb`*z(f8Q zG^+p;wt0%ZHi+s4t3{(umlMpWVE(F@rA1nm{JM?7`+Y4}p+#;*svEMnhgcWpZoK>c*IrZcH=zTdPF@bgo7BCL&fcd)rvwiC;6 z0R&dA=CtbU8l)>o9UbRakD|IX8!Ut28x>%Fx(Q52hK}EswBU17c_O1kvVE)QDBL{kvUUN;Pnnc^Y~b@~6JflX7kR%8KS0X(yQdxi#B)E?%2u zbNEIqrLRg{CnHD)*0h%0QbGb!pvX`^g&;;!nue$-Y^RBYm37#9Rj4wJ4SP|QB??c8 z7+;8$br`42Y3$tD)Rwr64QWo?s&kg6+-^^LZN#m%$~Nozsh5^Pxc6^*@^{3J~Ha-f~{={_7Lt~?c?aoS6&Vd4E^ zHvrQdKZvy2H2LjMC7AD})+U(E@X~g0bRM+BrBnDa+I7V>#ZleAh>CH12fMsj=3q;y z#&-iz^BdQ^WyMWigst5>HLv0O$gMrmK`g$gdea+P+;^Fxl_Z1f5xT%hK4-b3YU3_C zB(k;?#E9yQl!dl^MKGe&bu$fX45NeMLRE|>y+oi1Io+%OIwx?cj;SHc}X$CwuoensrQK3IsJnBQ9&XJ}fiXXyxb^Vm5 zqD67`+u6dD>?u#b0!tD3IQrAJ^%)6_@|7(dNZ}~>$C#uixyj3|7um~4#F5O`73h*# zWiBD79aBdDl;lusoD~lZV3FWYj+w?51F7-0yo_du;jO~qz)Dlfr46=9M#IvN&V@E6 z7ZV#-yx|S$mo1Uv$BhxAnF@6SBg<}QHF%SGinYEYeGX%bATjBu*UqI#n{h6>>eP#S z(BBw{MRhrvtWIZiV-FPKVB_ji#=9`~z zi3RBVCRi(6l;kVWb*NY$^j7c(%D4OLprt!-kjp63l%Z-FU!WaoZNbYkHj89@y=;!} zgD;*=tS6ty(6TuNGj0f19rk0LI6(fhBkI?AnuVD0)0rLL8q`Nb`O{8Ae~l7A)XqL(rr-$0U`8fQ1L>Gf&Wh* zOJ$>^IQV&-or1o}{l>fKI#Z~kOKaxZ$j1JZ(lLi1$WeW+pd4@VsaFdXrMdR!q+z)R zQW2?G+zit*Ze5pf)`2nhIxz&wyw>4mr}nnNq}T zyTz){A><_~Y3>3-Q?JsH{np1i6m4O(KK3vw?xdU_b}~GTH!4G-8WKTaVHr}D@~;7OQ3qDtua`?LA9iV?TE7)7+55O z^Ikeq)YoIjX@`7Q(y)93(yXZ#^w~a=@)u^?T2?-{-YR<*#K*=^?4CP>Wjm)&-$9ITHlQ;;sKmgFE7+-)P7^9RbKTcE*_CSgN>AobE^btF36nA9De&1xg{$2ycspwyPG#CaUlP{{VWO)%TV=T3xh^pDc(_U3XHPtDitO|oghVkN>yssb-n}k(#&Q)% z18)yc$R%8!N=t`Cc0RJz{3*$uuz4@WOFFG?DSqNIy+@r)vUW%&;{D4mH5y8_A*}xZ zS)a2>l(`Pic4MJ^qMiL}{$5BO1zQcZp((JnNN+MqT|)l=OCPeFeLy8nsJK#rflB$( zepT02X*8@+Y0;?)8`rb004=5e07Q_#*nMd*rksrZ7FU?^KD6Vi`hz&7Jrxb>8c_Q! z_=xH=n&Y8~8nd0o#+%|aSitv@W8kZN`(U&W9LMcwGLd^QR@tJ*IhFP@eHQATW38I#lOUGSjcJ z(iPEPDw6br`Iq~wQH_)G6q$Ehi#D{%UlEkgjPTq0ys2)t+Dk*hv})4YUHm>8V&4s3 z@JvN&v>?L)M*-ag#0XJ+W6-B&Sw5#Th#D_lkVQ$G_5- zH~S|w5w+V=$Yx5OK#d{5rLA8swRpRx-zzV0+HZ!^hLMk#r6yC+pNjJ?CDuf!NZDqBQ9;(2|~y4b$=z%ySkxgLao~MCs-hbF1bmQahw< z$%h&)ppHe8onL)8=!F;+-J80E>Eu!{Q}Q&nB{01YJmi?@h@Dsjl0g;iLduy+hY7SR zo&`5{ahE&PUehv`h^8!+_xVrjVd7$!7_m{QwM6dqqHhXw>S&X+a*6HK9CWc~D8k=c8a6`UMt zKeD5`EziDQPNlZm4pH-{ugZ>M_m2_~v%UokF935+D7%75Mv^nNFsyE;6Q{$=dLXE$ zbApps5A9;U`uSHnNE?-G0H%;Y?@x|2mHPR8)bv?RCDvX>m8Df~YSZCnrJ;J@l_c~RSCFBmNPoJG_xzkqbQrT0|BS3n1 zz!*m_7W*E&>e+OC$K-IQGTAS-l9tjHz5+lQ#W$ytkQAM%*^{0PG|=09)O>||PID-g zAq_Z-Y#9;#+7Z_T{&9b@qrkSOEz5X?TjPVTU)oXoHQydmrQKG*j%2!X!)$UweJR!* zPNv;Pg#%Jzt2%Y+X-BF$R<4|@W1Z+@%CoCE^r7;j^Q)A=?BK<*EspJLU^x4VUVQl^ zVx5NPdT}i)L2_(v67%w*ZMb-wR?(%Nzdu@4y5OxyUxGZN^@ox5rAJJkg_S#(NJ3Ho z=At>`qcXP`xJH>DR9RNXR8*${@pI|(rlv@_vu$UI-6ILDsZMcxyd&W`9O_I72?S8d_j8bypF@L${OW1$-Gz?n;OIlw z$EC>s0CfK9k)-o{@~?C_iX8%TS#hPM1pH(g@GLLG%vZeJ5O$zYlmK?XH5mpAQqb#* z@1Ql8G1Cg^P4E*jEo#e+NdOb_#UZ$sYlJfBz?6(@SKeuB0zeA96#*#w&xfrK)@fxb zJjfeV6(%qxQ@%E?x>k4lA6Ar?t%XNfFBf}A3z+prY9brEc3n0$+1G7{!eFmRNXk`_ zva7SP?h>tK$sMQf40klMsh!oOC+&>>s<}psHHVyrr8459;{+$lq~0#+hoyJN8fhzO zD{;&L%QVEc^`tuD)Y}O_2upennwa5wavSez{-9~+`zuSG#48>a)JanR018tNv{`+u zaT_%cQ7H}fm<0Uvij{G&yA9N({V&^PwXa?Va-Y%_G9#BQ5AdFRw)0q72|J-aTT&8~ zg}UmFH66Cx=B&l0rVSDU>62=2Tbg{*4_avgUu~PrrRn}{ZPSnA`zZ;KoA6e){CDEs zzlZc`VChliS~!Wv4_7MR>p8NB=qZ&pl?Rkgyz-&sO=X_?4uyX~!B^|)PNfpA-yY&M zoi3l(O~iFGZdRc@UG9ysZ6WN0sW>?JMol|1-D{SgZAW(6fb&Xlk*_#Mt^liriv8KS z6EE{;>+bwCBmk6oui-ha(%%bwJ8Z0w_<7nfpjT_abPK#o*YDK{d8j#VDlafr|}fj{X7$Gkd_-K;dQq^ zcr~R9LRI$4b3vzG;;Zd}ifsti=}U>u6`r-?-C=ua9J5y}^9#YndFGFoS~S~^4NO8$ zcvwDtD{jA&L+DG*o^80Cif(0=rkUShN80FW2WKp{*4Sd-BU7`RlA_X3oryT< zNDQ*mmja)p&r@SsiT#4TKO;934;*Fi-9Msf?g|Rh%UoA+&{IUkR+$8F)u}k&ai1@~ zml{h-ZS;Iino!Rz_pIZ5ZfF+3E;#6E8TF}-UOUT@lkX!gJ7pP5dDMjrj|tm);aRP! zQk;t0uR}tzw(>Vo$@p_!&gB`%%2IhaG5ArnT#ped+9ba}Pn|z+xGB)zvt3JbP=b6v zrHX+XIwd8fA!|vL(N4tl%@E^L)aABGk)|FzdVt3>zA?(KPd1qeYrU^hC5*hrPhT_| zI66`j(Hg=>%j+ZdRB&$AhL0b{hx;O3+w_h%6*(;4Y=jlGI5vL8uR;F+)+l&QN!7S6 zE0s!260Rl?c&H&b8S|9-3`2E$rhO45Qk^5slBpoW}I%g%S8(?c#V;29L-&P2*W za&~4(SAj|K3W7CRmgjx(H0x>(bNcG?um1pMUdx(HMjCZakCOV; zrtKfQw;tx_1=GVin&*$7E|qeO+~y6!(%6?CHitr-7zZGZM}Ab5U;97V`K+xW#Hs0P z;*hPvNX16DE3>_sw8N6$dD4XycvhX12H0I%WTdFP@)fA5S^BD{m4dL`&MMM6q$_%Z zd9~l7LysL{PG!|<8|NSoPL(!8AJNm`)Sg9E2mb)(q5lBGQ5fv>2`lb*j{doKcA4Gvk1c;v znnMojAt8;dwSD2iquspZR$cVMq&7Rs*a^;#WYw;3C zT{*>D+L0pcp&ZLVamI$Qmy@Iv@9_?QeO!g+Jh@kgq%#r=Dub=(5Zgc%ZOb^`yGJHv zoqx&oWLubW5{Vn-INO#pnp3uAv}PVM-Ala|w|0hlw6orqc9&BI5$fX9VDNsMDjeTa^*sEN3*FrBqg@ zq^o@LG+&hKji7T|rvifyTq3xHok0q4SB3b#dpaxV>EdsvQU+;aFA2R;6QrqIo>bt- zQ>6H6=ubW=%|%p|Hij~-OLyVqX)HM|+UsEt2iZUhQkGAH2NahTCej@4?1!+FY0@*S z4_ciyuYzQ=!~Vek0GOb9j8}KP%}T8C@=+m$ks;uDAzcPM>5k60-wG-k(QB3w;86Pt zKX3NZs2@2lpZ%jv#bG__<0oj)!nZ!B$s(>OwaY~BY#mc=kGaEXw?5Nka`p43;}o!| z?;b4qEh8G&`~_szYa*@@)B)ERpoNQ{=;TJ-{f%ek0sO zoOuya{g~TQTY{3x=SvCUDF>ppR}ZxDc;vpm>mj8iB|z`66ql^_D~nJiq)BbWxYvkz zjMUd;vbcA6uHodopmmecR03Lj4tyq$ykz=$pwvw#w&RB>PBdUJ+SB(Bt@5e*obB|6 z_>Cut?C9}W<@J-!q=IqpPsLN)k`h>6jcPCG-=_FADGsd(Y&{AW;WhrrI zRF`BVFMmLJR=Hd-ods_cd$ED?6!!)~OY4o{ZBo>72=deAO|DdF3XsD1sasEg56?9c zJT}~K-Y9$L0r4!-qg^D)enU(t^3yF8qhXwCJn5xJZ-C0~s}08rCv75t&izFb=~nP% zIEMS?>2}l<*x&7aYD<0f_ZsgW1g69qWbgG?&YQI`n6ePVX-Gttvf2^}I*+dPG$Ffl zO-xEqnFcWn@Qe*R>;(mO+VysJN)@FnTcflY3C0KCP_3|T^PG&sDT-rDii99Gk%5gp zYRK6seHnXXJ!NDkgxkad)=DA%FFPcD@%TjVzTj}@IK>y>iFThNt2(IQ3^ zo2rRpA8lCNQ)*xG1^)nktS!ghWDUBHDo1vAv#uwI-rb&zv?O?X>gPjalUOUr(}74* zj>j|`%8Ava6!fB$sOc)ohG`niEjkbM{OJ8%Y0%>~C3wH-w>J>9qA;x3nXQJY9t z5g5++Cp%YW7Y1i$n|>tcTUulk=t>H?sCntmD)%Dmd1e*ei>i|tvpH!cIL=TL%vWez zoVkL@6*z?YZcih^Iel&NDc@r}hZ}K_OknAAZ$%!ohtcpW&YL=ssW-FNe}GbZIOA@n z;PRbYI8T8|PW=8<@>@7OW$r$8YI(I4JfW@NO?hXfE84%&g2CTB>7eRcN;PBK0|CNz~JWp~26cF4=WaQ)wSTx-CNvkFu{NkJnM-LO`8x zks-vh8I=vZN5rooN|zyKO!vlLlCik`6!|1quR~H{8$IemA<6EtCB%qaLRI%rVexcB z`S7&mvB0eSsVWwG`>o?ES+=dPkDjVwv2MCPvl=>1VA9#eN(1%jvMI?R3n~`Q=`4-i(m8xc~elNS1-$xq^ z{c4!v;<)oJ!FDeZ3mM;R$U_N|<+s^uNnt;l#-GOnom*qB zG-rjp79+N(`PiKA_O~?qT9km-gF)K>)tVV&h+(PEh>Uo*rl7Xpc7AQLH2EoxMzsQ- ze~(&6XQUv^X{5UU0Mfu#0B=8zX^C(ZC6zI03P3BuK>1YrrptBv-V5sZvBMjFHLc~h zs5hjjJBo^q;U;PmG0qy%+Jweg$Jj~aYGUMBZcV8!wm7$r+iFkz(#&SD@Oy{pdb2d4+@1%9{Xd&eVRf$Wu={4eI~?SC-rStw9I$iH=}~M-sE_WHW;fT3Wal;ZmWJf z?d#G3Xu36V(>pr=4r;=Y3xm)qJ!&ak#GC1T8V{NJBbTk29m| z4C0$ycNFYnhccytv7bCs5H2BQv^qGcZ$2cG(-j-LTdz$z)2dXZsJp{Z=tiouH(~6= z&M}Po9lAOdPw=wONYSU!kTHBA=SAd8p2vmcCG^!S@XibtKbC+ zM2CAA@NLCuN{n{vNbG~TANOR@U$fP2-)2+*q$|bw#0`xrsZl)Y>b{~%kL#w4_<|hw zTJGM|Vc$Koj)tPSGIFKGlIlI=mpn;Mq0H}AM8vx$N?=M$lGN5*Z$MAl^T``g_GIQ( z^cl@LAiAFJNZ;*aKXo90(=k zWqzQ^z5L}wj{mGMaWqdchp0ObbQIiiSMr7BypN?)M|A@8-$I;PKh!j&fO(RQsTM9+!LJ`R&qL2$VyYD+G4g!1h7cL zMnzgxaa4$^L?<1Nsgn|+qfJ~U%$UP)Twq5-h-W!)5>CnewFk5@C9vhC$PPj;D5b2Z zubzXwTwf`1@$Dj~L(Xi!ksUEkAr|)SE~!7#7Nt7Z``v0IuSbZ8l60yg)`k&>8&~y* zJiw#|{gnQNmfKU#x40*9x#Vi=#)k?thEO#{v#C5J^7shN9yb+-5(90eeNU|^-Y{ix z)D#x2#%WG{4d*rGB3efX{xsovEVYFkHys*M52Y&k6^R!VTP)FSFL!0>(6F45zpXoZ zdP{cnhMz;tKB88i1{3K~VkRr^i57;_-#DP4=cw}(yIN!^rzBrVZLkT-17ojDnk6ZW z)}1=L<3~!f~%4>TvN>ArI?6+fevwU`PJ|qt;fZkWkwXJtj{ts;YO+pUJ! zF}TjU%Go?L_)+PKw!yhD;bN#BnZ4@~BNmr3rq>+Q3RchNA?Mc{t zg*k1xS}!+7Q$M{j<*}@W)sH%ZXBJ-4MPe9n_{}N55j@m)rRxd}qbowbu&6G_H)SyM zz5xN;zo*kXXXi~ehWl>7?+f8@Brx$&>yKJT9j?$dsZF2f>5aOsT{QBepE^i!Kqa%B zR&}FNM%=4qBrli6>rW@a_L98QWgYCOrCt~$YyEWh30Ip(jI7Oqj`K^*Eko!_(aAhR)O zXBgC#`5f~V+36QoEOzLtVZU$-z&pH)p%M z#CbzZ3FSBs0nq-R?XC9t(YPB-GciimsRfaeXsg-KNj-b2DGtrELv?5vNe$`$0O99I zTegO^ww(=>xUqrw(a~qN=wY3;L_s?bTDvgAR+gbQt{U4AP=ZG1nnv8J3K8963!0Q# zLyIGqpU#|V9yPS!)`b0FDmzysyFCy=W-^{v1514qw>){!U$PYFN`_i$anyYV-0I^Q zxNY^UfteAsTbP?W9^|%raSJfgOij zY-J6kU?gLuEi0Kh^%b5Wj?vO`85(hy7t28h33&h;}s~qAPM1j_d>G-D(;+?A4V0X9p)tk4 zTPk3dd&9}bqBh7h@KA}NEow?QxMaD3*UL2U?-0@iO+ZJQwH_~)Gs?1^OG0{)u%=l9 zTHaQKo?FwYl*@}@DN_nK+bR9^;T{xjqgiN$j$|b7O7sBNNhIqic!_etv<<<4=h~SwjX)*+!_1@ zLuZWzD|U2?YF*+V>V`=)%fa|z4_jWj4|(+)IQ&gMwMd40ya@A^rQVL50rRKZGGU(Y za%S6*?=`qHpOB}baR@NvDTO-0Jt!benv9=#Q280Fn{XyO7r=*IaY@#tc(O8m2&)Ws zB-^BS`R}?La@dUwDH|zDC*s)W@T>HQvisav7d{NA#!Io$Az?aFQW4~uyIGmA+gZLw zWzPPya+Q;gelE0@r_S$^IDZ69PH9iBssZ<8wv&xcaBxyORB)dTHPy`j z0Nz#mXdLtDQCKkeoy`ah>Q)9o!6)}rrn{2$yrq`2q<~I0&pgzKWccDgs1MOs`Zs5W?ME?Z1?i*G)(@>99#^576uRhj(7gDzBOfj55~MX6M{#tz#-wA@r}2LQg6cKi+V}NJ^A_af*IGM5hX$#bK3DNAsgfbTq8w0CBxeVr`-yXk1H;$cNsV z9_1@YQ3Eun*86-rFt$T^*O-jjcT#b&$Ihg(<&|#sblXH!qAX-(F6akdzF-b{q^VCx zX`KsOZA94^PfcECwfB3d8a3)OMrtIsBX;HX!CF$}8AvD|dQ&%^VlBO)5DkN*Ix;G~6ry-mi}f{@aZ^9yNM*PMf>R1}^Yycd!KOterF z>4Q?;Wiez&a(ll+eIC6HHNm#MZhh>DC?mp0r64l;kAvbHQm}c93bW=^w1(u7lWGfX zKO^<%sZ;K}2OCgMt13iu{humJeeZ~Ir6_VTP!HeIl7Q^J0PGhX3;pyX#kyQrVLOJA z85pH9yVb=_x{Nr39Va04t7_d}yOLEMYB53pD@g+<=T|7t>06N{sSjz65aKu4L(Z9f zcIY$RM~SJW2q!*!@~5UuSaMXAWq9a3tFufwv$Ah$nuhxD`DUHE&t28VX3RFkM7PK- zj$dA_7dsfA9@#9qltGy8qT0dnW6+vq_Y{OWgq;dfuy>{`ZA+ON;D;TO+fb2|$k&gE ze4395r`N#WgsN0U6`057nBxrFsz~+CpRMm)hwN6RirucrE_=kV;d;RF@Q^_tva)JJi&{dxe+VX|!qEQhZr86%4=K z7=r57iA*#QE4XmeGYDDstxhH!cn|Qsb?*-cW@4`BMr-$N7B@zP#ho9mEm{H(nqC8YD=nRg^VrKxu+j}cVT&dg0+I7 z-v0o7F5?RH>$CV7Z6f8X#qz0WlHps)l9HWE&xd+rJ$QNiY295B@pLtDR(J0w8*R_> z`cr9fSYl}I>-qHkf0iwx6jyGynV*`08AB-_XI%aD0Bu?<7d9-? zBgmKB+I}R37j);D9VzG&Q>mwi;CdR55pq$Hq6wQG771^bPBsd~TCO%Llz^a>y)sgX zUV-bLNj#}`$tW={tS7|M)HJ#3k1_cTF=}MxEPL0$`&EEHRRFG?DOm7edC&(u_cbW@ zWK3pEV;R<#vY!v#NlO;QK~O$5B1+Yt#;yxnkCog|6FBje??A!vdGe;+3v@c%PU43+ zr#viUPs-L8aUH?mWz56oBc;1d7gnRG@w=WD!>kRgNtfcxZ9Mp)VOLkjt<^#x48v3MxP^GwoXYHa8 zmR7l~sfML#+hTSqsg1j6lc91l!D%|vk4#c0cA3#{iqY5I3c-2bKsr=+LPHUnklc8Y z`XRAOoqKBGJ|p)L6`OoVN>RiEI@MNtm7BjaYk9X>k8Yf-mQ<6zJt)bVfZ%v5Sy}je zu~%sqX;Ia1bLg-ed9^sb$JHG^b=o)WNt_&voI~O|unAG}JjOH1o05e#hl-H#ax(s~ zInSZZIP-H|N}IkpJp)(uq;HR%N$z)E=?2#kNP0}UVYb?B?bMI=x27$tx^df8v72+w zLw6`~f#OPY_)_9Bx8As4Cp94;FRMWC{{Y^raJr|pu`Z2VmmzJ4?P(H}oG6U_QYl`_ zwO*A84shI)acv-nPffZ~8F>g#6IO>1ax$J`q~K)t!a06{sNZNO#1GDutCy8`cG6s! zN4jCKPHVhP_xA_3ZI7~jDl8RRWF^%%^zfCS@fD5po|QJ+WsdhAUF9#vf(E7O(Z4R4 zsSZwp+i+6Pd66%?4*Ze9@S3=9{d&J|{LM(tq@0XX zWyNM>al(|J-%;XO;VR>JxVgfM9V#1sk@Tc?g~+bADmgK(ElC+K`JjIqw84Ovghvy` zp4pF{>-MvnV%cksh|yS107&M1&#&Z{8Z>r{+k{pWmlhR_B&$CT=B*4|Bc?g=zS>*P zl_NpH-+C0d@*7L7I54CvREv92dy}q|t-cdQEy?MfzOeG9CopA7SWb&)0Gy%EvU=2; zZQd&2-|b0Hv?DLb@>M@E!{P%=D9^;I7df64PFkEmQoKZYDxVXPpULoFj|s=Bo{um^^dbb zP5@faIC|1bdHBBkRHsnikAzhb^`v2~@uYbggeAY(4Q>ZDH+6*Zbex~gIneoHo@KW` zNeAgXvrqD=6Y|PTQ4w(h$Q;#acGv zvWzVK^i<*y{-tVIe|*-J!-kb;4Tlhes1L@K9T6iu3CVH7t?0ZO4C)>rl6n30i0^7f zM}_YdJ2m7b4m*uWPbw{BEv(~YQr5JnYEGulOjV~$ctuNmG}PdjQm!u49Slv z4u=@IE!hcBKUil5pK-f&uqr|{Jm8Oo5_$EjMEiqjp3DgDp$%vNsIMH)ps2H9H`(Z{ zzK-^W-?=0xY8;J6jXL3MT-eVEF2jK_3R<)FbJC%$<{e;(!+@2)g#?aXAT~5p zyYEybcbAzQ@z$aa;UA`FYb48t!Uy-SJfk2}^ki|ypZFb62GcFD(xgZD?QZ%JPP($7juObH34R0^JHtBm>98ZAnw<_%X2+3*(d%7Wqq%e|H5`VRk zN^VDJu)#TuTDPObpSruP&P!9}m$;hV0V)H?icO$LjMCq0jJMla%5?4k`I=DoiS;J8 zCmwmIxO2dl#=Q03oo#E@r6ojY+NOWEKeoK9o^x6oNmo;W(vs!dfWG*ZpuClm-Z~Ck z{#7dBXX8uOR?G`)BP)CRUu7fA)3>ROw4TwtOScXpwG^?d%fvO0>ixbv^LsY`Gr zZs45gz~lhO{mJkU2CxXepUY zx|s)v7)LY%xcQ8JLTFc&ZRhz4y?-ZP%6Fhh=jls$#6q7QFt75c^Htm1J+4!1tcxSo zWI7O$wimYMp0$qGN0l&1C2%dhm_uz5j(YKtPNa@*m1?eZ5~X;2(_3=D&(5*S9S`I< z&NH<)!)t}1tR4O@T0radt0@^HmT0Uaf2^RIcT%oCV+sMW}Dm>Y&u0(dE zB(0Xl=B;s-#lS9fo-!vtY7vhMmXBTPN?At@Ix&O|>cKMLnRdw1Un)e$qu?|&lzDO{ ztc){rYGToFN}O6AO0{kA91eXbD&2O6Z?`H(0miNXDYw>ss85|l)dt$aoJzvw+LU-s z9-~h}E7`JBr^I*;9!mzDb_4Mn5gP9)W^-k2>*cz7jr!9fG%m+Td2&n{sYSQ*e9nD# z9V$ya1iQml(j$fKpCl@se?p@=w1l$Z+kZTI)2P#@2QuRS0Qr+mISs^d83opJ*UVMX z`Bu3zg|0hkZrvYHQk11`ayKAU3#51s#+50?!^x0TtvZ1?BC(;LFX+zy02)wFhgQWb zM_#3RWb&lQl9cf=?pYk0AI67LdU;d^6LpOl^r=q1nT!Q!8OOU;JSxRoOAc}Paa5l* zd`6E6C3}qRQ#j5c9oplkPo@({vZX>Y#!#8fZ>2b?(4b%Vec?rK}~4tk=q#eL$(5+j#@3 z4#vBb2OdNeWo(FCfN!{=+S~@IR?thoGYE#wCR3I zNA+#|{OUYw5?f+4bm{K@09hyWy?tvxmg64Va+a4QT@bmDTX68yM^c`(Ymqv8?oUML z157-64A&$1R`mB$+j#hXvH4VX;6EQ!?8a(*zs$x+n&ct6Oh@ph$hO|xQ;#KtHr%GN zRCXOGIsB6CAl1G*^jf{eYAx0k($O6<6wIfYT24+elvf=qb#5n0&X;mY&xjQC7^_S0 zB3tG~eF52Tyi!~aXK}e7g!*L z)~soE1VZzL#7t`;J{){oDCU{qjvB<`%bGsVk*PBzGSlcWID8ie#hSWyfpUeeF}gPr z-qPd_q-*^~xI?rk)BL3NW_aoR#+>T>Y5_OY{UKBM*nK+&e>eEoc~$AYDeT$2aQNxR z8&)*_6>-sgX=;w#0@uDq9u!8AT1v89wk5VeZrR=7q)aKv}m&hm80`zI<-~2 z8_PdSlR1_6k+5Sp{A$K~&wEwgCml#_L%^(&ij=#`8+rH)@ngYSc01!I&Z69+!HX7h zT?~#3VLHdl(!TnOVYWr%NPQv47oJGY^&Z_huF<-tBcZM&3xh?zT!6-D+tMue$8HK> zDkAQ)0f9rOr3D{%CB52x9O>k?jYVL0n8%>| zNv`n;^3sYimX(S_Pe^&zl6)w2!bLLMM?#--MzA_mVIX6@xLe@3LDP=8^`bVRM!Q0} zsC`!-W2Ku)+5-t$JCc4>p9*+|YSgTH(wCuQQu4Bw6gQw-<7!FB_v)gozv4UnA#@gw zAtUX&rBZbjg(Z5Dq%Q|G!pr3Zy zIL$}l;iakMlc^rHBmzG&zX|KLNI&+*m6c$QLlw5)lQc9s;|_c&d8J^C^R5$oK>{Z4 zDF-C{hgDNI8#LEAE9K(Di9SmdmagoS9$o2yx85i&B&J>m?5}^r@^*@9Y?7~No~GRk zZ446AnCYyIsfcgIVheWUKO?!e+5*t6v8x|H$;6i_nCTzu7fNo_(S3Y`hjvflMa!)G zHj}zbm@4#0>GP-9itNv3rQoJywp0ecpIlRft#7{SwLKxz;!&YP)KJ(RsMP43M6zAD zLJ3;Iz{2zUseRm)!%RN#my)B5ZuyL&0FvIQL8FB=JOvL{*bS5-ad=Q zb;PWBS7~MZa#snnAR$>&l3WBUA1Y-Q3wyT-O}vGy!$wF)Qhh7A&i30b7nG$os3^c! zTzI)t_bXdBX)0?3vg}j^5$FQ5PO}2taB&e&(_RdCTltf=eJRLqOS`uFM?%x=(a+Px zUHaCUu0vsAcL%4%;AzI=89UV6YbjC#NPV-&j&<3{+HK6-W%kJN^{f{(4Lv$%YK+-L zaFzD%MZH9_j)>UvZ+GB{vV_1~gQzI(W=t;$1Qc)ZR zE;e}D!)Y5WInO#_1rfA6+sP1=cp&VAZB}I@MGA7;cb2Bs*yfFUAH!;Oz?h2Rb`_bK zN=9`l(34lN1a}hE!p#p_8tAm=BAE+nDns45eiBi`Iq)NIx~%IWDs{oUO&Xd%5;bgp z3hYD4RA*N*)QKt#{ZdM~`qE~^aK@I6lKUqLq%Pq#*(*Ts@l{5mOuW8^A}ZM91BiaH z8f9g2mZ@AO^!_{5n%64x9`$mzA;ipmmrBUbAWbBnx`1BC z<7ve6Nv`8RPZTHJtnzU`#+2f9IRV4(6aKH)ng~Ce$U!H@g;C<}>uM~OdE$i-tzLUm ztJwkMAqP^i;pX6w6a^9gv9ol$EEJtaPh%sIH>a{`#@)U$NUL@#jU{)|W^PpK%sdJ%hoX`Svojn8NSM8@{ zt|aMwoipWs-H~DW}pzQ;0ggG+U^oE&IH)|SFHFV7_5#&pBBjXVp#y<)oX?u;# zhtzZVHAir^=6Q~F7JTQLObGdEMXUm)t$6slVwPG8N(%CXlhU~ztM0A5t7@GlXw?i( zireZ+bAjUCuMS$JB}|y>Y8wjiFC4iFlJRC*Q6ml>Dlxyxuu`#;%%s(pRHQy*M7gyh zM-3S`NzT=9zsDLITxIZMKIgnnb^`*hvlE!^cL?#5<%FucOE!B5Os8$lvd;=m3W}BW zL7=|pC-Y$^DM{X#nUzI}+`}$5U($a8U8Hrnh%#n&jauRbq$`D_5PnppJlc@hlATH5 zXKdB+OfAOkbKBPiD^`)q%CxDaxU49+&`v_WbdeehLGX1YMa5x0wBCoDTTDqaWlJs{ z-18L*T&o?8t7`$&o3+_X3ObKK4*91b*xlzxwOMB>d46bRXIF&R7@1`AMLyn=IF9{{X;zOSreGMjLa^s3^I$J}FCx_{d zl@=nxW4|T-9_Ju>{uDSfUVZI)7Kb3MvG|%bBH=9#zKcW!N<=u{oOO8sW9818lRaId zw71_=4ZbYbFyc4P5%qDl^eqN#lNL&w(A%=s;&iJRCpDjKH^fR7!dncTJMGIm&|8-l z-QY377=*8jop8OSrA~|zt8$bDTNbG;Ok~Gj1aL}J4z2PJts!mvaorE34!ESHsU7K% zI8Q^k4Qcs#(@#OPrqOWmDs*<-%TfpE`BP6eJT7m)F<&lJ5WfofrNPZ6j0tH4?}eo( zY~cPih>_+p(}RkJl+JNo&2PFTpZYnDHFD4Fq^divy{XdDbVkpJ44TxZ3YrO9rDhg> z9)GTbPsj``Bpw>$VB@82Ij>x^PX}BMsn@uKfr^>u-qp&aTkUSgh?vm?()`BGf}G&~ zx=y*)`2t ztSj&9LR`6!ko1&^5!rRax(*25iexPXf6W-7io43XIf-8bf0$ZPwh?tN2u_K(@Co zJMsxkx1{_nV4 zuDkW69D#xLjdiZOc~--nW9?F_YSfs|s4H-!v~gF~93`T;anMQI&bUHiu-DZ~nH8lZ zgnAmv)OR0hl&4%#2^q$&v{?&UqqqoDWm{seNk3s4f4xyzX(;1JkcN`A9Akevj}q~2 zn)~ZmWw|U!XF|#4j}0N$?UJqb^kBQ*Tx8{4J`gHTXq~#S%45IXnzxpUl(p@Q^wV4> z?7fg>xcm)L3J*_qfWI{y#$b?0@_F02c;fqFF-@DZ_QM}dBw7XzLK|^ z8B#?f+0y4aw7cSI-0-pHGuFO!F`G2OZd6De>8IR6fKbj32&j!sz57VNCkTrOxvS?= zJ4`RV-QQ@gE}4VAQK!k4 zU1iS+3U9;u_Na2w>nyfy@*I&9P}$WfB>wuC@-Ln7m)zqr@N6FmE07(A;?nOus zuZX6k7WHDHt{8(5dqzU$87Vp>u^ z`!vS?0M@7W)z42gMsY+Fy?^Tu6^-QeoBC(v}J zW4YheIz&{-?=1nsZ&xhRTziDJR-)K-mjbt^1Cdym%ey#-)4$w$QGKR6vw9MQvz+4t z6m}#eYUTM;;?CRFp~!jG-+$zUki^-DTx2_`a~V#LAe8x6Y#nhSiAuP(z8_}52CjaoPyYZFG`3WoO{_1+Oq;)HT8*O$2=2}(qwlEpw(dsm z!dgO0x)cuII`%r@&sEL&+-98AgtP$VxjuAyi||cIrq3 z{HoH#t2cdM8T4ng>r6ptlr1 zXUe(Li@Mz0wMA!0WwRB<@iWw7oSkL7s^uw_a9VHK?t(S!tSYB78XT9@+=jT^7D|da z^QPi6YM8$C%A1U^-+@W{ROW@W*8?R&qDMsNo?e=hm+=EqK7wkAZDf3*w#{%JNTMwH;->4ki?bJY%9+T_xyoY}`w0>oJ_U#0yf%-04@< z#Vkl|wx91Vhg)mm3eyj9|9#d&>FX%^>X2O`FjU5=F?WhcLVs=uO1{{X|T zzoAP10K+Q3pv?aO!zzNil(H4myDGEn`Jeb@R?sGY;mv=e<^KSIU+DQ?;aB=PSNK#S zw*kcjqlAZ59BWE^fu^GS6>(C91uI58Mvu@R{{Y#bQvvn=0BcY2q6NM2k)->?jlUYg z;JTEKsaijcEHiFen)<3+$o@1wf&T!%MSnsXe7njkDuRtYPkK!_-ZyDw3Lh%B%X)g6 zhV{NIa2irF|bBG_|%LPkF*<9&a7~lS5z#6!JyZdNWxk4%>N<1~lZ^U*v z<^kXCtHaHq3@$24khe8p9}bWYFFmLfq9IN^J~K;Z=N>DOOb0Tbi+!m}?DL_81Fdl$ zHyZ;>y*W{OZUo>nJS83U^G68)+Dzc*`FF0(PDy-Tw4zKOGt31O$i{aUM?r+4l_?lNGXNp@>0b?kB(JcppCBdSx&OQXi6t1A3xr6o$rwPj~DALN?PTF&Gj zs41X6v&lcMuF4Flv&3p=Qt(np}D5B4piJ(u-aLR?u*U*VO;eC>rTH2w;}?-Wtn>i5Van(;7e{4GWL_KZngQJO0YZH zXD*isGNbpFBgcFtrjSYV=6xyd(Drc2L_qhmoQrX6aT4j@SYK4>p~E>#Z6&RKcoYHU z)}UMaA!~A7#fYt@M3ybN!?^RuG3Qq2X4vuCB(kL3Uz=*&D|~p5I+*3jh`dR!hS}n_ zo@Za+IaCh#)C9|EF7Vn(XT`A7>HX?{90Hjoa*7!(BmjR3F>TGWI_w4{+?_~L8&{KK z_tKqY&X%0`oGz53_*Z#QoXcaHlJ2y9;)@cRV&8i|(d9HWw)(u3Ix3xr>~~`+PkTK~ zkaZpT;=A-oF2ABshWcfW8iKK%x2sB;z)~&^DIh*h`h`vkQ5t2qa3p?_qoPBj9#aqT ztOdAL*D_6ZS~~RxT7@7s;D7$q{xr7Ql_jLQL3JpWMt*dMP~!65<6HRxp((NMn^{O- zom=3_Q;2QODRV&I025p|w{Fp?F~<)|*47h>{{Tt$i6i?={{RX)Z#|$ZA5}?ap6?eM z>5i{-v<0)j;Xo;OX8x|pMK>LZ_vA9#G`62}%8t9m`JbCCs=dwH^y)G3_hue*9q3_xAI;UdQ(8e{(92Eg zM+_9UHrx&TtA?cu^hLxcbBU}cBxe= zP*sjHmoDnc^&`j(4e7~DG-OEs0Hiop03MXfvg4_y-`TZf9R5hh&{nLRW0q@U_tbw9 zvX@i$?^i^ph(TdMYTluz?K0^6g=--xNXaD8X*b(qoCzw>CL=f;>;C|>c8~mGX_>0N zTZBn27FdzqMwKXZ9<-Q|uJE3aweZl|9p-$#be1j`81bG{d{EMk`h523OKaP46t6*`Zji4!7oBLaIsOWm?$W*4d>;$1|)So?tdr7y0(D-~{^B}J& z`>Is=2EeYmA zh{h?XvEFx$A~}~7ErlnF*yd56i|(m+#j0qx!kOG8FSxmo=5dUC=~B{*sEFLb0oiM| zxDba=LV8r7TDi8&8Hs~7Tabupw-w7KuQA?c;LDacz4licHjt+L(e|o6&4}`&vnd=# zo0!&x>GQ>25^@5hYae|uTewVHEGamSx7DP9)8$u3r)f)1-rHNasBjmwYyxkUICu-H#q!e+ft9!CvmW@@i|Zsm=^~iCf@;$3u)A!iyV!9 zZL&Q0mGHOcy2S*+Q}EpG{n8w4LQ3IJ_W|`3_*LuZX~tS`vu;M2tR=I9o`0y9Nr+;q=1wH0Z3I@9`5m*Z`utkd|JLC`s#U1cz!94sHsI?Xwr4Yhoc!* z6ui@cQK5zBml1W@HqgX%DVQ(nN}Nyw-m8^=M2nyH>#TQ3*z5F#(pqJP7*0;P30W%p zsu~%{4Mce(KkU^@QdREz$Bo4rn2WomcXrjK4Fk{RQU$Ucr=o{F$>H><36PnM+m&ui zi>G@|0%Uapr>@B2dxHz`Y09zlM6*0$Mwi4_>lPx51o&!cQGxfbZsYMKN)5R_$!~i+-tE6kx zWHN~?1~edKl5@)+bwz47X6~*q%1TnU- zRlTCspfcsTJNvW3HD$6q&^D-7^vX#M{{XC58=ozz+T2DU#O%9QR-Tn6-m+w+MPFCT z#m^Nrt6K}Wc8|3&gFH8#$Zf4S)}!?s0@dSEir?Yu~ zx?9(Al7}P=_)i$)m#sju+#^8S!r=U>YuFjSg zjugfbm5-e%rX;$JEpQr2eh@#67Yc%KGNmfD!dppYE(SvM>eR2{Np~Tc2q(mox)v(? zG4!nt#kHMnplr333=#9D)q0-O6D1QSLYA)@+*bGlt~q_w+{h?gR~Q^LNJ-MQ4?U@F zH*$1wT;eN^IG?Q1({u3j%L1)7=6#nY<1wHuQyh=)T1w6br8bMBu|o_??&~(V?Vcmp z=5f}NlDUKXP=yqiWn<5{*XJeU{{S$f?xZ1w_h}LxLfzHFGLy^krKxQMC!2%3BAQD~slrp05$8lS+_a@5k`+=G?CH@QX+G&H zaQKaI9d=xnf_K%prY#EMw&SWRQ;QoV1Dy+$X-bmQDjngck>|E)Df5m^DLWC}eNc~mBE_sNn& z>Mq7{4WQv*V;IMsJmlF-!D<-@b@zwXS0S}TxH{uyxo)Ypke+xQen;{}9(b><<<7LH zBL|w!YC5uVhf}cYyx+iD6-yT8PhW4)*9oQ|G#9iH=YdIVWr^}B2GvdDthI-1C{#Npgj7c~VZ z-VwMape9wli2~Z7s60gHghwn;9#sioV~G==$kw7VlhD%&QUjrj;$AG3{*Ef>(`QQA zLVk{-l0I0esBJh~l9aLnl6L9K(ttdVl}B;+RFuJ**;5H08clYAEujr5n0!WK1fV4& zAB|rvs*R!8*QV50<`fPq!;V<^X&6GBZ7zkpg!3Ymq9f``94c}4z1N4J$6Qd82=O{E zpmce9E}ETee6;!$6rb%5icekXyPK@LadJV?CAG*N5 zY3NlgHT@noJtd}wW5*f)0KF!TBeJhv_L|-^ANaJ_^j3HO0KKNJwly+@Odrlxwz%N& z98biN^G#cA)8ZwT)cN9tIu7Z{#y;w{*^+n`xKfzoIGF({>70*BgKLW+xmL;SC6|gH z3QASJKKhXo3?4su(CSdq)A6gOD%oLWo;$Ex?-RHx7~`*UKi$|-)8KVGEv54Yp!Qk} z2*qw(Tm|=C-%5znkyj4$*=YgS%XX|{n;p7=NjRzJBbSGoS&5@OvYm^VH`A&`V;zv z52G}4KkG@XqBA)=e@Ra|g|6EVUvzA%G17O^EBzY>?~Iz>*#P{b3c0x5wjxyACmcaT zARK{Ody@Qy7An%?mgtLd=q9Bv{{X|O7_`Jm;HwG<+-*$U zBOxkV3MnX8RPrLdp3E^3w+Jt(OVmy&D(aYqoCI#DK%kDB{zFC9B&s=B@dTh|rLNto zLM5RLDaW3`1JqLz2WxF{5|olz;X1v~`?YQn3_FB|Rl-smd3Z+KtHe5*c@ksBmL(C# zj`+ybGlRF4GYVO3w$sJN@X!QpfId`|7I{uLLuJ;IT;N1Z1;OqYVTO>}A8J_qS zhzfZNB%rIss~)vI*^{(5r3@xBV?a(e#(gON0602NPjrgjd!OMZtV}?;t%O9jC70M~ zKh}bBPpPQ3`4<&RoXyEjHuJ6OC~Hr{zpZ~x{{Z`BQ36^H`N@ciNfJ<%`bI`6MN(TE za!gcjU(y-*cSO=LA&pr%J<&AOxU<@3Ln_wOsEPpwn41b)9iQz(SW~YQ1ho48(ofY@ zE=05{ORgNQS9Rj+`{}6>+C){ht{|4|P}YOzsME6-Ih*%uoElrWsl2lr)E}&>dQ0n# zCFQ8N>EQU4=e+{15>?2DLxE%Gj&zrraVdEV%Wfsth6t@**_o7`)F(fR1KZNK8UFxC zJwPh?Bx*aYcQ>(3xjI^TtoOzL0D!9gn4_O27gq@ENpVieh_)I}1zgC* zWGXNCRcnnZTA6w@CfIo;rEF3$S@y$U7LD3pKf;uT?+h(T_=c5#3V%(-wX1@llqD+X ziUl21PWAi^AcArt@4bGAh1ci#0odg^sqQmyW-1~`4F=Z6tnHdhi@CI>GpTrlMpNi> zTvXiSaP?&YP{||9twL^8sIn0;OWBr+NWjfSVW(PQa;7&FKK}r~oo5+7loHRgZVdcL zdOo4G0o?S+N^;?JVLx?3dgi8((k1Uzdx%?{39->_SMN{B^*s5IqEY} zRNB;qrKG7ULBR&Q`>)KnElOU#Ey2X0w5R3N(nMR7^tAH{e}rYM>CZ}SHy5rFns9`8 zC%Bw?`qMquRHhzr!{HYduPEq!DZVV3ueYz>G>Iz(LYFz2GT3vv>Pj5DD5)-eoi+U~ zR(#9qtxx+wsSN23@vb^@KG4rfl8Dk@EFsdC)y5}k?z7Q3Z#k&8{{Vd`N1}do(=c6i zKIAFn89MZ<%QdFy-rZqiPq=?h51k>{YmgjU`cbZ4y-4%hl{LlUt-V8t8&`;rN?po2 zr#19tI1AE~;aK{lSH!FjT2SF!OsM4|K<_93=bDG0r{l$1os7f9DAak1R9lC8YD>x` zp6;}U@s3;dsO-a2xLX`YTYbcpB>mKpaxxPX>`YKKXYa205$Q!`)TORElZB*=d zPS0Xwr4MzbPqH+qE9};g=C*j4;so!Hm3R1eIc~I4zHxDak@dW($q~|2(K&g9{{Zn1 zTC)z&7UnlSYE_{k+*iUHxv2c=;)z!UI=(KTsL+OuJu&xJak*b6z;)u5;pw-GEncUk zKz=3aRHVyN(pz@k0)~^0UR1*}?lNFPabEU8x9TaWH#mKyc3N3&sVPUZwoqRz9<(tf z#Nc>}pqge}PStTc!pm3|oMFH-gXK;&-t}!)u0(oLqDa0+wm5Jj@t`lpqH&6gAYLNPL+`FXh6=_CdDAi8pBgJpxDvSz zLvsiobtZHk*EISWfF}l-Y0wK~^xm-|=f@sSCF4y=iP~FlyeMR}BCO%kGo<>|s7^f% zOOEQAD-5rgr&f#2k~D_3D8+fD>(V)^i(Hhu884y6+ExlxS0%LM1dD7w_}~nz z>h!47VF@pKLvHId;? z4(cB|8@NaqS>Y>s(v51{Kbt(O;$G(;n=~5Y6)SIrCZ@toq(w!w*t#a`R+n`YLTixS6 z!(3^PYUitTkM7Y)8S<=cO&|NIuZw=>PBH6PQ;J&5f~`yFD1`DOsHu^-N(%O)Xj;bl zijqGHakw$s3rk?3E~h&4y$h2Mp)Ru0U0iMu!1K*pT#(*fb;X#@2RR&XR<>?DRYG0O zo-!C9E8^);xEFp803}5ZrCLta!(}W!(ue2cD?h5HxhN>1i<1)|sC|`qO(b%x_%r8< zkA>g|Q>i$qnMg_EB>^L*^u~Yc={%`waS)XgTC#;J-mLy03VaC7#L@suO}Vf2)YU~u z@157^?1`YoDZ{0|Ay0SblH^wxh9VgH{9S2EhY83{>D{HB^ZTfn z31Lc0dY&>8l9Z?KA zl>8*SIVl7uS@NUa#B4ailFFs0Er&X0kse~LE1YD5AK83^c&Nfog)zi!#p>i!R~|D~ zG}+8`80m_ydZiKJ@Y1T#<+3%Ob~N0cmUOx|-{(`@hz9{u6~o0$36F1)b4$o_IG%Cg z>w{2NrZ}I#`d@WfQbErw)Mlq4r&)C>LQ?m_SM_uE(yidf@2FY>QJ7Pn1NFJ|sQgs> zq`Wwz)2&HTQ4%yq+EUZOQ=TMg+x_g)$WnY8;Pt5SeXV6nDOoHEdnQh4Imt6oaSHeE(Pv`NS{rai|lL+*IeQM&2kg~-#y zR-dFR&X~A&M;@4jzPU4d%Kg|M3Qx|LI^QMNB09rwD}yAfQ>1Q@@~QG~5Z`u732E3d zqR(1ZPObj{4XWH>DfPP=LyRrCQq(*c7+2p=ZS#+Ih~&--@)0e6sQK0U$pJGh@vJDz zN?gLA;MI&M^XKJTxII1l)a1yFU*aX=%z17p-AF1{+(hJZ9(6e_NaD1aYfEk_TbUz2 zDs0F%6kI*$c72INwnm?W1a3_?G90;u`7jXjqPnjVPnX;}Q*FK4MOaS=`^VA`6ZAY= zel*-Tk#3Hm>AmgS)6){-URIoJ6S8)rkudpqX$xC#xR9;eS~fI=zR7K-lu6b+O8)>V zC`)BPZSK-AD3r%{Zf8Pk`ZK@bipt|UFn%r-1*NxvJB0=*mfx8%Nnyme*x+My^7+>R z*^+c9>Ln~;T=UMEUw@OSj$cQ{etv&_MYyhNyVHIa9ZOpv5zEk2vu%B~voZ5BTMn?5 zg{bo*JdH|q5;*NlVW`vLtpnk2;m45`?H66uDZm0B2?+|o#)W#|R7J0Gcgic8Fx6!60Q8VKZP`#OMlO9T@h?`$u1}c+gq9w;l>sC)w#gUnp-8#>g1_* z;H8&3_2o_6+LLVIA{r$js8-_HQWw7UAra*F12M$CrN(t?Qhbd{M`-Ry_eoFlllvoy zz7zM<`+QsMSKf2nDG}AULHfe2>YaO!BiWhI%i%aV7~Z2Rzrl>Ck`pB=hovV_uJEqW z+zXi{L)vT8_)z|JUxR&1c}hR7l*FsBd8H$Uq$|pR5|pFU6@>BEzBWmqn1H38Dm3P$ zd8zc!N*92rsY1`CG{k8(sFIs_sqbjC4y5`MPdOB}P!Njykq*97oY2><)l_9~l z)DY-tu!F3I)6CR)K?z?4dbFOj>{j9-q$xZ!66qug1D^X-cF`)@#N|auWtBlHSHt${ z@a{OAWd-TwnnK|*734Jfj+dJYU*S!;H-*P_gsY z8}y|%i)d}&=3AJx`> ziv|R|DmYGhe%hN6uFAh`P1z}UT^BD}TRGc)pDI!pa2kQZ%ciZ8BwX&%j9a`mZi#+cq8eQzQwPVNSCfkHI4q`u=1E#_u6Mz++kgP+|<;WZ(Yw-qfRQV7=e zKDK_b5u z#klxt=GvNNm7-e52uMy7y%eK{f_}rnNrk{AP%`!I$sIt?3ut!{mLyt7M!3S6N zakY1qUM%u-Y__6Y0@a^EN-6i4I8u5MNoWsn`vvwv8>f6?xevm=9Da>_Bk-*%LJLl6 z2gCBBgOx#ZV57c+{3uL?g=A&clgVP9l^q(JaAHhlZNHz+kmE}nPlcD2^}j`4w5>?O zhr4;B%_gkw`*VZsF~et5s~$ZWA3qM&5`D7gDNmb{L>NfcbvhS~dE__qsK3kGRl?$U z`c*DP^W81Zdkj~6g$zJ|s7d1_+yjAVSHppUjj5$9;VsTa>O89J84{ROmE9w)wnKwa z1JaurZMXbL;hbwYsVWJ}igAAzmTD!~l$1?ZjRU0xd5qJoDoc>iqBXqIw3-}iw6&v9 z)UNeSUEtnIr=hU4wyEJ~SIVxE3bi}mQP0a1(dFZAq+kK6QfayJ=LoH2FaTtz=K{Em z++?kwGsI1?%u=;q3Qv)Oy5S=BT~c>CAunn!3-ANWxvqdcpiZuXxh{%Z?F&Ie;!2`9 zTF+>skUIBe(2mb)+DC&z3gIh1jw@T8qKcw-D>2-r+mG6YhX|6A+b(QPcbPq?jOvy) zacsDA0Hi=$zgidCXaPrHo(q|z@Ow>dAq(+%S4nj}q<|LL)P*E3pW#Yp`CSo3!-WjE z6Q|2gD^mMIZ5wWUf5MX%H*76|DDa>-6}R7tFFD$4PPHpqfl~KBg(5ROuC+76dy0S` zP{O|o?-DytYI%A`joC!e2edX+bn!|504fE${gp}TM7k+m{>U@FrAxq~80`g(cpK@P zn!@kaL-Lzi8opT+pm}%X(pT+!GD%O1>B565yV>$T{vetv_Km1#;mq{VD!N)n{{Yse zvenMgbN>K;O=WZKWOc>CHHM|zRC?laC>6=J9Qj;Zr6Dpc#OHtK2EUCVTx)Si-+ArQ z(qu#5%3>%6oPw^o?}($7L&q(rH~eU5Ol5qgH~eU7YCd|AEx+SH2H01h(oy_rAuL7_ zbn8+C=JhZ2>?>TBBNvB-mXRsFC?AhH)FPwqkdNbDIO8Xn<0JUdX_#s!QcgI?0OEzo zHh}1ANqH%CXi^XBsTRfCBSk~$NOhF7m1+4L)1Bf2n`G)*(WPh4o@ppmsl97X&amjp zet9*=ZqX-VKwE21@vb4X%0tCz;-d-ik3XFhn`EL!>2M>Fsqp9ac4U@ZdyE~Aq!O-O zX_rrDWW|jUwIV2SXkR|0Q;dbZ4}m33HiWN9X+(Ix5-GfU?fgfIi-~PPAZ~oUF;)|0L~ii-T3$+0mE5ThG%2)8K_SDlE-tf;8P;_JO z<@qz^MO$$5p-Im4w#W-gw#5eA%HX(gh5e86qcRp2J||iSWr|$L$<~rR{7+xwNqMph zn9|FN0fPHXJ`^ah<9dvqzM>OP1js@iByrDNn=5nsY;1h?JEn2^pZNJ&BcW zksem}i18AwS?RaJYQ%7o<*KHj*xP(!^Ubc zJo1m*L>wJb#tlMoXxA1b{`D=ecTz$|)RAn0n9a2$KwE222S0U8w#O(6k`kmTIMSTg zu%p_d$hkKZ$hc0B5_Oz(#ai!?8;=1~FeOGpwCmO6Dm=NcW85xR=}oh5arLE5vK90F zwLao3@U73;UEufF?}VR2aSKm*apGdSHRil|i?< zr;!~<05IIIIp-jDN)$d24%3^q_~fNCPC(L zO}7OucL|ZqmYPztqtH`#tHcSD7bI(H9JJ}-qt>0dShQM1H4!Z2mN;l9J(N4*?a$s|9E#nsR-zM(2a)mqp}vxe)tN|AJa<--NZ0mWc0or@vo%xslLkVzVm z&YQdThE>JI+zDKG@zijX^B*b_t&^r3RGhR+sydK!%RaR#^>L9W*_z7;Nm{&WrzTln zmV*#+q(^<_D_ea?K6HzGTa)aeUj9S8Rb(~kS}j+bxSzTb;@w`A8a8nz;AJ%|wpvVw zKjRg_xZWTR)iDK4dU=iP$;01;VIcxuMpVwt+#?)QP-8A8HamaQr zlm`1}of~{>Tx(RNgsHa}lIc<#=eDj{uNso_m(5Gx6$QtX5T4?#)yv{E!oQRZ#*-m> zO9ncQlb%$YY=^Zp+}`f%i0UXxg%!waZfA8UQ`*Cc$Hhbqg1XeH6C8138FxGYLw1g)eTrMwT) zsBybNHrHWGQv2GXqATIV9#|(&l|{B&*44%$AqZhjrFm^B_;qSX%_ZoPTa4*ek`$~A z{{RY>7{A*t5z#R^?eb#}i_BwsvWILI18+?AG`WnU-UN8Hb2|`CT~p;Xb+E-+m8_%V z)AOqun{|IrZKvdC9ckn(l(?UZO7|6>=NmqJ5nk!EH~oa-qn}iM+HZbRv4CpZDTR2O zxN!?-;ZOiw$rTtOTf^I#P%ZT@FOtmc`N=K5j zQ5}95wc727DYwAlYf&fbEcw(sY`)H_^vPrCFQ*(<);SdBnzvePl5Mb5;C6J|i`j8s zqLI>-{M8oqZh1Oiw#g@NRP@a+`8i{`MU+xmZ@v_US_jJ=Dm2#8iBgbMxhQd14r=&? zE<~QnToZ7>C75eSX-gcAY3uyP+l?*P5U8<}xM>OGGuPIeX8mDxr{X6=Gc70M&z@=% zZNo7v*3wlS53epQN6x2PuJ*-U>~ekNWi~ar9bSXVpM_wyzZr|35iV@8rw#c5=Tp1H zM`y&586&zhe!50E8sgcRzk5Ix2=P^x+eWT?`BZztN?yB1l5P$Ktl>&L(_Cw%vV3X$ zFoi{q(OOCLH70bnJEH{LM$Y z#bdpoM(%SX=XD_U9<(L!?h>bGiXbJ01#vnIo&VmxK}O4Xj_bA=DlRu7I&!8~e|1V#fl9f|klLG$3J-wt$tJ9q^vCbU-m+%K`(a$^ z>HYFUTHQmQ&1sAsx5wjCZmR{iB)As_I@OZeQ_8JT?{Zv(yL%~ObVa8Gy)T>R3L$hDGJUH{wkQ+et^Q)6rSi0@^mXONk zAsJIx7~j*CNs(^43xugyOD#q_2Fo3w^eA#kQDbmrCYfUn<9-O+c85FZkutsWts zVyDkYQ&E=Khih$rWPD2d_NX?wi)PU}-yga?sUbR(jCJQxEcTa|DkOOvX|jOA6i+0N zI=S|9v|mOhO5C?gS@0(~+tAdAR_EEd%aRLmQs0Rw2dcj64_>07+g-{!VoQ=5%9_WQ zp*7z7+a18I&KogY3UDVr559+K+Q^sfH7`LF=`!o8;f*h~~cM;nadb?Y4V=}yQ< z`W3#%C6-A~#=a7=YRzkTn69@HJ4$MBk_xe-ksgMvoMkjy{!lPA7YB_YUR8aC5@c(1 zcsk~~>+BWk@#9FvCNw)`mFRyIWxWLfYHOi8^RDZ4Rle77nmDohox4cD^EvaUJ3bPH zvWu5~?;UfePAOL9K14{5s#+Qd&xqh3*eK6-N;t@E4eN2C@ae$Y&hwE zFWXVL@)4rl9EjOS!ayZk{q+&`CAQjIz2UwZ0&;WdQDy}++po*wjydoGeElhe$e(C@ zccMgVct%Ft_O0=Oy*ut3lNv&FKN;Rf{OM(g?JGVXioABR;#*cs6!% zj~5ZjciQlpjF3IqLh3Q|SQz{1oPSv}Calrla5BQBsL6jg2~W(^E!z;xwJXEknVM2Sn#ZX&p5vY)BUH)^|JF|^yukfW4J$AcXWbtOo19F^a3H7H0<_FGg5by>Qa* zb{mYzjKp_L$Wu9i=kum5a34%ooM*gCmgn_t{{R|PYhCtqi<4@X;#5!&YE)Z+DQiDw1pEFPYopcbrmk%F+oaUJVqZ+*GHWu@qfFyY=*h%+i2rodL8G?7s4R)O#4 zZ^U0DC(PHl?J{NEG?T+|fEy|t$DJ*p$YN7(cSXlZ`uwUqo8M@W3Uq`e)}b>TC?Fn+ zH1(!<1=P32VoQGui1Vaf<=t4fS)3n5SZ`X^uic7e*B(W}*DcN)b5NWVtMsXM>ss22 z8Y5g;_X!EgTOYjB(qP?VUJ}9!vLf&_D^FZh7&ex8(=HBhm}vC*{HZQI+AC77Wxgt* z@Nfb9Ij7@3*9KfQ@Lq59s9$c^TVhY}=NE#<{0p`ik3ZCcT;EU5ng1A?R~FA6oj zE_F(IoqDm)0-17ZTa|BpehIPT=+b>@7Mn^MedV{rb;bM$Pnn}Fr>%3Iir%R#M{7dJ z^!2R~87?jBfGA+|R@|wnlOvRDS>W@&N`>3 zDp>Q!l^bosGYvZC9f=|-_e;e{MY23)v|aj2|m32d&M!leI*3>5%(CfG=rx2*3Ecjtf zy%ppICj?;p!4&JZ-p%%XxM(;sq`^WGepPFQc(Pg|$Xv8=B}Ryx9r_-IyG}%D64|+f z83<)*2}<>!5znPDFgI!NMoeU-R~C`sNZYSEw%qPZiNd!t-PIl(rq-;Vk){bD71#{bbO7La+b=lKb6`VJT6`LZqEwe6v?64%i|lOeHef8!xywTCsy%cKaU@ zwj5rBE7~Ut`DD`&Z@r(74HMn&?EO5cM`Cg@S2QAHF_?!jF_`ZPJC$bx^;4H=P55M8 z)e#J8@spf>8K)k&*k(dYE{2>`q~SR{hG|I*^G-t3AnB&&n9i8WSZKOc%QZPx_)krj z4FXG&F|tm2)$G4!w!d#&l%hk4U%g6|&}XGI%6kNd?oC`9S)AJQr8!C&O|?y(z-EYtEpxrMaJosOWT6;d#!8b_u z&|R4^mPUd0P!C#e&gJGkvf~t&5W~!jEQ6kAu8|dXk3N0n&KnUTB`U{FN<9WD_RQI_ zU?y#~4We9xZjzkxH6C(qaSgf>RuH5*l@*0~4>M73mp&c3J9AfZQ*Vr?9RZ_0*LJq5 zBl=w8hji#j^UXS#&v&R6_=%3(#51Ri@7KZVI#sc&HOdfr3^ezZP6u24~Lj`^14wXb3gGjT&q#f+=4c?&?|{ z%7VP`vMtJs#cew+XZO{jJY+TAQx`7BT7Xi1@11`ql$5RclaS(5ozR4w=N&7w ztw?>Qu2Nha6uj;=Cv0Mzxk=y%z1d9`wRo4GFIu%+q@}2kmmTGB)8B^Yj&cjd75O(j`T0n|hQt zR>XeV5>XHa<2t_Z`^xYsIK@D^T~gEwX}j@?5%47AZhb0S@K?v;G#3%n@13^AJo*{o zyvr@cn6#^Pav#R64ZxDy$xJ|l!y!6S2U>w`Ipar=Tw@&vtxH;-M}C8# zXo}XTlVTY7QOVbyp0uUiRN4o!u@P@`*Xip^o5C)8@{iAR9ZB@4lQa}toxOz~BDThP z{IgM^L31q#!-L(|;Pu{}R8X|Zi3@`#*UNu8vV^Nsh-f`6rcQ7)_51T(n;BC`m3w6| z(7%fvOv39M*=V@Ih95 zfH|XeD@$A}p|`Fv8=R+AIWg!QRwRWo%9Ni8B9_u3Nkol2UJ60f1~qc|Z%<2pGw!jT z3fxZKBg=Y^9aibS_s1WIcOdPzokF+8j<%f-@Dv=|#7-(4b{>2^9S~bd_=|-%snSy4{-MCdarbX^!4gmVnseyH?~kRw0*Q zHl;bsj8rrF>em6w&AqnUx4SV4T9lkS%#8WhW@E#W>ydloiA$(TRam(&+CQ z*-6d~O^GpWEJnE{)*TF_DLPKVkj2L2`{rUeiVrUc3Qt^ClRi1y9E~lHYb{K;2BxvL zdQ@v;R-`FS48HJ@g6jH}Rq?3t(+S&z#w4{7y~f$esf|2%J>z6+`o^DAO&3t>Y27|_ z^adlWHtO}ST7@MgtZ7a@F-|l>TjC(2R}gJWQt!|1ax&nf7?46nw=-;vaTq9RsZ;xeqIZprX?Ou&NG1x1jixt}Dznxb*X-GEo&J zD#1yD-|(X%JP3|9yp4Uut?PjFSZuk>wx|t%3JkLj6Yde6DYv)$D8Buo8wd)=5q8$; z4VZSdl^g-==IKtJ&%n}wuUf5W+JY4h*Z%0j#VMuP ztjh#oj_3Y#mp_tl3P+73+YZ#=HcL6jLlpl28VgTltyYh$Y^!>MLE&eR-tvX;hFp@f&Ctu-4xn6$X*J{&f(bNJAl=VYzJhqn=N+!(DZ z?9HJ=I3vgWXeE7+gtl?v8ePQ})7hJQc<^lM=MV)kgy8VyJDM_G?87`oY^A#MyV6pw&M-WF5Q&awG!-YaCEgLpF3WV|@t}|7(o>O* zLVD;%W3$ra0pLq5&UB1YqdVEMg&^oC@5PmhO(@^-_=WY1FCHm8jem znhR>tF^3z-92=U)?4-++W}5q>ZEjo?PwS+w*tZ7UN5JD(91%!Ft7YNlTk$0hT+~HA zf0MUG>%mZJ;x_fhDYPaBX-qvn=pya6?4qSu?_J=xrwX{IEB^pGad6naMajdA`$P9r zk{y9}apnAO87V6KO*HIQ@6lG)bm)|tOqr>^NK<>EE0BUIBr$z$ZJecPkbp&Zc~16? z#(JR4PV^b>x3Gb*30riitxoL_V58yVr)qQkIHwkMxaOA0KRS20P3;3sx_>v|)A&>y z+tP1SB}8{9GIX3*YvtZvY8jIN7|uNJiCI}XK_hoB$xRam0tZIRnjkOO$ zP#?HmSsQUZ#`4+s-U#!8~q;~`E&P3fh+y@}yyFnkc zk{1Png7t|}SW=Xi99b#^{Ea}93gqR%_Gh7Om9yYVMtu46tA7^ZCJg&}oKh6bU0bN< zAk%Fw=&mwRp;F8hYgeiDtBhB#3olQ3nFY?^uZgv4-xa}j^%g@w5HoFXVxY^oU1BU+ z-c*>Vjc-~GN^y$qF?tGTIGGFp{y+&JeIJ{Rv3py&{?IZ=Oyt2z2P=2?TDh0|sT@S+5 zC2C5^3&wUnl&|LNSa>02v?ZkK8S7FYp|+OVB&`8lL!1vfgwSV-m!-DLt)#0Y6TUv` zZOBPdBRmqg@2#~GLFj#JGi31_E&H}O8VNr<(`zLuOV4o7%J)#DxPYD;i3(4Km7E_e zwkfTKID?S&s7S!w<4H=76A8jUMxrf-kf|*;kA`AADIT2-Ow@wn;DOWv>OLAje2g|-Tf#pguW(HnmTvW$d zAubNJ7BslM-7SN`QFmEZeQC4_MjF(u34CW`_fc*{ZXhKMy)iO1CvQ%atvtyX`O%3c zYcj5Fw5OPd-p;Ml$zFR?+4zdL6x+|wX`6hcDYQp&UW4-$B)Sj|gy;xXd^p>BGVSu4 zh}=?zOXEMGDjrn>NlT3>ZYVq@aWZ<3l|yD4_;R(L3z^0b&ZeQ|wIV}tgEC8V5~P8j ziym~KIrOAPoa=75pqFB{C7>%9*(7;XjwlIQq^nrz&V#J1dokT@@c5|*K4U&ql)T=M zh^b3ts1l*(aYc5#1-95**-!&ceQGFml_y-SYf{#Oq~qZj=}|;q&#CYl{?|&A-=l|R zA|s8sTT@z3>3!9YN{s=?q62GWX_Ie~gsnr=Q|u{3mgOjFbDagmi^qbeu`p>t-|wic zt(7(t7aUg`N=o(4jD?m`kUUW6U4Xwvp)_6Zd24Tk2t2XpQ4CnnrOD!ZmI>FW{JPTQ%C*OhaD9f6 z!4U!jPW@xA%Cn*&-=vBvx!GBS`wB_cw_U@HV|5Q&meh9G?&*}YvzNEiovASHHl`nK z?OJB>RfMY>1BygjbaoJd6(&ea`)lir)VQ}Z5+4>4gcqNY;12rJYqrKJTQgA@%Ho|5 zcrr-OtpzS4JZ(WCTvX#tL9uaep`{@xT--OHof+_qa?N+?64flU>Qv)y1OOMPry4-W z#Tgft8D$b=cSx^sUCCdT2GpreDElp|x}g}Ps3@c$tM*rMx4OxQ8!0PN|kP$7SdEXi~G) zRpEseGB`l+*pbE`JkY~v&!TAU`51b7;+J&Edl*93j3WaFuR4(>mf^PCspU~4qeyk6 zZ_lpukTa|68Y_n0bZDeQA(x6Q%;x)3FQjR`imdAHY$mPt9OTndRwzSgz5O|er zLK{MKD@wGFt%|rQ3OrUOwcU^vgSt*>_)biLZdY~;#qUpRQk*>Y@~Ia)wAc^brb*I= znD>FEIV;1^;)IrGB3PX2%G}}UR{P`dt1HbVl{%Z7I#VF{ej(4&pX~Q%I5Ms|4Ld26 z1?W9=RkeNUd&y}D@5_-FPKEW~%Ab=ZZOfYTt+Nfc20|M@8Tr?QojP=(zJijS`SVR7 zU-qMKzMyB~ar4%fx#x;v+rza-aziO4u`OtEdGjDs$-o*OAb@jGqA4li#ZgieIqzNJ z#H9yFY$ZJasV%BVVQ)e#HLXA-ljli?8E9vM*YC7Dy3!AR9mll_GX;+nL%Q~82PbZE%IUK0FaK1&5zCH!MBZVm)oj}Oe^p5mW*##a#f)L!D4@0T8V zVaJ)(!NBG7Rce&#R_Tm_P=ti&+u_Ar-DL@oq(L7;Qgg)KqOI96+|wwb=2(xTOPWF7 z&lM3#b*73RNe(^mr|0Qc2Fn)aZT-Q=MA%+1uRZ!^x@?-Z_vbC<@*^X;rE*j{cru!l z9>I6KtB9oX&S_)@oJX`Xg#(mfKNi&qSw{uP+M}R0P(pLmA6k;cSDImJx4Jlv@*jOD z%eX+2r{8U*vcthdB?H1mM`gTgT%>8w&ZWJ@34|Yz=e|$qbLa5+Qq(b>Uk)nR^Kwyd zhUJxJTgYgXBge2GrB@{>=3Q41YRi5eLzP}*Kn)cbEVFMq7w0At5?X!g*I{{h-KGb0i@kN^q-4&UIt;SHlmVM(R8ggvn^|Uus4G zKx8K+9J-2{@|lQ*IW22abexUryg^~PHubH>ySoY8Ij3b*VLm)zA(#k4)~~F2=8-aW zstv)UwZaXaY4sWFQCx*R=Ma~-wM%14TJ;)sHI4*K`S_|cik$EeQdy4UP7 zxr8>-maMNm2Txi>@wqz7Y$i_$XjU+-wmx-W21cK51?eFt!KD8Hm0N=D1WC9o061$Y z>(|d}lLf1S^u|gSc#&2-N+f<8QxOuet~~I=nmrUL3IO@mkU7!WTo)dG!=U0 zopLi}rWCTK+dTz7#m(T(C5Z}2Xq{;Tkf@E@JV<4oF1y0hsMDSKL8hIIwo>weAu2qL zISw04s!KdHwK29xE`6g+snPDz@=L$j&h;_K_PH*(Aqs5@abrOB1Ent;WYSrQK1&T} z^Jrew^Uf`9t!l|zha}Nur`&C~91bZ6Rx(E_`c#KxX-<@vR!PM+`wLr6pydl~STwOB zbeE9k>upY)eiffyRKrTRNGOhVGveh+QqEMPl1&zxlJjk=#8BI$3M*QawH|6&&2eKq z;|_t1Z7T+ean-LY)~#wMe?Q3U4M};nUGTWlTslbkQ2A3SR4aS(Vzt4IJ=SvKM@lX^ z;#-OJ+*0pY>sN|Zs#az)5YLvr@)hCDUhIJ9^P+N^VzdBOk zd16lxV?H02)e~F&+$k<4E|ux=X*l2KQdxT7^S6lXw)4sMhPKqDE07&_{DDiKBw?`F zOKy~m4Y>+AB;@3iUKGom;%2WgB2Nq!#s=rlG+Sx+Uev-IT3&yMq$}*_)AD@3B7v-c zO9ba`Xg-v+Hsj5$T+1H{y?%AaW<-{rAcEo>!Q5cbP?aPp3l61RJ;H}A=uCb2GUN>4 zxSpo6oat&m32RRxNvzk5YHIX-x4n7)HfQIFfr2ukY+NW*e0+9RBd!p7}_h4r^3G=88 z%D1XS%RqD4uc6!|YWqT?w%xiy)+8oGfaIM?1asm2oYbih8k`-JS|w8C>)9$KkC6tE zF@{vjFU?D#wH)Irq&700wot5kVA3|^ur{WHGE&cV1fgUd>pswLC$EZ}SAIwc=bcm) z?O5R_;5yY`=_WkCIIrPB9@1lRka&vz6cDStFyT4^7Wo_g6tAZ;ohrubKf;I8qwoI! zYyMTIzedWw5yt-jDhIu><@xH~OB3r=KlaJ5jh(hW^);R&7*^xn zrm?B73BmEEiAiB&(P^aY&V03^yiK$LI0sT|^KEVVNUzK=b?xz@zoRv%bSh1MLPq|n zp_mE#ifdPLPCjX&^dX-kiqEt5<&Xs*=IvNG*b3Awrx@}TtJ&Lm;86Z-SBsZ|jDKhQ zWS&2MOP{3BWRJHO$YKg{m!{{UpF{{Tb1{t~Nu zJKLOoX;d?3f91keEBT%5fs^jdCpKrdn>*uw^=hH~&nNsPQ3BB*pY}?i4W>>> z<-=8E+9ePDS*(jpta>)8$Fiko9Z;(ATI7%YS*`7oK3p?LO|n73-@s^;K$L=Z@M;uF zt@1OS$p*K#N#(bI(H_r{{o2p7-~R1?qvQ{oYsGJogU`WO7U@6kgFqM(e*<68Wd6Dz zL6iFHN+7Xz+ES3xxRfa-)I{#1j-P|&VGbEIK-9LUp2+Jt&<>>P$)u-m zcsDq#=WsdVm*f24$?(#3FIgOvQ%V~1;`=q7T>5FD;`q)9(^?7m74~ULD=JV|GCFxu z2p|pi2&CyK;hl?oAjuSp1{-9HZ32n6zry+#W5?f(Q{@i@azwEO)FG5hfA3Likj2iV)%X&-IGKr93zgRB5Bjy zq2z84bxkr%IC&|`zKWXD++pJ%^;FhG7=JG6nsfQS5b3mJ(9OOX@b!!-vMunwAfM>M zhHbF7PEXN=f1{yiInQ?#r5k)QO0qtVHlzI851xNBNBOoV_%)GkiVv7-yfC8bN!fe> zT=CPDY_E%-hLhstud`qO!~iA`0RRF50s;a80|5a6000000RRypF+ovbae;PWsfty@>_9^5!_=QB1u?Wf#X zSC*SPgJ_f{0>Xm<5IR*ZQW_LdB+8Ts7{3PuK(c{e%w$9969^wQN<*rh;8c-v1zKX3 z(J6hrrynOO?}%B19|N|{r4Rx~@gMlfUPV#Ke%Q07&NzS;02?mu5a|Ib5LAu#hT4K> zveNGUO&A0c~M0JD5oiK@7&D6#oE3!Yd`zV}Vvx=jK$pIXXUc=0vRlcrSl2 zjzX{XtC{}M0o)OyTX6R`>9&uXfT=HoFLx;>((e1rX-F$QS@SpHymTvb0*24*CMy2` zPy^NCTCa6We`s|LDSk67tJdvYUDUYYH?Q1H<8Sb}WU#0i@mZMGT;YD@d-Ma2yN82h zDfY~#X4eG##;U6^A$4OOYniYdyXsLPznk9WCdLYd@zlOlqOXd+zUDa0l;AE9tz4E< z@x-+GyLB=oTS#I&7KFEQ_ILx!(HA^MYk&gL4TQf}EgQBE()iA?%r*3^K3R zS*_&Vd5lDAk-&}oR3P-5=3FI+@)p#q!Pm-w1u*7m^L7QkA%T9qeaJgY~FdK=m`xP383*DaD)F+i)>gJKUxr-1d(aeTVT>OvI1OfXj+Ke}={)Be)|clmPw zoO*xunoy(b!zzq<*139x+o4`h5pESmv$#gK&R@pm%jn^V+$b80_9QVVL!_CVwATGRUwK(*?yvQ zWrOA}5F3LYErX0TIsh~cKGnIJTDqRLgJ4O?fGA1}HAPDpl(wmfIOp5~drMpTRYjP5 zfy)M=TWSr}I*^quC#d3lg><~9nwL8S7guZV5H?pi%!$rKT>KvQ2~HR*=y@QoV|YB) z1}+h@9ty_Xj#FB`X(eK17g8hg@?won7&XP;ks&&WuV)^PMsBavvz(`DmcUb_VylqV zl>3yr7y)t~qA925M>*_~)C4NPg66oy5h`(Gz$s|Xdzj^SDG@J>E@N%^3gDqdi)>X} z;VF@%JbLyW+SMpp_>{w|3%0I}H^deLj5DpdB@Th~LqKQ5?kl4t{b@h$MqBbV4CSp}akg164OOPEI6%-My5ql@ zo06MDd*W(by1Qg(|CqJn%o^BK$FM?0oJE5mgIrEiQ2{_U zy&}3JHF)$nOu0L4xbl|^i|{pQWZ@omaPnKVJ3s#bqE$L3SxzMeUG(Aq04OMB#6f27 z5yjMFgRBFHax%7AiB2%og~&Ps#Mqp7DyQZ(Pex4(*<-v;qT+O8Mxzqc;r9&u%Zv-+ zpuhsN5csbR!N|Fu0&`1CiSS;Rp!n|MbE;c~%Oc4QP1G}*Z4Zd_xj5d>WZ{Y-@_ zBZw2f45|*8y=UnunPx9!WKRgfi}SdOGg!NcE2R_!PO_F2a3M;qqxORoOqnWT{-&N? z2D0%HS7X4X@{r0YL)W^x`Hm&F0>5&!y@aB2wnamnh$lkt7>yO++1tKuCYIaA#`}T* zRmE65(~0DYU-m<$CGI&h8Oko@=(va1R$%bekgs^yk0wajJDW> zV0~B1`r%!adGn|er!s%07iY`=0E98;Z}`ECFJJSE(GNL45c0@;Kg6j3(YO3cnR?s* z08u4BC-uq~cwgwmpXxkJg|hSg6F}A7{ZEiIOnpyRB2SYOUGG@6i=AF$QN0GG_qEXUCl49 zT7?5oIE{6~KR-~q@fP25^`}aOd6d~Kim3KwO9~^V*;<(e z^F_$MC2=)yHf|3rfyr%PN~&*5J;s_ zVy&2}mio-BE>)#cVxE$*3 z9!7O)Rfgj$cZp?<^^X+{z5?$vg>Q<7XaG5CjGpE|T!3V^lBFgcue~W@+_VsHSoj84 z-wlH#Z1!XO2_q2I^)uKY>6)RJiCYRkTFyVNCy`S;jQ1<2x-X$bL)}W~7T#HwkO^xo zOOFR5xsayO_BmA)^z{Y@EQan8FQ1OhNcdvoTu6uuO@zWzv=1ihcM^}Ii$!zHQ&n!> z2MN2dglE8~8olacvm)7U?F@Gf+VH!c53*&$&Ev9~rYco-HIE#daOM(Hd+QD@xlthL zp{rNs$rqT_jCo&I2IqEk18dodSle{H#tU2|Rk4oHU0Df&4HDg~2v7GWvgmNQ^#VZ0 zCsy-CN1IdIZ6eGn*c%#Sl|?H?>T(}ZMmp3nRjXrat*t{vczA;MT?O9+7!tvJaPI4p zW5O90tQ{g@ILlNmhJee$5&&?)LvA!-vdlp0c4RSkKFR10hi8?!m7 zNaA};vxE6u^G-6{mTa=E(u(W2z_&F+DmaLpi3Oln3w^={0XroDtgK-w4(=3nUr@m| zz#YgS#};G_t%r+;fS@Jwby~h zrp9TTqPL#Al*IHdGZ+*u*2*}}BLP5Q0`PGTuT2?~XLS{{xLa43n8c*SXh+LjBcB_7 zpbIs#{{X1L!7Mk#Oi_Bot`m?^e3vOcg=x|dPCN!8t@i{#T61B`2?{Kc3qZ$cek`e5 zSxYfc1}T{?yD-Ey!dNwPmU6-T2Zk{oYb!P)XWq4+@ zITSCpXDm~~HCcmV1K)CszK}VSN67${s92|3e3ATmYijm2oI~fQ+`$P#Q-%D!t-w;b zkXBa&!SI7eOqBlsfe2xfMzD@vp*rr;?wH9be83@+mf7F~P~?z30Y!D4>k_93>IRjL z3t%o>Gkv$ob>b!;np785k6O5~OKHN(g;$w^K~J}LDx2vk=StzjoP0}o&~8y_xnpvJ z^;2@5LEJx^0vOp~CD!A1z}6y}p1@SR)y#50=og4OZX7y6D!e?(nmz^SEaJlR5kvDt z7IFpRn~A(WUedc`67q2#m%GHXBU)nzhBxgUo}x&utyr!=3k=g66M%zoWoqsL`7w*T z!4q6cu-z?R3o1rNDKB;FGJ!A{5N^7|GRaSKMQgCSfRS7U0xajsWhbX(qCFtQYZ#?k zU4eAe!=FJ~1%({<0Nve=;aroi;vP1KM2a~qf~BRHvVX)y`L~g zb$f42uG1vMwh-=I%lUdG!RXlqgYyg6>f%SVb4JsMkvgz zgOA)_Fz{i9zGlH6L>iY(qA`D&kESjipK-5i9Z@V*g>Daa(XZmU$A4|6FN=xLEDxyp zX*vypfn2O-iD^%5m8v~%9Up1-=!a8VyHQ6D55#(fTfYRqZoM*qJ1=ClEo<39yOu(T zy+wNLklgPBf!MObafrl zVaJ(`29rl4@Wx3|vc)%YpiwBeC%U)+u%f+d3d#~2;rHk#Q9wT|3*U@2fvS;;cpPHl zHzbPYE|McS)~0}Fyv8^vwDs~B9)>d^!`5pOganFH1ZD#_U)9%mvk|n9aL^w4>d)YbevqG;wS!myp&qDupIv8@49K zZ?uMp>ds^9P2GvJHD_=QNLR4t%-(3!0Hr7{yg@F37{h0*kY`f>mDGKR0;u)QzhJ~N z4(^49-_%NKL{pXDsI|(#&!bzM9}-Uz&~pBzZBR?IvX?elOjP4QD`3D8a?!+`hD-Cg zl6j`5V*%Vom$yW*qjgcCu+^hqRDD_lk%oVG!lMBEMKIY#1=PegyhXRS<}jv9 zGE2rQA(M{kS;sAbsD`^t$o^O%dvui(^w0|7i{fNJ<;*A;;1DQqb%kZTkSD_$9B^i_ zb3h_WP{qSEuNiWZ*Fokc`a02LkdZ?!uo6=udCKA)?iEX22oZ-7E8g~DD1e}f<$Z_G zP_?QKi83%;#X`-rKcH$GQRr8M`x}GywYn-h;x?d2p|zdXu3eZw2hib5JeVMI#k%z^ zjQmX(dlU65@sVmN@IMV8SD6l|S&D^(Z@BsCYq&dDreDK<%)8N^0KmjJ z>0ftVW%HSEygbtcDKWBQP99HE{+n^fVtTyF8tL|-4sU-mk*`LEW3UtIg$LysXUyFJ zGMr;Vxz2KU22y-ABaE>Nc%E_!)Y;Aho4Ji2#K1h{Ii4Vj>(H_f?O9hi_Jr z&N1ax1Xb}sC=C{TqpxXHeFKXCWy(aM!7DCCIh+;4@Ig7f;v*>g(KtuS*ERTtyR5^+ zb9GfQg3FomuQu^AZ@|rM&MO<3m?bMu*zOpJNy<0q?hi+jinVi8x}I=e?Qj{7y%nUraZ0H9qXDonmD<`p$FiT?nRML`td3$11o z);ccAL&3llvy_GlBWTW7fx3|G!~l0dh`&~ydyg#Qu$6OX7?uTPze%xqLZ~eU669JI zpsP7MekL3)xWsa_9XIExYlJYiZj&)u?7EGoQom&7BG7|KMd{@ZYL9~A8-HVLe47$j(Qt!4nw*5$=3Iw;knTA_OP#cY6iUBLFewtpBJ^V z>6t(UFj1#2pg#j3;XJM;4Y~K9`8Y>25Q~ISVz<;?n|;OE=^fcF4spn|>uRGv>vA!3 z0$=Kj01UnpGT*D7q+6Wjm&T?Ek>?kTOBCWf-dVOosG!DxG^64+EC~^8ZnR-3i&E78 z0PP9#iz9#otald`$Q!1SlT?=$KG9M&R&6!jN5C7RdmrG9z0s5NdHM`XPXu#IquwL6I9b$v@w9P8#}O4nA(WIV;( zJbtl_+w~44)$&zI$ZB9(w1lk9`H>-g(ic}V;Out%N`>7a!z`XR;g4zrcu9f*CL50g?Kw8Z!Rm?Rvh+UFtofoYngV*m@G#v z?kz(EqR=tX>J6C{U@dOJJ3kFpSZ!lqw2mAH*}(AN54fy`jSd(# zDqnW4Wa9$k1=6^N?S{~bTd1~w+3f@Qp*5uyuys@xNaDE zmW&Dmc@AEqVPwJvOD@M;OvhJp=I!$+t9lp4w!zkWmr_JP?OiyFnWNJ3T)M0(6wCF% z5urkyKonNiZ^I2{eC-0#1-C9>D5Zs2v^!lj;#GMuWR7-F7=uAZZ8$xVN@(#Gk3Rac zWM@RNygmog7+%#IkT?Z5b6zoi^RO2V{{ZAij>cJi%KaVHufU6fUq#s&RBbT6B@D=M z^>!@Jti*1&Y#S;08&MiHoOX%DHB}Jks|X&Xpm5r@ULlS|Z#FPJ1&~`j%k!R^RzmF6 z7@0$ILINCx*An#gE5AxwIvA2PXT;Rz$EX@S;w#&(2>y5VYNR7g`I~UE{ns2#ei-Eq zgJN2@M%}X+>*6mu9at%$j=9vo>Vuino@;Y(Mb<@wOTZHr0`~CiJToD%n{SAxUbWU{ zIOD;V)r09>-6M6gJ61e>!uA*(WUO&go%0-FtBUTM6*^`I0${!(DWaU7C4n;5+baH{ z5_C9Nyvs6LxZzp$Tbw8tVeP2G*Z73agZ9(%#fryn?v~K*c8gpa z)6+5(JsSoapqvmXhHm&s)4=&ye|&uyg^vYCFy`y(qa2btH1$<5{w?# z7p+RnCNa{5^#1^WYq*N2rWqD1BpVdGlERNrEatyZ>xy1?hgz8mydlaq4hizqwB)m< zWyKQIXsxZ=EstI%LVczA{34#)VW?QG>GG=7tZY=RSuydCydAM!J||?1VmAIdm)Ngs zVIxLw;$qvWxu#`@OV5O4n7*+)dw~KtFaqIY^EMWk=pdROGK!*r?`!f8m>YW9B9n(^ zBPSfVh>e-2nAi6$kaenF0<{#m&nOhq6A6+A;;bsN6a=ihvo&K>bc_+%7iGDC*8I|K zC5}{9_K!pAYqv9D3+=qXE!tjKA7H*cFq<=JDCr#+)C(nIS3fe{k%RuY3kLgtnNZT< z@v2O{u-!f;WrV`GE*BlU1$AWOA>@czwIi1Enwo$+7Vmw+TB%82 z<_B*S#qsm*A2V|4Dd^f3vc#cQGWR(Sc@u3+WQrock)G1++^;}-8*j$=LQQ)1P>^vP zPA@Gv%)Ds!roT=uR`@^E{$+r+eUrmc(QC7KOcNFjbyUj@fuJ8~)zu0LFBV6zMQ$&} zN0GJQ=3+dqa9)uy4}y_1l-P4GqJp22en~+HW|MVX4Zyn+b^$4;E4iD8*a#VR{mRHt zriVKIutto>j!fDkwB?6_jrxWL+_{&@A2DdU$WrXu*-8Lnk)XSBYFBiltlhPB95P+= z1Rf*}FS)(7!X9OaD7O64H_;m6D)KRl6=l_vH{jIZJMJ_)09Ix}(#Fw7v3X!Q$Xhrd zcgZWIqjfWF>oVRu)WeryaGWBIEUfcg$}g&IwjdIl+0~qZy;tVo4;aXPcl%O zVWk+dEvhvf9vNLlQoR&+Dlj8GW|7`9L%R%YLScGM9Bjaqh4;T2&A!oXTG=~4Q&P%# zstE2T-T*l+3=G``MTaRQo6$mMRT`C9HH$yj&laHZ33v7mNub2mx2H3u+|4W7Az)AW zKh#-c4GaA+4*e}^Ij0fY&m;p0QDfQV{u*VPp=-v!M|?I#@CSj5JVL;v8>4tq^%eO; zJqoQN`2> zTCl$aJ&_s_I zTB4Z0O@A{@qX2G>GZz*BGS(NpA_`AN-^egxp(G2%?qz|3Iu_zJ;RM2flsWM{I|xQV zm%07|O z9iTSCJ3%j^edrr|IO=0}7*m}d)r&`_V7mqfMKaJKl;JPUMx66Z^A}=gYv*)wDRtodyQ)h)K4PrL2 zDOH(JGNO+_q3TdQazXQ0#4MDd`MF59>>ugtT)Ma=$7V??q$@)sI4xMM)CsYnf`Hurw|lX z!SD^cWb-R>I%wEA##1m&+RY;vu3OcyL0Q}k^!Q{5HB8W@vAH%gfBCMh(kFOEFBzMn}v<*>cM97|C2VlBH>O9Oup)V36pYS}8LK#6k}3UvMwG#EM% zxeS~}cepKD{F&xj6u@%3Vfi}WKuZZ!qcd+*8Waj!7R;DumyWuKYNHsM{&EuJDTKi{+C#yV#3cOz8}hafOWLf~rfH=s@>( zvtt%2<=PG+(XU54DZ@I8I4A`RX0>h=u%x1^rm^NVS%I8kYaweois;j{{F0uYD*-46 zpM?=pEEyP`$M-R{ER+>n_frbSJ?-OBtW+DpCOOX%uC9}4JR3x??u+M@YMaEZk-v$h zi1u2Q167xJONncPO52#-A~u+3F>8>oMEUA78uDr?{PRe>sQkrHS6F7()N(5Qv!K1y zwVB>!t!_CPvY6Yb+le_KRaW%KQ-2=n zpgKjeDUVaD!Rrx)A)p$;{4vY^;9N`MH-i9ELbT>o^@H7#+yLRzDO+o)te5U9s>?_$ z*CV(-sICW4V4PU2PD9xJOi7T^6+@E+$p-8o$ua%L&<{bFJT=vuy)dQMop$)RV`{Ri3|1-M#`0{1-VSV zeGen~PUEK9rUPixoyU@nW?G8aI-Wyuo}teHP!T>EqrBHujv~ZlFI!I4_%k07;P~WE znSwDa6PSSo0NAw8qv~#Q8DGS}qo3fDEnR>y@K2uY52$1DxTlhL>mxSe<pv4#uFwon(wORa33vGo+ zzGa%U#ON&#xZ);aPk8h036VhylpUZzWI60#517RkX?$Z3(o-{MB>*NJ>h+Wc)+xVd z4K%TcprMp+5m{610P%#OOG94DU*Nb2(rGc7dTfrLLJB%s8)E}6jLtFTfN%&1#TY0( z#IW?sV$+?B-8r#;;CLz7;KFJ6d5X%C>-(+Ie9WSUDo98!pn` zLfZ)0lHz2gnsBUJdS>LBiwkSlsY(H)rVhqg-4$8c*idr=Bnp^VF>Q*MPM9lFm@)W? zR0fOv!^qq&V3VxPF_&QIl)_W(0s&(@%LvN+;xCDVaoG!t;$YLfpcEjY#U&yZR#0&( z2Fj+2wS2*%epp@l&q&zPs8*&~-XM!x3RKP2P+nV#j=C`P;sgCr=3dlpXm7*Qt7iCK zR}4AeI2fG%X6B;ZvR?xd-K?xRuW=zMZw`O?`k*|HCyD%Ba)BwDKN^nuqN-zU<%nCb z??$o~?HR_P2p1Sl7c)M4*rOn*4fYiiq-2~6 z<=)w$-nRC#F`XQ23*?S@hg{X-ry$1_41NSN+oTu^EsAzP{5q~?T({T@6LesTqDmoi z(a)Kha1j=}eM37F6V2EVy}*nc7|S>X_(BLd!$q62e&c936EW7nHF<(X1r0@sa|Ktm zTqfeE0d>IO{L3$RLDU{{S$EyJ6ehdyE7^C6l>z1qot3?)*b5fENmv zoBNgff>m3I$kezi9nz16cXcfnQtb*EY}*BC53GY1njFlWO}>GO97H2YmO_u=1?VEU zSr~4+W+)tNxergNZH~OWhg>v!{EBf& zLpOO2<#!2ztYx2@3KIJ7{{Wh)+lS1>4>1Zu)Wuh4Fu=IDc}F8Q^lF$rw~|9%xI1 zXenxopHcIuJZk}Uz{N97?wNOT+67egV7OLlQp*yoTE#GJI!dXmS=So+xchl#H*Q#C zw@_!)!8$a|Pl}r)h^TgvhPE(PZvfjNs=lFmqh&v%7WO)ma`VQ%;JDo#S}uStp5v+6 zU{>SAa5JPT zQ(Q)m1OUQX4r*MP=p$yAM9cG)g4sc>*O*%i)w1}0E*PdV!6^2`b`1@3?Ny~Fl$H8~ zCbAh(L2u?@l!Rx1SD`JD{v%F$jE5Vj6cKFkG)}IeR<=F(JkD=(a_CKwCp~rFnc@U& zg}{#MvzLf=o>|+I*@I2(FE=ZS;|N;VKvPe+lv<9L)xi*%ETrL8ScP0>p&?UoQowOl zG|np$yqb3r&|q0w_?E0LC>5}nm_s;?$_$I*qb4krTKA|8*?RSyTEN!Gw>5#c5s50qV$XSN!aw?RaR?D&E+HyXQ^es?a<_Zz+|?x5jw zOXE6Y6ERBG$1&`@t{~ldUg(jU`o!D+0Avy0RtcK+&R~vO$egRo9iZoAu(q(YG3P|x zT#bo`q(K%4S0Pb@&_t#OZth+sLBdBl2qIc5b9n%B4UiT2S3Rw%#QF2g{7Wfupcj_P z#49safs(_z{{U#KgwF_~!AgjggST}f{ejx)JlDDYkj(TRMIU3ASpONeVvXG-! z29jmEv2@PJ$~o-zrpWAeq~VVcR1C3Hah+VepCAi<2~60LAZOksn{!AE?CE?(fgO*) zO>4qeh&Y2+Q{rHo*9I6P@eHC9fC9mx21k?@rxk)X6^(9OqYmlp-RgRJGg8Eae4=@y|xx+z!bCoYS_cvy) zSjNz{6d%l}hzs_$eHYYZFGujL%uebY4@2~UF%E~$Z&}<|7;2@vjm9*_*iAjzJi&JavW91;;&0GJ z+>}p-UGJHy)0rZXXGCRdmqrBjORL4WZkrviXzLX-kNW^wK2=b5bMUWcrX|RjHqY2* zQ_BEVZ$?mOV-VGAX(1^I%K<&&e7Lw3Y3NL~U?&>^3@Zb8m7`3CPXjD5Y4L-ux8hQd z-Q|&<2!E+1i;*Y=R|w5yRB^j!#{xsu@TV&P>8TL0K9fjAi2{C5u(tWk!y75;iM@gN zfW`Z)emt$OxTIGzwOayip+6;<@UA`qmzZg^0cqV_$&w7SnHjG%?+Yc6YUaFA?nDorm^md|=c#9L_H`9}Iot zIvJ3C13@@9{s$P;cBPtrTn3fUt{db6Fvo0F>7@Sr&2=o{<#i?#mZDJ)N#$tju zkLDn9s?UL_;|#`y_Hm~fVvtfh&Y@*22(nxtPbxPpnDhp7v`UABb}2nr)8wc%b&-oO zS{Fb15mw(!6)Ujr2zOCh`s=oC9-1B-Nr$mN#k7kJt<2V}EGy&rA!&LrJ_u;{Vzb=z zRmi>`qnR8O;MjB2A<(7)T8Jy72rHG3qV^>nrPwU1m8)PlfZV=^H*33>h|{LuARg-N zDAWchjq}xEW2VaST+G(leI@m3w!{OTMmC&!LN&nDjjF~^=3RNVUox~FXju&hmAOpi z)UAX9jAS7b7<`hm9p|WZQ-~kB)HDLUYJjTJ?kdn?pzP)xCf8gmLsn`y6O&_+HdJ3M zu<96110EgCRLVwJ5?uqclW9V&o3v2po4Hwt?cjqvRI05B&ds;OG8fTDRXK`r4WSEw z^9sV~J(e_P;TAHKu~z;kSAsMT@{~OlV&o05g&9eK1mFZWRsnL?^75;M&t;j+2pmD= z$4T1BQt*5cCOpVJW-9NvkVe>)a`#5f{ED_(5WlpnLdHR{hDy4qfUY5$dVj+m@A6T; z3wt0Ihf5}E^sCZl3HuN-4EpgHR;)ZgT(z{{^-iQ0SK6leEi;#9CUitH#L=>HFur~) z3Ql7(F0`w-;&B8?)F2c~fTralRYG4N=u}f^B-$1)LooqrgIjI#4vr&O5|Y$#+t6>8 zbt{WFGm`5xHbjTSvfB$}0i0@xYU?lLf@$Q&o4OyCH4gR?!I3q@$S!bAvEy}MavS1_ zS0hL_0ei~vFB-Sx>)aBgC}dbK%&}|Pd3}Z-sqVybLEwnYl9l0HT4o`I63K6}QpK@e z50)ffAPGn2H%j(`-LO(s#sn}{&eLs-Vh0Glon~YG1u5d6|rahqLQ!O`c*bdR6$G!mkzyMm|lTx+t!3JHmqgw`? z$J(N;)KQnrZK7PBOcm}~ZUT?Au9quzkimCJ5BL0t0s)~caZV~`S%t?^_@6NgFe+q+ zJyjkBjms(T0^SEr{E+}!m~l@drY_2#d7yj4iLJW#6bMv>%2dNlEVV~gJMc`2GR54m zNA~ua*AAw36JWx&YG7pt!&o&1Y3B!#>_Lcph^kmy_ruv=NRy#tN73^JO60C7bb0qO z9d)dSzbcjTUJQBr#}kjeU6}`_CEU{$s{l^uzUeaoO)zCRn2ex=b!7EaZT|oSw?u_^ zVhz-_(bl&G7=))vyJLvLf?4~1MJ?^l5w!*Ig3hbt;<_uT+aJU1xvx>zNz7{vK($Ij znN(Mbcm2ztEnYybJ0g|0ybTB2#X@Okp#(!`m@K?H=ZR=yjm0J}?6hCZKveAw5uRXF z%`F7|KA{1S>kZ2I&V#IhIg$@h#?ar5Jpxsd*CJ&6^XEgIg)woN`A@BmVd! z?|^9k0N4r->TM!{w<(PidV>nC3#~p5PgDiCe_5&9}clGLWlA(yEnXm~BQL zxs1;vQJ^VktjZt6?F*3=?v@MWU0lSS;sO;&xh^iE@Nk|&Rz)+H+IWZxdc*^w$&wC2^pb*r!QIea;9-O3`I<&iy$gO) z%KZ0D?+zg{)R^D3GXSA|7_ev5X7EMfQ)Jv+JCDpxP!BK1b5X7yB*n?W46vM0^i7t) zDyK}h5{4V9neXA8yhkqu4BK9UH6{f%lIeEOqXog2;X+J4UDcqA1qiU*J6%xNDgZ@l zrmE{|xHJyqU2+|?Rg=uMtGpRws6nYm!G|l1ac65e`o@Gv4sknegAjeRJk)YjHlIr|UR4b2w zxG}XXi$?=-7I-|3H9|3dfkasp4OUT*1m7C8u~nL8F7J^l`{d(jF)(t!=^oNQ9oSz# zf?tS?ev>A1atB-)n+vjgexs90Mfk*^Mym&?mb#Ql$CxP74Y`23v$6Mc6NgB{LfB-L zlSrDp+roYZc^q%zXVF>YcbNs#h0RUl1$wI8$0>pFTFrY#@~C|{=2xuDK*~pSWN~Y| zW?11V^c9h7p*??3W(^Hg3zkP%W2B_Ui$}G6%%-cR-<{d|u#y+^DK#P{M47TEY3^J+<6Sj;YB}ku!m{Uxdu(BC z74UIVo4FW(G42{u!(YNOpdY&x2k3{EWt9hA3-cYSA<24*!i@fo3Ao51rWOF)roTf1 z=0_K>#t0fIEY(L*b}I>HMe__!fh=@CdH93kg-d+uJxbuQ_+^R@A<1j3*U-aU_I|{I zdwvMl`KN6ZTdxy;p_E6MT*nmqEHSL>nN0rxlu?4;P%;{J5tziW&k)V|jW!jfnm5Gq zN?0q~c7%x18 zW*a=vSWcc(Ckg67$mQ+Kpr>))oV3EI08;ax>JFD%g6}-Klu;LDX>7li#% zt`Pf|uBAhX(17tf3u-D2hP=`;@v1^}G(5n4yP4%q>u!*PB|kV>OHxpVxX%Yj)v=q7 zFKA%(DM(w1VGbOTS!ld@foTWSS^0&hx~RGlF)~Mmo7FbU0qP`MO0_YWhpe{ZKIU0E zsuE5r5%N;8?pBMf_b@pv&(4}9#a`6M*8Wn~RD3#JG-jUWpeXirUZrX?c58G94`8yx z8-S6?8!^Jsgks)RMmae_inNXt^i+@MU>kbvh7;eqDY>2HQs zS4anezveQGuWz(&r;{t-ng_|sl%8-t?m%;1im7C#uQwo*4w&Cf@wXnT8<`=4{cxC< zdV)o?9#f+FGO6-1IuoF-ZvaCC$mr2EnhN|vqpr7?S(eC(ZB!O=%e7Fr$%D8$m*y4X z16+KFj(MocBiJL^qUV~4w0*X{h&6u_xqgT!5{CNN%#8=DX2yIXdKn9Ai1L!EjUku| z=na##Blx{N%tKlm3c|5&%%GN+i+Xi1#tHt(l5bRFcW|eOqk;U!bffJ76mYWH7`LC0 zaE)v69WZkZEQ`#Z7(C^+FeQgE3?)$H$Xd%9QoFjA!a6c2YVU(SE>Vz|gR1RkTlBmaXNlF|7X&o}VfT&Z$0gpKLOK%yQW(F5}7XJW1m|W?7k z&85_=#a|eAFIviWadE^1OjTNZRBg(9 z)nbsrfvc&joN_kzBM;Eoh5#d^@cwQ_rU+|JYzlRl`m3jsWY-k3xCm;f_zCg^S1z^HY)3c{woGX-OCL)gd{W~e40yl4oW?p4yl z6kL{X@o2?n5B_jWt+E%`I22}IL_oqiH`Jw2+$OzPKs`E?6)dOD6yL-qZR2K&!{8i2 zdK{8)!(tV(fm$-_-e7LWh>&wzfho;0w0=;<(58Ic!NnIno7i7%kah^ zYyk{&4@$V8Mpxj>v(XPIjT%-&g?wEi3ZY5BLh#we>KiR`nB@#jn>71^eA$Qrse{{$ zMu_(IACmAAoI?mI>P5>z4jH!q$8qs#T2I+1yS$K3D1dr~wziwM?jLJYbTjt>QCgm7 z-QrVxnC7L&Q)I1d5g(^bB~aXellAY$*SEiG1nalYH4j_65TZU%BmoyyZHw( z(abJ0h46y`HOo%`gKFh5gx+-3khhVvJ2X7v&v%X_mS8` zG&o>JiO#uTZU~Vrv0hBf;Y&%&Q)lX46j)K2Gu=#O>3z2lr=;K!z#C1VuE~PB>^<#I zSh)?OZIaL~{{V2g1n0Ma{^fz9*uy-&r4Vy&7Zs?-@kj!`6$~DJ8Orr2B0EzI5Qj4| z$4iB*U)>ARnc$&Xz|T_m_xCv1Y+&3=D%xu>F#&2iGM0^KZqW4yqx=jNS|Dg)nWfhk zEw#nEtxQe-0MP#c<08LTsn#^Yw!fHM0|pWOmZ6v6j>?F~@Qn&(brw73BYIzU&#u=>LKM~?lMlq}v#wDu25AcB~ zk$mYe=r{Z1tfi|sjLog?FcD>6`jqRjzW9Bk!iU9ptR-*` zahD<$e6jxL2_hG8+Tps>!v6qp(JZgMQIPIb{$f{2(uKXSyDtLET9u{nmWGMHQB_n0 zkTr_msZ9tPB^HYa)LiVIRC37G#;2Cc;A&9jGYcMHWbTN@_Z&u-8Nw37=hqNZajU4J z+~|7Z3sk9Ux^)c;2Gl&l@pTAY<0e?I=IUH&D)C-mDcIbeoN+Gey~SMbaiM_&NTlc{LFwUI;{q5Ne%=v(#|FSjkIo1DJ`uQnx9Y0_W~312XSwnc&P4vF9Xe)6`-f zLOisT^5Sd71w06>%$pI(v*OM}aMr zFNBt=Y3ft?kC~O!L?LRak|F?BS)7=Zr?}MnPpz~0zy)RjOU5axX@Ohlgu#Dcmr04j4M9(Ss|`5mMlg zfk+akV=B{34rLJC)y7zSt?(uxeCdycK7Qjn_-5RxYT!uuz(F zxO70yOF-PWgD?zdWCU&%mnn&73D-l4?o?fC8*bs>K(gF7cQ!0#b_?S*xJA+f6#fmD z@g6w$iY=|3LEqhzO1BR-<3$F`aOasqIu~|YEOtv?hytk1`MH#P6eTHI<>uu`chbrOt}fzJO;}$Leqya5s_sAahTO~E zD&`#ZE(Dv(9(Ba3mR`&nBE>iaHDlc?iBWV8s zI4rF7DN^ndFu0^dNDZN`qf-n~ReW$XS9xohc;g!J36qz@p!}*Zte9NY7}G>MN12?8 z(MI5n06iO2C!Id9;G@685*;yY#cAw98HuQMM)B;Xx|yE+ML4m^QEaeHb(R5m0O2Q1JXE1E-IhJ6gi8`2dUfXm!&geK zQ{;@x)IYlInby%QjssuuGvc^$y z;g@4T5Zf-r^ux>U@iHRIsa6k!RMkrZk|7tCSgb2XX$L4xof55Yx$aoajj%AUEs-NJ z4Woggydc9dU&@VS1}>jtM{8=nYI3JTY*%pHd*+pe0v9*L>!4GN77fcc-RZ{FY+Ur@ zjR|>o3dwtEfb-lB!w3v7>RWMv+*fuIm#EnvqY}YRlA_`{4ft+TTS=t^TKJWaK}nY! zBHSVasqYgdZ6gu1@2~_i#AN z#D@=2mgW0?A(!E(N~N6X4=r2Q{UUCx6a2;oecwSa7|;}RQ7(r+q8d)|DcCXgo(mY{ zfekY$af@-TU=K5qe0n7LF9T0N?@-E1RV`io4N9UYob-IJ2MxmFBe%@lrg1nbSmBd7 zvSKy0#vUg4W@9cUab3{DAW8vgTZi~uV2RXT>vpydT>uxL_sl%Yc8j7aPvy~p)r&Z% z4>4kb`+ndYi@hZ}L33?D(A$u__>@H#o=N7DW@Vc(=97Sn$*5>gd*={WsQ^o7t9?BUaUWA@hp8eK>4Q*moB;5 znE}92y-K$O=8aUs*NJ4t$S#;u7V7x|9j zldCMGM(@%GQU_#3Fi*%?hb1#()CP(e{d9txMKC7|_2G?M43pY9+M3 z(USqn3}el-HnqVLwY@_Ha`GNUL;|o!h1mAte;4%~o(za>wkS)P*h7;#c2VT5{6rUH z+(MVLBsTa0DPlhOC%2f)NKl~gLMe(?^eD^WP3jOLk!s!tmIE;o zSwO;knCUsAyt`9O2Ktu{#HV`l43fZU8mpX!yCq6y%ej}a>af332t$jmFD`~X#?GuboV^ZMOu}y7*0!wtu$x6Tu}jQd6ZHw!3>P% zVbzoR$DF4aNncy6V}wS#QHZ)lrBWESR%WuH8Oertlw^TW8qfzy=ctx9##3iT$(@r5 zsi#9tK~7|Y+L-~Uib|pQ?Y%EiGBh7cX+qracwTET#d_=778nWq@3H|58 z4l5CP$CE(uS$UT4nlgYFe7l%o=I&YNcY<>)cCBvrT|uOz(Sl=lfM!gE*AO}z`Gh|s z1UW}Nrz6@`9a74O?bTd2x=xc}MFeom>JSqe0^HPL#^FOAVKq^?lyQz+-NJ$4GkXSO zPNRQ)16W~Vz_~>Sm_dB3B*b2j;M|Ttg#!2(!sIqjeo~EyilSW1MvY`87HG+|N zBo4~Emk=P8!h{&oP`5JSSTs4F!jb}zGlz7$4&c^FXOP4fSk0esV1X}Lmcx^RI1Jpp zoiJ!{Q=0*OCgm-^vu&d}CCAHrOLFd5)*qdx#0x86RQIt2b%;A`5mS{#W}%h(aGaiB zHd*7`RhLa7`ZmUl?CM_|KQh8X=@Rh;8Kn{RgT#!9fR{5qdrQdbH-=5j1YOw2F=q@) zaaQI=20||wB~uKq4P1y{fR&|UILf!gOD-#N?pm7L#ji8YP(^If_Z!a7&S6Xf?bNUl zP)!r!tPY{DnBmWDFr61_B?XTXlcp+@VhlW(kebBWwC~g`2bapDp5~+2$N=Ivr%@s^ zpqOUZSa30kX{#EFd^Hg=kvO5hhq!v~_S-C_@5HWhRA&L{p5TG7i(J5T^)I>-!cvEC z!Eow5wo!@D*pg2LjK+j!eunfNTD%KFgH@4lprAx zGzJdzf~@21h@k37U=4)j3spXVooVSYOWU$F>E(BX33a=4XOE8lS{ec`v8q6T-P$&j&Dy7E^*t=4$ zDh#-`wqEivIdmb4=Hb? zHq>yqk~0cM$7rU20c=2HP=hmEu??W2wwq$Cy$!SUw*o2P`$;cHZ(CG6XWf-%yGH zb4>B~SXuY_%Zs^CQrLZFZ1Y#a^2T?~+S#WTFhI8<<{h)Vy-MCfU}rBLd`DLV!Dg>( zXEztXe4&UW3s$9g{)s?}hF(px8W{{+6FKgtAX>7ksDlv~d zUPy{!D8RdsBA^~foEtQ~q~1}jTC{Q&_=8o44K%wo%+%M!qG$=XBIo4^UfmRTGiFq6 zVyJY&uwg>#8O}e_HMHBE%WCDsP3{g@7BrI5{KI@~?7pKDu%4Q~;w7t9g;%)It!sw_ zqzVTP*h`aR{H3)$$71eP-I9-8N{Gc~^AqXB3(Z8B&St)4weXL^d1&&cY0SAibFr)5 zeI@ojs<Qf@<1C%jr$4#>`P)SIt z&L~+hC4PG#ETY;sWk0C8{l2 z%(RxmhA0~}h->P&Za{gp&9IC{`UB`rWOpjK| z=&J^Y5hF#KJB76jZq(Usaoi{n`76a#}n{UXxxV0garmxgLslv}mj z#f>wG&LF$Rugz0v9^mM0-V{S9sf`z(ECH6<12DL5%F2to%}d{Fc)N!gSJrO2H}F0En3K|hvEd>z((ygh%YFUt9`;AXQ&!M?dgEO#JSoH zp`N1TKsm$;DbLqh;%sc`%`M&U0{nPwX2G9s9(msOXIS6363 zY0S>Q;X7!VplvdBFRcwvU*_Q_MitR)Th42g{{Rw+*9p`H2*Iia@=wKgBUjvJ0CQk8 zbzrisH1)Jp3p**kaDjQu6gUVfc+6fn4$68RmP=|Q6Qx^V&+*iB4mm_TJbZw zj9Ds{gnpb7U`Jg?{GQJ3G5%IylwJ_e^ zCq^&K%0|~E)-y6MjN@IjdOC${vrikRv7l~<=zF|_%k;}m^h)Di|S+FDawmhlsSp) zX9^nx^s0+AQn`M9b5KSYTTsJPVmUp}ebkATfotxxHHH=wQ#X{cTK3p6m_Iv~iPZ!1 z1e1q+6PCl$ccvkIJJ=}t=I*8d7Zk{`xXvtQwF(j*v2Ql5VZE!nFxM8i+t{kLkkd0J z!1B%?QdGL*Ze&IoLv@KfSS1#A?ILQ7)R=~FK?za-19AT3L zpi=dQF4_aoLLZu@l2LJHjodzUnA)dMP(cOQB`T{t;)!fncS`w*DNTpmbuA3dIQ1ca zFq<~5x58TxcOkLFH+l5%MSr4K!E?d*lt%C2t!J^6{pV1`HaNuZOiq7 zpm3GOAJ=q1kfx#>D$VffUyHeo^60K6gMJvoQZdfl0o1k=oMhnki$kKW<@bZbn2#9H z{{SEbn#3Ir^NvOvR(o9a-jUv52i7Ncc^wVKk0>mb27DPlk;Zh>{)H>tG)B|$n#saJ zutA10>w2l6yu~b%PCiIlcLuIC3@dW7UO9{%EM;}w{iZo_3*BJm zt%`_lSa(l}U<(JfZoFb$IoWXC`haokEdVTTDw)c{#s2_a4nH*;@Pe6fgj_Ea(INJc z)k9m15CX>2>M5-pO!byA$c6zqwf#z4%UAN5N*EYOvfHxMUSZaNd`s7iJ>qJF|tDOxpPYuO>c>7V^b|$|&mXlGqCSv(MHl5q1uGg`GXh78hCY7duE7RNY54@TyjT z8gfG-EZ|Y4LCwzYBCP-*uy&dym@TX?xU!7X0eiAsP0Y|hH|euJX2dC}(=HE*w}H^K zxQUWsuD-AaID;EdYYlpIEsT6y7y&F@ApT$)%{PA<^)0Y(dL71^17j2oe8k>4c=CXj z9761zSX8QxJEw`8)J=)ZvLLaj9!>`&;LK9j(Ji6-1l3-@XxCLcmJ8uj^#-9ueR7zk z8)IuW`8Z#nB3g<9m>LF3Fic~2NKC<|>heWZhfD48Et0dasf_Of>~6;m!7X5JPD;lI z3WFn_v^OM9!6F?g;p0Z18I+$XwZcgm%tIO<4_4g zoC3u7jpmkL*-#nA7~5_1rmUw(j~z?(^f4Z}p9y%*X92$s9QQJzgatCZp5>*l$^QVX zGQf}UxIrF(!>~HUx#pE^nc?1WbJnuY@`%{nJi?hvHXKPx+IWr%BO^7M`H2=-63CY% zeKQmTmcv)h`@#zPY}rcecGF(y)#84Dzw1t@Urg^x1!iG8i2 z2s+qqeq})i+jFfJYvD8o@0XZGNn7p%AH=jUT{h+5cN#GEIzEbSiPIjR)u4!-{KP@> z1hn(CmLI(?6*#k`maYLtG_KTct?ie>I0XLyGS|ettRa%YFYreGn57D)r$FLbqHC>J zqm8#*#v_s8ZT8m?XHJeePF?A@Qx9E8Nr#hqDhrrw%rExwF=Z>{{$dV$J1 zXrGx%5x}SJ4$y9q{lR@3{{V3+(Ek8=mRuBDXBfC7Q+8W6NEtiq}n26=HLIA4OhgB4~2vF{X4}HP9b?#Wxwc=#Pgn7>8sYyP3 zD*CaTqC_`nYpE{!o>iGW5|dR_Ozq`W0VHj}`wFvAtibD2<3e0i8y zuZYM%&2;J;LD2_Xofay z`I%~OD#}z|U_yP^TVWkHBJRGYaT3jR~XbsnggrYmuO)~~qS=jN4 zj2A#)Du*I@1fYm~SLOh@v^fx^Y(M(*GIWOp1I96O69_MAjBz2cl( z3iX~E>fsQ5Y~8yTJBtMe2fO${lqC^;oPpl z1#+cgreQ|m(FTIuGaNMQl_axLZ4ohT!f)c}8J|pb%I#vcST)|O?)ysv{G1+lA%_nm0w|FIF0FX6JE=!nM zaWJ-p-CyDk_^sx=VOhCa^y@o`HtsZ54cY?|0uTW>5N!ghbVDuFdR$t9Zzqyi zRk~h&<8y&uiPUtf%9h~9J$Afyuy_up9K>>ma-E;K`U#}W2L|C+@S^HL=@U05>XG5} zGRuR}4;hV13_%fvtH?j*VZwU;$=MB`?SNV4f8GlJ055YYMB(4exQeDf_JD8+OvdY3 zpA0Q!8c{IpBQT*QG=reBakt`TK9JPf1jYG1yaX=n`G}3{q$+@8=4ar`+6oxTYLZ|i zHt`%jc#1OYNIwaKiBnxRt`8)~P;cl41~qWf7s5-J@QH~-5B?DdK;tvP1s4hp@Bd7&K{M4aP09LI4o3v;a=#Ub@SHMFs9HChv{GAa)5a&EQq?4gCWJ}7miNqJ7t@a_ z-NOuX2t5j@8l6C8Rg96RM90%DS3brIOa(zPOY=lR?jlCVVnfo}B46_^1y0VtsfSS1 zUx@uG7_I?$OTxF!tO|LDZcX7_8qDL|)qfBdbOd9%*`z4s@`No?VvF#mz1q7+Ha50|fgzZpgTb za1PBc#I9zOvALx>oTU`#3+XY6Fwel0WI2PRO;4B!M)tRg1{cdKysl&g_Zpc4fJR0r ze_l~`>OG}C9m9K!B4r9yuWz|Xr2ha9Gdi{IRJY7=G7(*C<2NmxU^0dK9^ulhjozKq z&4P`|6D!~*Eh3D|pcK?s-@+~k_9FQ*gw^%)WAMgPxZ*`l z8`&!Bc_9SZ;*)<(Y+c!B54ijZnPgI%JhLD$DQlz5L>DqWNH(qwO2X(z?Uap*%%^K- z%ZZ!anl8zIiF-?qkZ)YHLOnBuXcE-R3(uj57J>I3$l*%b7u-jD0*+K_SN7B_6f^O1 z=up-^nb{%}P_i>y3KXujh>npgjA+Lx$Bs(#$q;hVg6IZ0l|q!!n@!pVk64fKG%>&9 zQzpbKjh#f9PrCpb}*rAidxT<~1=oga+ihy2uniCkl;}Mde z(iOv#a^}4pOv6yFIe%$)1Rj7xnP_e;FX92sorRelKBL_TYNFic%|q}}Gk_`Nxadq$ zSwOvl=T4$i^($G zh^S~O3`)6Pzn3e@DHz7abK0Pm=&5m&PU*qKUKtU37@Z+ipEpm^3gnsWqoWq@0MNkM zE20|Sn_QNzS+6r7src7iIuhkI%uTVt#7x7qjBK^3pZr^MbvE54fgMX@cEDfE9>){? zSzJ`*Z*%ILMU$omt5@O_E$Uq}pwzi^x3??8wjQHo+mg%9O`={lbykwLvfias49gue zm8ng3=UT4TX)`g5Ru(>^IJ?IcrhCIfEVxoFX=;$4l^ko^oQjxeS@igrAOdFdHcc|w znCYZmn}%UTsJH=UU4LnM8Cm!!Rj|h$LPNXn26>plV4vL^*DX0ex-nywiU>L>{elaM zeHRO8(K(CgMWjkB7r9L9F&Rp6HzDX_F`lgy>@&2&g6j3sv^T#HlgcW=MeG_CUV;qpK{vA z139T#v|N{>;G`6}q(mUL#*lFfR+B8i>0(|%&PyD17~NTy^5#~EE!14_ChKnSzG6@w zLk;7hs%E=L&{;qROxMhEc=#gGaq4CSJ>bQi&d=jb(m^fY#3&vEi0p}1JB1Kv0(*Hc za_u%$5i-lDX@QIUK~p>kIyhzl;@Y5Hn6T;u{{V;sU5Iwtg3h+^dX9S*>g>0OYn=H3 zx|Fp|?B585l}**6y`TColaUueDg=B%2irj4mB=)u#>DkUd;!XA4dqh)Qq`N6|U*%R5tj_B(rJ)gYQL!6>Md@fuXo zR{Z*wFsym%)MJ^ip=X)7SunD_1Ixkw@nC|EJM$|=sej6){{W>n5@jV;L5m?sGJHT3&rSu#_59Wy zzcSke-aG17o+Qo8gv%RHzX^N)0GN`Oi>_$-D&<%u8g?SXJ9`z|_P}u~1es~r$mm!6 z%LOsTM(*PH#j(Z~;IoELC1}LEM-K6aS6EWNQs-XCr`k?9asL2>q_r7D{gVQ?M1U2j zdHi~uPAz4Wf4ovv5nIIu&Ui>BES!>5bnMlW%D0gg|_!aXp+r|vQd zc@G~f14@zvTXLVwKXVIX4h}{bR>_C-5DRkK{vR(WGL;s_IvYGiBaO5W?&pOlM8w!w zv3#FOt2V?dTNeT{D#?j+(9Aajjid24*(&R)tp6d|VxB zL*f;~Y?0)et4CY_(`Ay4jAKk;G^*){Rny5FlzeV7WLr3mCdw^eOeGjyky+*z3ZlYt zPK<3S{{ZF(h?hiduq@#jGox}=&7n>i&S91iNQQ;Eys|NgU{?sBrudw)Q`sIp;yBB! zf;OfP%&Jq{4{qE#V1V{=Jl$k^tLVXZ?lx{x@O3M^8IqW*;d*hAHL@^=?i{A6UB>qc z*s0jyZp}EnOX3(9qrY_@TG3v{f#HLl(iO7fj7)b^_qADdH6Mt$gXGJ7N{4{!k4HIy zCr;IxDPPsfc!fm>ptZ{8zfIo+PJE2ka(i)2ExL*t0wJXfe&X(GGI$?+LSI?nat{C@ zV+ICVy7Mn$^?rLL@?s}N>G;D_4(0Ug>n%6^lHxaF@>~`+ToT>?aYxXS?EisEpsm2+TdMpMi@<_?4E7AiTdxF{Et!b$%CHmY@R?i1*y;bEUF z(DAepywfhFig!7#iER9a!mS=N7ILe92TRG~4e2cioHI?q++1BEX>%8YClO?N)^;vb zzNOT1>THE=tFc;d1Xa>7z!}ebiGM6ZzQA`5c6YQX%P7H*FBwX*^X^%`C0_$ABQPTw zQ-gr#C|f%Nc|#3M&8jL_N2_iGsm%5o&0-u>-GmkAfhDqvY~%}e$#V)QQZ@Gk9041|$Exb^y0w^6?dJsU zj8vuZaWVAHgaREy5z-8ArZ$}j`VaI+>Ncv#5`=n<%mq_^YHg9XGf#R%Rug!n#FD<4 zP|S7lXK}hw5r-=WSE*jWGV5l<62rHcq5pO$!yqdu(zY4ZIVrQGO z*vK!V{UT7LGD&D!vB=HG<~?2kxgsq1*iXqXK!}qGd}6YA3XLnrL>6=gkp6S`1t^vrJ5dzN{81v2;! zNRZTDiexJJWttv}DDQwOD=izeIFUM+Pm{A}p`8#bn#k@w1~a*UWHexe^KpDP>i+-- zd;66b&{WZct^7mY4;kTp=AzJ^nmVXevzZ<*mWB5yVCONatrf%OTFUGS#0#sV#0)~3 zEWBF|*>cz7Rk+ol{KUXWtWFDCXdp%2%+o@gFlI-0Z;bF9MPvvJN&{{2h`TQQl&Wyh zL)QxU0!#qa*)tmq{2nWJ5%qm=LihxC+@*P($1Gy9#NMGRW9WcoR?d&cggIj4xnSHc zFy}gyL0)@Nmb6y9M*NDyLgMNkAbXJzxajdOq$^8cRqhk`-8a!S-%(i4+d=5HV>_y@ zmwOoTH!E6-yd6Z+6g0hHw$GwojPZc+wWakJYVK)U1dv8`iVVu^U<6D?lL08&r zwBWE{UCrkFuNP9~U=?m*C~ose*omO?8Vh(VBVGl&mI{BVP;*&9vaRIp8#vD2v&=bn zl}2;GEN3@yaVbdNt{GX=E#X6wp;(o?#^r}PI4uRTvaAJs8cPmr&gqW# zkZFrt2Bpk77==LNdVLyf6AOUXML zosCdBJj&L(?+*rqFE_cKiKwa$OX*F-Kqh*4Sp3;aAS0gUgPE>?;hY(C zA(J$4MQZUbjCp}>i8|UgbqW$}RNAqa1I#&k1Ae9&POiYSc_NB`_%94M9|Xflzr<~I z1&i02Sm$K3M~WS*2-v~v2n06=!7#<%kj*(Cn4DUULj-%23dew}6=|2a>UOwz+FHcV zM(EP+1;n+TT@ES6rc7tm&Cb~GEZQ!vPbovmEj0WQZmL1rYqX|ps~+mn=Tkq}ZWF4; zV#@Mim>elA8B}?P=rUV2m~mw&8?}?Ec$@rC5?k6^#cs^PA>WL{JS=6!STt^&!V`;~kkp^|ME^!xq z`cZS+*5|WK3`>N>u)qYX-JnHF5k@AGis!g5R1AAT`$y!fmng2muHt2m+*moR&4^j$ z9=oVT8{2VZ4r!U+QZ!h6m3T8KZLKUTxy`{M8g&;-y6xPk$JTKKYq-^?a5ObaW2JF@ zEPc`_zdBZ6NL9=z<+#}<<%=xwQjC}Ft1{x!!2bZ!7br%#=emj?D$k0K^aYBv zN?_)V#b7Ybvk>T9R)B4je8C8#@+!ZTFp%VP0+<@DN(9>jKzxxIHBc81Wm}1ctTL5N zEULIUqmHp*zVJYS5&Oh}SB5q^JI00h?plzxGW#{Eq$LhrRxS$8Al?E6IRe161?|lt zHy~TNy(LIHHL|z`aMjt)O#6)L7>_c7)Fxm-NwZ{XiwsFai8x+yH{Iw-OE!}fUcfbV z@u_M_xP==i+T6sQI8%S31l4&>pyjO*qhsBDOXY(>`g)}YP}4ZthBKRnuL@_P=x=FQ z1JK+HZm=u}bwdk#vMdA3Yxxq?BN9^sp}?Z$oaPK~JP1Z8((p;%uzsN+fIb);x`CKo zjQH(qh`$Lol^{B}Rsye~9&EM2F|5a9&RbJ#arSem+lkqI_xmuS;io@x6GOV8h zw76qPgBM|=++G8tCT(6+nLyY;$mFDk7lkjm4rs=F!8L&Qk5x8Xq*OM3SRyw?G(wVQt7S;=KkAYg z`9yf?C)v1)P&dlO+VEN!+WL8m9Z$}IfmhDuyl16izvg1TH!8MHI@1Z#C=%C8;!ype zrxYdcnU(a)Kq$^zxIfv9DJ#X*pECsuQ?hecprF&#EeG{FS-ztaH8t{$TblC#fg#KF110LO$qlkZ@%M7kza*;n zqA9^8ml!Kf;*br7L15~6N5#((M=Olk8Wx}m3?z3C%T%d77bZ^;YsQzx)KzL}t7mak~9ykVU1_b)rB z2j;}1mI^pkN*0e9onaF*d`dMP z5#tKVz9qsAc3Z+`>}O>H!OnA2JkG$xV4jE+_SrS%=2~6vs?0Lz`QW?62W0KiLi^18o zt8*EAyQ_U$#G$L)Ty&cKAu8QU8FsKPj8s71w)M!!wjE67ggqt7t&*y{xI(`->{r;^ z8Q>RN5G`=TECU870lYv+Mp4WSOLR=)rY>y5VL&?frSTlb*xRQ8%Kw${nb!1hs`Gv6@?c6;4(VL%M1+vLS{03}=k zWos{}rB!knS48zRd;nT|$57@n>{(NR6P*`jvIrg*BSTlz!~kwO*VGYY4-QGMm@@Ad zWov#XqtZwRvlFY`uN3Ebh18%^=?-(!k?1LvL2aool2mF0?ffmubtl2gJerF$$2MU; za~S}q-Kr44>S%72^(*v=4azaW&GiW@xQDsPGb>y(ZxI@`lY?wa0Cgwi`9t#T@BM|W zT6Yslu)U=wDgOX?BFq^}o{mQ>ZZ5nKW!W=hf?k?1xQKOmh7`+ix8{-3nez$bMP?LN zaLr1we!;VKiPRFD(InWyswLS~@G@<(GUeq!s-9w@o@h4;L3stS;xTh)O>ZEQPL!WE$v3gCW3)JLe=zP56y%Q5-0+>8OT4 z0s{Ra$Miq~@3xwhoRqRkY2xCfY5L=+#}j-QYkNXa$hxbx)#aQ(8CJ3i1O?wW9>5qB z#Hxckiy3%t&G;Pk6SeVNO)%79)CgmRqjbzCrqBWnH*Y5d47o6>3Bg6gV-uPwYNv5b zD;qrD6&DU!){AjQ<|PJX+!a#TFoO$O>sV91GLa6=z!*KTnwm%Yq0{vsZ)?+69t0xUohX^)Xp*r^>M>t zkQD0O!>UIS3h9Zsb5K#Z*&UAHB7ABAo;w9q^d%4`d~99#mHFPuv@jV ztx8u2{F|9m6AfjPL4;of2QaJ!&nth4-GsK*?>NGFjIdO z&mI&AgNS*z`GS-z5ev2WDlQfwJVvT3Z8mg)jN#5Dq%G`#C2SCz&l8pIkSFjybs$bo z4M(Uum;eL`@ca!=QoI6P+o&i z#8Jd`HHp-mMV+s0VejHy+#{-BxQrkTzQqW2iF?8HEqwNr0+?(naAC~3UqUG3W_8VI&zi;+#eLt8GYt8YmIF5?u-eE1PT;#L)nbjZ7 z0vMlwD9>a{!DlKdtvc#ogTAHyxGHjaMct#RWD}%^#7YEKjK0>$GiLn~F9{K3s2aQ{?Xhcz z#O>zAFrqH*k(J)0;qhLhe@lw1aLdC)YhSRlUY!iSSRc8Td`Q1M?0A~h2!R>tXo1IH7q%O-=47X$f$q)2Q<|Jw|^SH9Lsc&8cTx#{1OoVRvwPrI1{+LYg3@&1MX0KWTWzUh0s`S7!vs%kvbN*Oaug}lf(ke^^A$w5S0m@ z;>B7D+#C_K`m*wz_*j`<%3dZ;0j-t}$+!f$D>Rpgsc{&zsHOY3mIkh5rZ8kvFu3_g zwH?OavS&RU+@GnGJ6z+l8i>PFfNxw&cIMG-(_s)}vY1wk!DYdwSE#UB^R?U?+?|w* z3;VTWXT&MFNE<6%PD8H0(E*iOC*+om*e33UtW(V7M5ut6Yq#?=sV0RabOQF;fU+A7pP#;lwUpK2R$iKmMvXyrEKutLC86;PYP-yG zna(1igQF}LK;q`Zf_zw6keORGB^rsb(PrY|?5Wn{)#q4wXKhT1#I-rQrHDBCMC@m2bqL0ZXjh$GT$EyST%;Ik*MKn-0tNAaG|!2rX#VU!OhHw^sb&gMh*jY0z~A6(A=eD*|@Va zA$TRH%Q>?!6LcDN5QsLu5Mx~(8S>8u6{6>u6-_HLpCQ=XWdM+2{K~)r!nOR)ee;jz zWThfoPimeK{{{Xo_018C0%pjvuof_G@JkUpb zJVDtdiViu2L|Mq4p-#zzySSFNZlg+I$`z!n9|Ggk#883f!SgU6ZNeP*wGZMA7b)V2 zs_4LPvNS^mIbaAFB4a71vM1G$}=3AgJc?32>$@pIJ;R>t<1HGgN5<9w=pZH=3oM; z?E98Ob4$5HS8_PVh<}GLyaGS}+5ij#0RRF30{{R35b_8ntoc<$1AS8Rt!e2MU&#^z zAH@{N#*JSmbm$C=jNPE|q)22^c8&O9PiW0`;X`+QqFKs>0&1<1Io_ZZ1Q2QuV zQF+0~q$z30lBnYyK^Fze^paIIfY~$Vy^qp(DhPyc@*2?dfPUpc#xxLUS=F-p<~PMEp*(8%#VvG9agbU9v1}IK z(Q!4F&AOv@$)JX86);9+HD4cKgm~m0b7GeaxO6Z%J*~&MuRr(nqs`LnCHKIW;qQ4L z@~1=^Z-iiML|j4%;74wiCxYw~nBpY(`%3P4`8ny|&KddBBg2R}L4eAqgbq0T!VBejlr~+dwdFM-~>X{lToXV?R;+z1YgDlyp3*j=TkYt2tfcniN4ni zSl+JK%)DATxZFOc_)*wsZm-d?^!c&QJ7AfJe_L_a=FFs-q}V9d3i64)85mGLLX)7v zVeHXd%6mN+X>(tYzs+S`&UPh~q24Wk5y%;s1m7pf_v9`h-kvkEnY=Lj@%2Q3E{R;U zO%%^eCt>9*(gvb-4uDSI)2$t-y{Tu*Hj6S!8H4?wt?s{DRBo{Ms^RvbX~SQ2Atow+ zzQPmp_?y9Ho1z26!vury0lTJ*bvFK1tf&`f!cU0*0A9AOHGD3?cIkxa6?)(3uCLl7 za10{0kV-NEOiOgnZ$D8l_y#31D?~&00@n2DdbbP$r#IImaH^}HzcuoyOMNWCX9sLe z1KZlkRe=&@=>6$7{(WSV>nsQHEa<$U9(DL(IXXV@iQTx`V^ita-B&=(0)nGoFNN)AL_U0=``?1Z+3cDUo1QlQJe2q0@{{YJ3 zihT2EDpC^f*E30=J}8H{s+OKtpO*0^c>e%m$m!3`+9_V{PvnY`ogXtpl%xWy;9`{i z@4WyT72I>H&D#CK@NNgRx3PG3Iu@=3t{05^wAb zs~PNK5A}~mF=-zM@9}vR!qOVy_#ol-pCJ)GL@GL|rn3s=1W?A7m~Ys5^Hr$4ehyqm z&pXKUC=Tr9`iIGXy8Nz9SU_W>a_vD8>6WtCp+83(D%vNfLj5uDnk?9r5`X11!p+c$ z(CWJ15H*XBjOj%{*Bph!M-Pk2;6r<*OipuvNwcPErk9j&3EUv2fy+7I_pzLte&UB% znoAx-C3^2K%?BAJnJ4^UR8h73B;JE=WWEG4LQ=#rr(%8o!~iA{0RaF40R#d90|5a6 z000000TBQpF+ovbaegB;a{Bz@>vZG4^Mp~e{p*}5`S|mZf06y+t>Es&0Q=Y-H}}Dh$nki} z0eAlZMgb{**Gz7i{{S7aObbf3CUy_`Yz>zxCfKzwx}`tZn}QT;zH`*L*q&{yXF{_ro`7@WRL1KfG?I z?8AgU%pfE7Tpj@X&Jbf~3-2L0Il*&jmI0b@M8U@(2?0f_)_vrV>HxE2LybJCg93yd zF%^`Xy6K$ZLNNyDN&1*zUAU+%=;$Nw9zg>|5TKL7J)UtAjDl`Ele#a59;S-{4SfYW z%-k+$1*M@pPi}0&!IV(en>yoTG7uHS9b{#NMX-=9x98!lM6xXSFV1eCfNHklce0suYLqO+;dOh7{ zy}!%-;7FUze|WNskMVGA7y9py4gUaL@s?>mZu`ZD`P^>@O8)@YTvbVxkTxp~H=7#C zcruv-YS7q$oJr3b^P8Ows*xUAp7Hi&mK$-kAG%;FHj z^p(FDlh@V|4DC>C?%?eMY*eg1sUs(G*-a=QjA>%Eu7G4A3T4LsRQK%W{ zr_a3I5Dofhpf5Vcb>peJ+n@-2;?oT}wnx+|JrHq#5fr0hd5f24JrKmWUU)I145++| z;;RQpV%OC@)S=ZVcULAX)pusz&%YQoHxp@b?C9-Wb10KjVG-PCt{7nsTZ0gFsrq1I za>+n|a_mFUTme$36mvtElJsE{2sE*K@J;KLXcVTXyLC7GbBD#~eK=@7F~})|AVURI z;}jO?K5||`9FI)}z&X0igT1VRulBH0x<5zv#De6+Ykmvmb0~zz4HVAU_H9s!K}NK; zVjPG;AS#C@=NUW3R0@gVjh#;SEnp;|1q;tA?JgpMb4n{6X%BLttN_j1AVT&XmOSB8 zV%7v5j*`t3;|pp5aw}YDJ3Bt}T6h&j?KvVRUYRL#jCv)_SPgCCA+YB#ZK)AA!~PktVv1t{k!8% z09sHZ&ymv>vT&1TJMX8~aix@iwHbf&0Lp~Xy@|vG^_wP8O@yny6P$OT0o4<)R{i3O zk)R!c<;7iLcIVeamg?(^?^5E?ynQEZ$;6El9MIHE$Z&;iv9k7DImyQ6h z9gIy*%xgfCk={-qX%V8x@8=%Y`Kc3iH=_dri6kX2UO%i%jDw|roE{GZ;JbL<{bM5L zu8Y6TxgIE^xF`L}$|I;qud$h50UfJn@&5qJM@T$lpv1pK0Dz8KC=0$WT;Q58XxptP z?<<5gP*#St7@EaMOtcgMbaB6VCTnVlu@g0a7^Jb14jHa>K5$if8xSbGf@4gA5u}8> zA zylBs6ZZuPpfexJ$#tFi>jE4_X7tR=NGQ6TV=UWMwunM$=0q`Pw>z6F}Ezl!RW$a;x znzqGPUQs<@9aRoNdcn)i457v4Fgr`Tjr#^BteEps7-<^9?Tycv`|x5C8WIW1oz??8 zgMtrAkEbNT6R5OTw0p!H64!i>tQxC(=?ZqQ>mRW9p|O5kAWK2U#l5sHAEnEpG5~w3 zX4Y+hP&yXsN++di;!He}p)x~K2HaS9XEZ|6>R4qW7)_OQ7m!SBHT0GUE8 z4Hn*TsZAfF7XbjjLGoNu4*1?xzX}+x>^H3tFBF5zmf$;ny0B`FX-A#7GCq6~ttUgN zgE$B(L<7t|@s%FLLcZ9)ST^QGO@KGuGnm}Rh7rC|JLc&46)=e1OY?(;O19Ab&x``Z zSpW$;BY!z3wbz)QPC3APC7N%M*>d^ERsbof5amc70|ekfQFWgZv(9QZr$}2bb{|t2 zumhk7x73JYg*mWzCuft(AAteMNlj>_J`)8<_h2tzB4SVlT|kgNTkVUnKy;WsRYb(@ zVh;Yr=mG@eC_bXP3AaTR_TyL2C{m}$zZpQHqn^i)7~kHwgeIp&Ti+Z3;yT6T?_BoE zA@W_d7jB#5c!2la(bapL_QPVjSfvIN&NAwDf~X_Wp^Y?fA}DFjh4ACYIjD{3@_!oR z6voUzh&QaChB|@Z`vK^@;|?MqLdEIM1SfAJ)*Ud!4%GPg#E~)D#c%1sc8?v-D4}6o zpap(#N|6moy{6f3?yIGA*GPfeE<}bf4z9KO7}b@d?%09y!!Z8<(qSSC?tq^g>xqck ziBX~9M~v4J4yXtolo{E^SXxs|K@Ignp3F^3$j3=%+oQb4tO5qE#eEb$aA^R7a=HhN zy69lEH987GI&Fu}15jaF4I5s)ad3@^n%YO!r@RYpc{D}#7#-AmAPm4aHZ5>Udj$1~ zYY#hU1NW4qYT3qz=6z*t+uPs^Uam1!DeZwf@pGm{e;?Z^bC=j|z7rEEORnudBNZN8 z?r`{{_l{6c$NZ4M+7iDbUs!z>PhV#^Z#3iSvh>2dj+eU`_m`b1J32q4$ij+@y;r$# z;(|T}{9(EsfP!rgX7#Ky!br;Hoo_w>!9YI0@FN<|fqT3K2i?R1Ne6a7@#7-<0tm4p z_wecKH>NSz#`NwvD(cgP7|)Xs?GSt29>~hl3`$t9;}(t<9ig^-Zv{@({b1oUkDQh` zXYp`_cz6-d7^=Xh*IaKnQkp17h$8gJ7Ci#FY1f=s&XY)}o_Z&iV-|LTBuZFam|XsP z8g(@i(;Bs&0v@k;j#l*s>PbHeL* zS~eZ!a3^bhIB_-=1sIpA%4ZIXvQSl9&xY|ptp)0!Zs;fA#PR?x3KBgzA2$`hqf*U* zHw$;6FoelAr3s_cj&+omTh2$dv#hvT0*vcdxgX~UVNw&Lvi1J}nXHQsWW3Vcg$g?a zdZTpUv23W&I+3LJhyFrI6(~oPM|fLAPECXKn)8nc(1CNw(5I|$8wfT81Fp%(Oz-Z7 z0u5*dd=6ZHj~mWx{eeXxRnQCT6YV}=bSt)Nsn!ZYgP=D<*u7slH!3U0nn}$ldw}N% zP$slAQG1A_$ymxDB`v+F^Ts_9A$jGe*IX$14jPL6?~ZVf7PV+9b~zmbhj^3#2ns%S zNz}Mhx6ha={Fyw%0?H{{xW~o>G6C3w2e)UbFf|#r9Bv}swED)4B!DTZ@a>0PNN|{!%8Uyo2W#d6~?N#iU9+Y zM*N0r5Hv_=uBfFtV)DoW8>D$@PRWK`VFTAd_agJrF*XOCXvef~)>))06GAq1Abe$W z03A@XUn2bFvP*C!Z6_%Yj8bW4jk_+ia(LHV17va9uH8;|>~Y(3hJo3xt>wJ{!m62i zBOfKjX#;%dlftC%Tw)<1Hb6fie)7CTy*0zyUl%TAYhXcnvtzsC5^)+O)%LB%1O-*G zE8QXc!T^}7x}A26wj+5$nhDr?cg6EvS|JI=WPRWtu{lm9uEgvwFopwTC~BjtV75Es8j-SivIx2c&Cb~8~Ve-;uO;pj|%U&Weve!Yg`n1mtz9`TnZU=cDm(( zv;zVvcUp1h20AdE>U*2ckHNL5K1jLB5LHw%ri326$0R#2T<~f3zTt=ps6n7A^VIi| zMRsWrMmPvPum#ZaJ@2;oyg5_}UqD}#A9w??Eofr*bbOBZ!Z zupaQ1#Oq>5U@qt3kyP6-6brhWmQ#!&FBr#3)hOC|T!>MAtPu7lx}?n^who4y5_ab& z$OsT=k>R$SVNr^=8ypDG= zUShS73Vr3F{?Oa6BWw!+_)~JYAm;9voa~6Xk3B&C|E{gSdz1co|e0 zO$XPWNj&45N2h@gyqS8<0EDa9(}Tf^1g*)f1if+DBz&R$-<)l#+6Pfrn;H&%F=A^V z8GrJG$&C<12+tYZQ_pM?iYk0d;V6%MJf#h#0dICy?~0^>8%g;CT{C1p@-9QSNb{`f zkFv17`fbe(RWo>t!*aVyFeLvpb#uh|*vF=0DUl*=Y0T1Buhbo5~UHfz150L_x@``sI^KkJNoAQ5IFlm#i z;|8*7al;&?S54Hwaj{)&Gf}yG#q05jEGQ(4ybkZT88iSaWQS1KrYm>`kyLsyye~dA z`hRD`i%?ON_55K`(9YBxio5>#N#lhQ&;7x0pj$Slw3}Vf$R($&P)F1Eh4`@SXscd& z)1+k*6<4GPKcvS9m1vIxzXf^bWmY+KB(*xQelm_#$w4O>(xCpl1hhZaRjywBwUE+k ze40NzzHoch<(IS5j1XbXeHe!S0P|Q6fFbV4d@sw}Cp*?`)Sr1K4d@|Lq3UfsEgEn% z#a~eW0Js85Z4XLy)yceF2bWrQn3J8r*zbdUWAB2Z4IPh3FgV~RrfT=(do_p+ruf{` z%8y??I%fwp+qVE~)4z>xdhFq;q$At=b?@YnN(w=tfjb3BXtBZq%elO<$CzVRd<4+FrVM6D)d~-3{;5H+RT@T9z2LAw^U>dp$ zyOW;SQ^=wc@Z9g`wo8Oi1&F%mMrZ|rT#heuw93T+I*rNrd&@mC5_x4f<2;-pb>EJ^ zcu@R&YbfPT4wH|4@*Sva*EweJ4q26Z_500*ZI4$__20nbS;`&fSMxot3Q9U(=O2i5 zZ>NK$R37nR;FEf>JB~*p0F-YG{bPAT7d`Z1n9=m?#X(I`EHv(MXJzk+4_38D>6g6A zC|8sQDDHh%F{{V8ENxqy+8FY+1sBh~VYE7lp5$P7Mji19UZ>|KZs!eG4vgpWU??32pR52R z=PIs;_k&j#__@Sx zN?36PhUT)Z3svO)7Y_gv3T}UAyynQ4(B&Pdog(zSf^uWk8qlV&2a~Uy-%%RP)l1Jq zJ{wND!j2lz{JD$skT{51I)Lyq7gRbmXjl}nu&rWzm!02#KC~zr*N=NmS zp~?Bf2jt(zC~Di+_G5X=ae5z$_#6TUFm%^1=U;dbH~eI2>HT5*u8i9HQKMR>ZENru z%cOmf0_yXRk1W7ZizQ*j9Q-`pQkBaH1ZKul{2bU-=*Nwsa(UO9YSAr(tmHgrI^qci&H(5{FJr zIq6WRagS+9Ixwl!9ScVRxij9@CI+^8`?>Hs=jbw z^#d2sk}p$pxJYlM$tdl%1S996^Ni{{S*3f1F^6 zA^!lFOJ(it;|e4S4cA{nJ~iEpWJDc@A3go!CBNtKh2dQz#-IDgj3m-R{gdqb%M20? z-h^~;_uC73F;;?arCH+WV*TPY z10qrYI(2Vcn`9r6fMWjuGYY4mI{}WlDs}DPK>5I!k7;8aUW)E>k(oe~fH$C*o^yqQ z{#d`xZE9bwVv(&}6!prl=fRyVFp75cgD1KP?fl7bZ4G}8;0L()dU-^~zVXYJPN!$aC2aoy81D_heG?Q6IeA`RcSo-5jw2ywe?5nOa*c%| zF+tuBy=66%W4kUTG)0};aGIs%+eR2euTQ*Q$O(p$0-TjPW|>9=A;3FCkmCRd2o8r7 z#Og3Tc(?4sOi)*m9#4{vXbdgG!~B?=fTJPcZ`wXM!U`*TvlaJavb$r#l5Ult^jBRm zCt44{!d)qUAJ##%n*12kaxbE*))Y*l$EFRedZ-V%mExjXN)KPe!O9Z$e-7P}wDu`4CgO#pYb3ccKL@Br(Db#yeZvKJrB(A^h+)74gG+2)=7-h8 zGsYgY{kTaV@?j4HA~sJ?8h$mrLlBC(fptaJ{{R_xK;VfYj+}M7xkqwocOQgDdAy^} zuKxfequ|6mQBI0L>B)HSo1{6*r#W%f>V3!T@0Ry+ZJS;!Pg%JNM=CJs4qqo2mk0Gf z)>-F;y=}|JkcVe4bPDeBeq2cjn)K;@2R7hstN;UW(VSv8L^!$@?45Z780I333m(DE z>obbM2mlkpH|6b%9)DnCXc6O1*>B`}#+dk8*ZSubC29LVjG%c?h*0{j!_G?qb2gg~ zhT@Xrqkp+L%JxY*f^+28?-IpR%Btjw<2t`3GSPMMgHrFW*@*1O4B zd~}#o(y??9KF9CR=up^5w>D4z4cz^nE=t{jUe>{yff^AxshRgWZY2WQTlfzaP2#Q0;#X03nJe%tWMDjE*Rlj=TkLaV>;)@TcjL`Dys4T* zhpc3Zl6N#V?uH!mV20ov)9vE{#haSl9!T)@#DqSOa1)I=JH7HfAh7j1-$fH#m?qg< zj?TvJ+^#_yhXZNAE{{0Hq@5aQ`G0J6&>H}X-$yvS%Z|sR%)kdIemp29j|)%W`I)YQ z=5Pn;9M&C+2k3D1Fo4CX3=lYkz@5Py9{Rs!h6BESOcNcq_`yzy$7~nc1d>p47>o_th>aB0(#L7^ra-D!FG~@a&9MTWfm%3!5?X=Jf%wxd4vz-B7 zl)@#q{lG;Z)B`_Zh{ga%l(#lg73&&0`}oF$+ekOL*7b)xAi?C@pm9@%Og%Qy5gp#X z5$}gpNH~B@@HggSl_EmY1@F}ycROQZ9k~AhJX{5p+?~(ipE&;4Pm{~txmBWH!}TT` zn!R1@ea&Xe1|EO6vDW}F8aAk1b6=NiP39>gHQx@-4ltwLK~6wa+MPI;8uOcg07lb6 z9^Kptj*`G8E5HE4tG-5?I_nhmFfPEylXMdKPTV-ZMinelHS>dpAOxXd@O6>jif9T; z(VRJ5CDI79cgoZ0j))Z)Kmi@=9~f=V04PGbcnCXJrYOAK=EssA$e*c=NH&{nBj6X4 zAP^cgKd?S9iV`K}U^kB0(sVx;_`_nA{!L*#j#uj!{{T@12}A6Ddt<2>-MiQt9p(-I zKr{h~9^6Q16VBayWk<$cB zHgo~s+{O7L`FKGp8v1Z>%e(n9Mch9V`TXXY0zMf+=*r6j@}H(@@j5oj)5gGd3>f6R z3$ed=^OPO8O1kmN9WYu7W~p6zG9*aDg$VN=dLw}B0%&5@@$rKA zLXaefUPjLcT#cT%>$mG(_tC+y*pg{co_BoT{t2&mPC`O-dgD{t{1`=F=L%YA+N$j0 z^?Su>VBLxMtR0sW8fd=n{$qRzM1Ub4zsocOR1oXa2;efQ7e`1Y{o+0qK~|B*(75!e z6NCVs&0HKjKz;#zZ1mtx5c5NWd(7jg&fU5i!OG>B{{WnS%%{u!{NVe4&3{HVdJ+DA zZ{BkpG)e{hn3c-{3h`cnSH@Z}BR{(nT2_!V;V7Q&2veR@2h}FIGlny8U|YvK)lQkw zCrBXBby49l>~*r6lz~wB_mzRS{d>bJ)BOJcTy&TJ0Dra|+{EG)3*%GkA^YqlMm5hb zIN`J~4>8)mSXf$fG}(U;%AV!W{ul=3X!nR{sFpYJCUn{_)+46WERq`(7ch znmX`e;Wv%~yDmULPNN%maECo2dgJEw#xFBT+;#k+?rZ3g>t=Z!fc3nA0SDie&S zSpa&!-^N0QGJ{tWdwx82%R+<>X+JWbc`6%05D)#pVX}Aw2ci#05^(@KrvCul@lFst ztAcV%RE3s1nox4Rq4KI5a)q$o~Ks86Wab=N(;W z>$S;CqP@%)f&Ty-nGCiG+E?+H zN3ZO?$!OA$-8EmbhvC;ubzi`Wb6E&x1$f^M>z=zKiRpyo0VU z#JMhpGo%4}??mX%*uvN!b_e2X6Bcg0%j{s%_#b?KIAvzKUcc#yy9(DkB(|NhI_{eCm*9K5BE$Vh9x_fr8<6ai?q1{ z6~s5k2S$LA3vc6$5{!p&HXgkWoMK35{LjW9H(%ZLl6XJQ>neu{hC9fEhxRxrHShZ{ z^im%q7vRCcf+6T1N)O0y*^LFSv`c;`=Kw5?IwTHU-3Pm%537l`i+Ha*c))-du!F(X z{Q|h0MB0wa)bTtbbDeOV9Psx0!G+{e;6{$BtJt0K6!8Zm4|_Uz+{QJP&S(cdS>W%S zz67K?u82d*IM{+?r*>DP5xo}<+?+ZlyySQ5jDh3)V4C9p0L(4z;~4p50{;NG#s?r$ z#kWzv&-0FeM4JH)WFB3sh9&?S_aFB1(9S$R>zve?1HcLo6MpfbNC;0Owl{ui1f{)! zzYIUn9Eq;oUoIUU^YxIH;KeYr3%-x9IeP^Yl~6_TrSplU^x}vk;GuWl2n19Tw(-&4 zKm0ZuoFDTO@+n%AUyL0na--ff1Lni9*dY-~+rx{!xYRaOA!-S+J%bg(=* z1019dvzgS7E&PTau35j$8K{=Sx)JM}@qpcTKHdH>O2ATTvzk9jaV8@~OC4-j2LXkg z$XyD%_18B%mhebRH0x$3_1c01djU?+G4$Bo=tHYqiOJ_TF6#sZ31h~oNr&y#an?Qr zPh{h?wH*+jl$R>;C|7ns5oRzQlYa@0#HV z2BN$V8}FYnmTVW*#;?a;7_N$oZi(q-@rD6e)2Mv*6?@`GAH#YBUAX%^<36Y8!l+_$BNG})K%%2>N)CGB$P$TKm$wC%1rXOmXgyxt zvJoU_zL+)0fn!F4uQS-^a@&2p{{YOBJR+MxJ-@s|$Dkq#pBYTZDXI>lZe^RKosHBx z1{>>%`V|P?!Q+O!0N`u66;>7BoHlU5>;UHI`4A9D`SpTHBYKX#Tv zsp)#5{5h_?VJ{%rhLMXQW3ThZFr2!5{{YjrYX?*Tjb9Je=A#@OW#_7i&T&YOLopQD zk7O108~ilC7}mS72GQLJdPXYc%1}_d_kPT84I2G^aZ_d^cX(EzzNN-9y#PS#xuEY{ zrhA1%p*Z4n%E!Hq)Dh*+?qY-py$*UbC+7$k831rf1vVPqxS66gNeDi_IaODarrP^3 z$D~u^0AMO5)MlHiJ;dvPYq&wm^t%MTvXxI7;X>{7>4HHT+>z34*BG!MS+LYLG8(|5 zvS{~DG;1|{W73nj?)APkh*+f*=c3K?(T^@-rua`pVfBzXuyx>zuO~P`3<9WtCsEbM zlZ?n!TI=L_XCDYD!-J0%54YY9tcH?5DhI0B7aT<2F1*jC65^ ztjy~GeG~JQ&j31d zr)ux-9v1qwV3j%24~z_nP%k%c!}!FxDrdls=2DKk+wTz9kLSnge~h3duI?h2apUg_ z0YLQx@73o5Lp?FE+Vd_BfJdJ`@p;(*I=lzo8rq=wyFZDO=hXP>`kyy7qjz{x8B>&z zk434R+D5jEF@#;Z)H=^aUMIM4o$KZwd<~ z5V+?Trt3|kuS6;LfmMwfd^)G(!(qZIjz8uj!?#cOoF}R2kWU?85Y`e#_n+m-2u4UJ zY7RrKc*4V^wtF~k)4s6uNC>0^M;vq82Qo1P5ErdmyjjA?VzGzYUs&sQ*7iFb5c&*K z#nlU-@7pzSBJ8Hs^iPali~ga47XJXH#4X-?KNx|ifH8p&JS*m4B&Y$7K=yNhz*lib zHgtz>*oe4tg!Sb7;|Bi#9N3zO>{to-$Qn~nj@ta+#u||{aP_h6_5Cnhn!(YwHxlJ& z6<~zqKPAYHJ$Gda%>%b>x3?hgdtrDr_WuA0hxt@Ft|ywgMq3YgH}YWCID8KA$4>q5 zs>{Mq08TjH)-I$nZ2~>Gf&nBk1s^u+J3CMXhMrd38m}F zinj?5ckzb!`TlSPJ^m;3Wp@Oxg#2j2ln;$b>+aWqfYc!aX;qJPikr^_{{Rv`@Hmkz zIUuiUkBmrMKdb!AjAEsP&7%ID6z@09So8r2>atkv%LIIFZ{hSV8(i-=}yG z2Uvg@jdcjmAw}umt~x$#c@q2|&S{|T8kcLw2C#zQeFFYD&HxxbD+kw4lLK^eETQZO zA0@#_=q=elyf;8f^az~!xT~|2L?+Ky2mB z3|t_bEYmifv37Q%D|Fo8zl;Gvfm6}KbIF3Y{X zyy0w`S~fMkcKqWEdk}?ZwX3zjlOvLl@+!=ZLkE}XEHkRjdlnn?1j6FO9G5pXa_9Ah zB8x_-PCIR8Y^al|(Lp$;&K*>SmhI!STwKWwE~16rk?RX@hkpBi7~^?0(9q$akuk6Y z9^x;1nowZ!egb%liM$IDK(n39NP!U}hfYu`bz_X==9Rn&KRLzhCj!~4;}(KY-qU72 zm!Cp6uJx%Y(in}zP=;(iqo%K=e{koZjSvwIYa<^qfb}w7mLJ(8Xji;E-=;5 zT~#0rjtc8$FvlGL`FHc1Ch?aRVAh^+=if{O!VN5MM<0`xNdjWgJk6KitdIk>bft)R zaBWw}05Q~}d}9fF%&Q&2Hjj4_98sVoPh-ck2}Z;a5Yyr(mgej2H>pnilcs3!VOuCy z?et(9rby}eCGqLTS>3LpoAW;yiRdR3zTF2sb6?emqJ9APa*-t)po7A*YzI8ttvV)LaxXbrgSr;#I!5tC&vz9qMp$Y62 z`oLNW`a7@50^`8KHo?!Ry}NnCP8h1fc_J^BW9P;csT8^c=mfoJVF!?-k}um&Kn`<8 zanF2C=xcu;&Nc!_d&EFcn21wvUrCp@zCT!jA(YoWemTj02yEgA8+L}x;HEwd*1P02 zh|;)&vlE61Z)0x1=NO2!@>Mg4qkR@<2kbbFL))&qVkT;@Cq(QBuXuyc?a(iVf!jc* z85G&5ARA6cN_TQjzR|}!D3RQ7*VID8(A)q%4LafIX4gQz7&FRa?M!yIYvl9T;%HFB zNnfNeYf036~?tM?Uf zBLo`nn+}k%^PM@0Z<63Ch{V(Nzr5}T^@y6x+5y$wU^`oyo+z44Pmvd`_mi2nyRReW z^MS*iAe}h?*OR5e0l0Dqqo4w>;{e4Voe=X+xXt*oO{nVUdZ&2w%3P>I`uz3Icrbzl zI}U~tX;5Z?+2Jyc@&esD)@n|TGq%T2o9saAkVi}r0_}OjT|5}8iBv8^n?OO{rVLSv za*aC%y=2TP4V4;vCRan(Xz z_3@e~{{Vo=Og)3!V;mpm{QO? zlKcQ=>f(5%I{-C0BSYM{BZ49~oP5Wq-l{wKb%&HSKm)qxQ|A%nhu|Gl&ii+&%omjI z@l4`4j2nTSJeV8d>f+~h5q@9Agv9DFr^qJqQIFeiXCbu&8woFK*A^`RywRs5Pv?G5+D zI&H6m@o4&RyM0c8Tj>YRIXdEX9Vf-YTms3u2faCTOj2}btbYIm^@RqSco0M2a;XcN z+XZ3QT{4no^eR2X#j{nbKoQg$c9#wtHI3{qbAug-tEfPKP8bS@>;C}2fJRJp2HCOt z&nRR((rqG#MS3^JI0(Y&N>5k>1JLw2x<47D4ZR}=e>kei2-u8}=VQ(qo^9r9=Ldo2 zH?|#Ac&pz6y2Zqa<(JMnR3Yf3{&>Oub53A2^PQo}-O&q*qHW@cvwfAnmmhGIX1Yz{ z$|)wU-}8AVM=obUeT+fn)=Hpy?gZ|y*PIEsBv)_^=yJsiuy=DG&+UdJ6JqqL?Z&(O zU~uf>y|3%+b9<0)K@s((dO1lUCfF6(Nw+FodYHB7dR`AXZM`)j&ef~d8NCup0q$N( z=&wLA(J@j^cq6BF^?}n7^(Az)BdMW!DT2tpLJ)j`I!q;b9+Cn+6IrPB!R#T{#6!kh z%7C^BIpz<%H0sT%Sf6({?o!t(WJzxh;E$eW6%(-0uAx0|-U@&!sz)i;m##pAIB*F7 z;WnOJCCA!W179bM0Rc2du=+4KpGUc&&vBCFg+E!O$NZ)dCy(nmN1PC>gz!beEj`Y@ z{9y*hGf)RU6PyET7gdT`^}VSU9<-JN46TJpqqo_ zH9KULqo?ZoO8LF&x6>SSI60^^`%s?;$|H#&P%%7|J%X>!$UQ8r3X~pbclYTH> zRe=H)&tj*XTO~uYyzQ<&Klv+jj3&RA5^xgTclsO>G&V9<^MWWq^sCA4Vrgs7AvXEd zJz3iuigXI#&m#;C-PiCRQxh{m2;n*LQSjxUn<9-kPT?jZ<&Wk6G`0D^`r~zK4<}p2Z9W@ zrYuM-qQl>F3e|}hs8765QSjo*IfqhDruH-hRm&I-H zBl!GaP|*6}_tPnXHk8rz(mAmVEIJ4L$7?pMU_a*-B`=ms6Fq<28jU;(esBP)?a3waU zmsjWi0Et9%hv)X=#3B(Muf&7zCD%|Xym({fSe(-o_1m`?-8)x3j|a34uKQ%%Y?}@l z#8v6SfAKA%{kj35jpJSMNdb0&X{iAZMe&HJ6gNiD>qNasG9kR|)4td7j?%rJBCB}l z_kytOj<-g;PtF@?s2riLQftNnVuwyJbl>kH^bZA4HTivFV^9TQ(K03=XaTG>&M?yr zTU688ahrrbwMo94%7$~6S*Fs!zO{?9756y$YR+ddbHiVU7nB0*V6seTrC%pY?;lF| zZjv9n>&^g#AbKGQ@sO`?(;PRO9@F=q+GVH=B$%&UIlxphEO1fDX^7kq-U^H)31+w~ z!vw+6#Gl=qVhs0&)3f`_m1&9yBT2@MnG1%}OGdoKchtv6fl2^9O!eO8$h>vjP}@z< zK;w)%oNNR@hZU*Rd*>J;7%V=-@iN`V*J_(EY9-f}k>>IF!|Os&PC<{T(YkM+nTRfd z0|EhOY-{4{g7JgZB8N?Lag1B({bYl}$t9n;olR_h&*u}nWkyt+!ZtYqK_xx*d(Jul zDB9Z+%?GRvTCHgbZK7=UxXMh2G!#srxtQ)V8Hvg9rI8?DsjI_{mxbxupRTx#(c$mG zg{9xFoaB2@e4dk>TslL^!`plK$r5shCv=|Tdox>4lb+uH0J9Dh+%)q=eI`GEA>+)z zYAI(G58g<2#PO5*$k4P(^l^;2)So+F8gh)I{Jc7RqQ93F6eRQ-zc(OyGgF8UMa$Ut zfY41VAfA=W(bfQ1)#3jDI28v#qUb&OZ+w#HtZKLXFY#!%fgQrc48GlYLsa~ZKhv_<8bq~lYpA;@G@v1RY6?AvX zH)~xlC({Rq2M)nH3cmagZdGZU_jB^iEAvPg&Q$6_%`p?JgO98f81ab+9Vi)x^>xX% z&2`!Hlv)Joz!qza7^pP?Q*(g;d&^!@ya?Zye_gQK26~ZeUoI7)3cT`D(0%5C0tyC8 z#@aj+47l>W0hRU68JaaT+~XcR64L$P2R;Yq2au$J?TQ6av1oxkxaLgflaW3Wp793P z{cx11rr#Ru0=^7>;C9^>KV+n)=*~#a;sC&qUaNHI)Hyu?Yq0$=d#^*W8 z;zy1>etqM$w>>!<+rAhjG(1-R^LZ49gq!!qE3Lsg6?Hv^0DK|(GK-D=m}JePO*-C> z)*~xGRH)m%sP{YPO&EIcFLGDt?}>|txd&c>A>lmZ7(~}$9P7Fmku|Ze8^H_Pbh=>i zCg}RiQ`O+Eh&Q?|?O{07amv-2MK{D>j3rC+;|- z>HZ&h-|%;fKs8DtPsCgg;W$q{Fo{zaOR4p&96asiF~5<|l7H4jnLmyR$jF{>Qq)b6 z!PqqNVMN$6ufnzo=M6}vkO!APmMU?&0ynkm!Hjtk2Pj8@wZWsL1sDeONrBTq3YIsP zHRl9m=@8ta(`Zi^b7D;B;kqbw(ZGSQxn_d#_?f?gT1CA1(H~|;#54i#D5ESHzuYOuP;;0x!o}vLN6<4Yl+33bl8uu|8S`^?zjeFeXqq(vuj-mk-oCyD$1aiPSSxx^~b{bEe3fbNPC)j66A zmaf}Uu+x|?s>F7f1whui0XO4VZr%kU7n_lt%Pec>u|wl4r9|lPJYD!>asiCncgO@E zBZ{9%2;~EY0Zy(Mc-<}1@Tgo-9<(Bq*MydM#qiR30P+>S+RAx%DR4gSwbvm*zJWR2 z8)tV}784wObI{Wqs3L8{FKbUG5TZ_4h4@Fr!i{tOK>l}nwcYG1Uv<|}Bw@)yj zddAWm>3>)@Fw*y%{b56=q6DXBZu{Upcp$oT_Ja3J5Ze?fb$ksU`<&qq(SNhi!t7B6 zz2G~r_%h<#D5WZ+!c8Y89K5~=C4r&^d*?5iOQ02cr{Toh5lV){al})|FiSPuKb(GK zghywu8LT{>+|R*>Gy-x>jvWS$aRSxU0u-EgK73)n(Nqh_b!<-=2H1);1Rd9&mR&kN zg!zt$P04&AG4%u`+H80ZpE%%!K)NODo5T)--fOOiZ0>l+*BJE$PkKw&B{l?QFW?VG z41m(=3xlB&ddphMD6m~srXp-BH05u3{AE~5k%~Kx48Fj-HE2BAJPvU?5c3Cw2D=-; z+MqcQ5z^J~h`0jKszT1%K{k>@0AI@MUIo5NK>#KPl9>FoFUq3QZ%* z#|#`I?*?JU~6?6jO53$qubjGmp5Q{A14o-bp&PCO^4Y>*?}n!9JtL#_+g}q z_}chJ9DQ(nC~wax;O~|9&n-22>{RzKLe&XX+eg;lelq+X2yCDy&LEv|L~Z(yq{Gcs z{9NZ6Y~)zwCr)|ZO`$X*E6CIme3%9Rg$Mw*uC;S^DFjdO zbMJTz<)J8Fd~X4`VABq*c*M#k%{p`2t^q_?Z_>C}Si0}y9BnZAPrx2*k)#o{gALbX|13_t=N!#sNIcg$^2n#h-I#Iiw=)o-Yk2bTDaDO;CL^ z?DvSR5!tYluTgyr-%$#n=s@%xUM>Oz_c9>Y`uq<#`Zv49x?UJ8}( zelmiaoq(sT8{-&NQLfZt_%7>88hnl%2H}S!E7^pgG{%HnFf{~&q6Yj1B9&vUV?Q}& z#CANI?WOT@Oe*282yt3pyaA9NDI<+iC#)EfNveY5o<}GT8%+YM&h}=^Ro$eB-+2zn zsaLW+CL`%mt4O@^^YetoD-MRA87D_V>j>SH#ShGoS$MrfxHQmJE1~DE{&2RDRTu(% z;L;MUa|geCEb&K5llENog>#Zgqx3Xo0Wv0N>eqj~K_On$e{b&?ds7N3o@7kgZtb)u z@A6_+_25Z%U#OVJbhm&MdqrGiDU4k^XzdB!J@A|M9R)4dsrY7%TtwtugTiK#1D-1^ z)xXXMBvT{b>JFQOtN>=5hl~;+AFl;paZTua;Rdnj&;hMrZ%j*0ZMXtny0hMKs@Ek) zFHSF~H@-l(Pre`S z$C(yzsZ-1+vjsZb6M9C@$~abIp%bfpPZ&W#IP^RJ0J>$R&+FwWojgCh7iz6^EY|mC z2JNskIiHNSO}Wf(J}zohyyu?8jkrD15RD{066>ZS<^n;ukH5xn$~AQh1%7i<0V$~B z`*H}fUfQptjMTA*xUbL!#^!c=2X=T#igz<`k1u3$pdft6QQV?nS_(VRp*tnT4lI|D z6WOC3`P6!n{qN&0j6kQ)(qSRAhA!X3^yPwWRsod3t%ZuA7k*4_0+D(Eb}lhl0;*`y zdSJyZOQ%|VJYo$m4x@^8T{sAEw^~BJpk+}Vgi}Y@-Ykw8r*QTa9&pb%w#8}jI%k$c z0LS1d?-~h8tJ;) zYoGD5hh7?&a+Ro-^f7BjuW*t2#DwNQhu#v++!5;C&@mT@+6OqZ>hvOBqQ*bF~P$s+K&=qeI1?;ibAq3&6O}(~< z&I4q?JloFXE%k&D97ffcde!63Y$OVrg>PhukFe2cK~M8N!KYr(Nj&FbhJz z3H$5g4g*8txAcB+L6ish9bAIMa`?7no9f=WSIG3ounB!8kJcw=d}#wo*fB0+RbGe2 zj9Gn&)C29-8fSh^FY?wW07ZBzJL$md8H5nw4je|c6SIas_P|XN&PJl27p@Z0FHl7L zB+A%;KnntVI1v*d>@7NZaXySg#FkBg`NF<1jS?9pPP!~$yLHjN^4g%G$k+Ax!j1sa z>WTNp2tD57a`sT_7aHZrw9|*3`NH5$%p$tH1{^Qqflj3@m>3NJG(la5TzWuJ6jW*F zSh5N?y#(`Lc}Gk|v!ibIIJdoI=nmJ`bFo8hp|GB@7^D_45PCovhTfW! zBS7P(01%3+R>pyS<9e!sp2U{+J7jU~pqN0Vk4)=&@FY$sc;gwij!hR`(Cd!Cj=mZz z!}o%iN(~{Ydqcihm((!jHq5^{p3MR--MwAM5CtWL^#RmCqp*fp%CSjU+~{dN$m`eFG3GQVzpAJyGW;H*NorN z#-02aoFjV?c1v=#kJY4>BxJ{uV#s-T>%Hx!P0gpKvtY-QSM+Tq~?HJ&P6?k2N0_p z1Q(Z6`?T$G$pVEmIUKQ{7io5BXA=}BK_gb zP@Gj+uA@v>V-Tn!*L{z9euN6xjr5d!n5!!kBM3VIK5{>_K1E`wA zA9~5Lg*%f30xVK*y>)cR$!r7=K}c&~)fpUs*pPstP9uE~DkkfS^d*`vGsp5xj|L1f2^* z&ON&9$qF~UwBZ5VR>Y!fP(<((2-Bxch!22{`?op3(1;rsVIJ@nfa!s-I=5~MERQBf ziBa(}@ig>_spR3^OmGmjX$j~>aqpup(g)yQrXV%Sk}leG?Z%|>FxY$nxgPC9pio}B zanfM5R_R`-U2DgjA`@fg9)d5n@J15txdFZM{DuN!wCX8-%qa<=;S_k;eHh2m;sqp~ zm!EsWeY(2~$opO3gg!B5n)QHB4jUenoGg7Hd%#p=TLS~+hY5JtOUHN5895jm3Pelt zV4MjnT|wJ)KAhe}YLKBg;Qlae61LKG@<_xKL))+!o?F+9V%|6T3uS^*O!qkf&8%Z|%dZFs+3YFx?#a06suZypt3i7oT$OadJ{yrD(G2 zjsE}`xz>d)gL4v!5~W<8oRfks7~|Nh&wSk<>#<6_emKTJK-Z~gs|(WwFld*8XyDN? zacC29M$ad9bijvZDg*xjxLc(r(|v37{J7jpP6cw=&Y96Gk>ilrIj@5fk42u%T$j)q z`0txnEF2iXW-WNp$IGM60GlRB?zqgWVT~mFgX<%#f^4jeEWDG zpgi%}Nb~XI4blGqtHuLO&-KO#LH@g9fCu{NfTQ0(#KF+tkb%=Qn~2)E z3Fu}JDB=V2fP>00(*fUa>jW199dAGX!~iA{0RRF40|WvC z0RaI3000000RRypF+ovbaeR`u7I)rD2I_105IF?J3-w#8TMYNaO5JXl?fR7H z6~_Mn-%;*IFAM_3Y3RxgJpgz>hrc*sT2|vnPx4zA;LFYjv(uyyKL_;?>Ya7zE;aG$ z4uBs$B04sd3T)y0Zxxt(h`Jc-5@TZWbh&J!?q9L<+EZzK#CSgAz;)g!N%!g!SBJzj zhys$APxs7y)T*@91ImP`Mb<1{qq>xHEAT)z+z9rj7P5X?)_KkkH+>HOKJ{_*%uz3uv!iU_@cntGy_!!x#n1j3JC?`IKT5T9tiX zF#)8}eTvT+gGCV%lP!IWEoRzBO8vwPDO8J+%a?}&w7qLi^IKthUNyu^>}KfeiCyY# zx;2S_-K_>S&BroZRdv@}s;t9LY6HHRxC%jL`}cw%Y5o~m+G+2T+@b@WU-zq-_ZO1( z1@tfx#9nLMvIC)fP0E0~TA~!R>kP^0l+mtLh%iv~5u-X>`b+O*6c(2%dbrBQifG?CU6kz89|BA-)S=O}nt(Nh(;5|$EEu-1;SMJj;{ zfPB})Fosx-EDo1J@dR+NwMkfSp~lR2m=`9cKsDID9Kw@UE{j9 z7MiJpdei12MJrnf50=>2LRNGMe#(~TTz545iixG`X}fxMb2azeL#2mpCL)Fxu*Q4W z%m$Kz6|TGR!;0Rf^)2e*&0%x_N2sEMN^8*R?lhv%JK74aG|U`H5WJa39m~G3G!3^} zlx2uuU6rqOj=pX8&3tOIp0v#e+;X#g63ViSj1Nm*`p#X-okmQ(bXJ*NhVM2?bGkBuQ?Uq4sN4M77S$yHS774zB~5kxa%g|@4=%oQqGy%{rHr9L9fLs;P4d8>OuHfbBNtEM!U zos~;gd1nTg9*zg|E~;L!hCvrUH(XOK4ymY^jNp9xe-83RF1Qzr8+7EA4kL!$@L6(ig6 z20f_uqKVal^jk+&Hn&)vZ5m*Y7P@#8GHhvT`UcDG2&5JP*a^-c(0gsR)Bal z&Rh^jF1fS!0k8~RmY6r$UvQSw%FDnS&q7o+wVXN+&SnwCW}Rui+UOm^^lLfNU-6HLZ5M#-pVbPR+}Pu5KnXg3%+zXNjzJ$-?ME*co)|QMk##?M)vN z*!0D=y&!n4MdDnLF8FI-BoeB|YY1GXmxb-0!M{%FyoU3me#@)?B4%FE;w4>w*>Z`EwlmzJq$N9eHa!fxnh5tntwAsv`O zpqAZ;`9W?L9s%r+sQd#{qKC;(68V(Viq1RyqWWzIEy>pn#qptU5od*f+mT(xo~~0y z%`P#=R$d@zD(aXZsJu29N9GhbTTlUx#rVu+!LMxTPt2w_@R~p)I6DaZu>QrXyZ-P{kUaF0~EFKw-04FzWyanG< z1&tBhDizKgR)EVIYAxV2JFS(>A(e)3AYJ5+A~8)BYNFmJ|#YBx?5d`wfT;6grrj7+x3%A(9!iys-YL6v8V=I7CI+U63dFeB51-Dv5S3SB`OK;K(ViO-^EJV0K!8JDoOjfMx2Wq1}&aM7IgCJL8vTBhSQ zfch9SWhMoyy9gd*fh<}nxGohCsk?y2e2&G(43u|5&3zD$SOB=St)d>P{Y6bt3yUp} zC0AH)OI&a$UqUaafa+6~QgS*5y2`AeWY-GQi)#@hH4V}7sK6=189-lC1hVwtYL%k) zjY-k#OSKI->G;y5{kyx!^uyXhV z%s?~^mtkyO$;21@Cra}or1sKL$s z_LeI=fj|Rok4Wxql4!a+){j}VgDV(7Pj=IYn$#$8yLi$&+JIQHIXbfZ2qu*&(~20g z!x56~xX{GOv}BP?GafFWW>EtX^1k~^vCWNz%NQ6Q5`Rc#VxUV!P+|m zOh29AfLBw^ALO&qvv}IJ)?%$u_>?#+uKPf@!at6zw3S9`$>)#%MZ?<_gyby_22ggfa8^#0evX4V&oH5g_OF*-T92`mdhvs zleks#Ll6koTeNib?38d4vt5OGzjF2DjzZ~*>soRVBvdCrvlrZJ)UT$kMLRjreK86I z!d}Y%0DtUFr;&H1r{9@L{g~GEs1~SH%dc+vA*E8$T>k)mpuUk}wJl6(2t|Q;1sqGo zik%ei)ng)Ph7Glz`ar#PD_T~!#Hl*(EZ)B^Wr>P1ZgF+XEW>JFTw|l8Nu_J$D%9(t zqV6Eel|e<}yZk`V16=n$p%E^xJ9d0&2XGNLjAP~{OfBPVn1l=|e&M9wZ5xDkidGuu z{2&S@j2M5mnHPmRXK#3hDKE$Ihn)-~wD9^OV^J-Nj;HUrlz?7`OQQK&DhN?CF1Mco z_p%*{uPR(7`sINwrC{oo$`&)E-L?xap!<#9U|F;fdkcW(iw)fug1CvrV6RsuI2-au z?wYQ3O4W9&oqYxD1S^$T_0|AvqkfRBvT-he6;_vFuH`XthVo*HnXYpZ9SJ(x0QB3$ zqsAT$(~gVg7H0_7@K(KQz8l1Cz)`DW)AE0?OlU`ArVdy^sD|BJX)j8v?^hLY#d}j} z=Fd=#U_LP zgpuD*L>_iR@D%`pTyHjRCu=@6X7=1G(6YiX{FLHKxpi1q0BbmtoMam$aIVN0^y}`K*Uu*XSEMosMyBM z*v(gc;Mkd0;>$dJGKBF#aZ<5_gO_U`TpeswtY&gxs?YhJ?MJIBYrT8!`@jaTND`q+R7j8t{sxVZE5*jD1hp83eo1G z^r$VhRJQrVYOX^9yfou3fc7uT2`gY|x-Slzc!VSo)B*_g#q9yZO{vHIU|L`-x@Nq` z2*}?zm)>iwM9?niL7*7yg#?Sr{X(d7`}_WBF;bLoW7U3p?-ksCo+cpsUza`j^DO&K z`Tp=kGTS~ef2VG-lXzWSxAV7h4VD4cjJZ57q&iBSb?w9hc07}Klc}V&syZ7k{{V4h za|%N9U$`o&@EBUpYw-}&2PSJzPuvPRF*v8{8Lk7WfTMg?kPLMn$KnHMsrC^g%%m96 zekVmq(%05z{Alb9o|#Mmhcx$pv}1!qvBo3Is54b`U%%8LCQXmIOx`~D>PgUj&s-ZpU&FNanI5yyWO(! z*B>3`j~M~J1+L*v(*zCcyP1Xqa$i&CyUKuXBiCQac#Q}ECW9gcCYSZ|w#^`Yd>U>Fl7dT9Qo4j2$t z_f=`{?gv{kzH{h=jcszO{{SP3wJF^KdLM~GP|B6;`i84d`3TqkLQw{(1>NFdbkU9g zF}Ind+2R(k$7P-au=X+MgsWZ$ZhNkL!L*haeEmX&V9U@9zpk)fx2nE?$>4xoB9u@C zd=YBgEHRii)n|#2EgW)-@h#UkXH=uMvYQH|+k-kp_h*@F;vLMVsWjYj~_Gc%gUR5%5(c+_B*ZqL?-ToovP80jF2xxX=jXOdiHqq^m^z@2) zXGcGxQ((ta`$%_4tE zQ{1wVb@c>_b#3=6uk3-60S;&#fkD*FM!8MqkHmP2(U7h#htxJ(1$ZJTFS8$UMJlSc z#yywuD8~YxhfO*c(gs_-af2mh88?U0R7_vs=fqWBe^V+hr_4|iEI6pjkQsX6^RI~K zvRHavJZl1hvClNW6C2$nb2kAob6g7$63z#_o5}-Yb-$M}PGqZRC0s<&@Q335U{y0i zeq#=R6%a9%FSOVKB@r%wb+A6>6BAh1QOwC!V~W9;*W^~6pgWmeHU9u*ZD=MI>nX$< zdC~ZlQ0Wo2W~*^L4?d>s(w$oibhhDFl&ET*_kP37H+G~QLs*7bw0#8K>z=Et?+0#D z-US*s9{fJ#DA;XFW8OP3H~_DLana$>_pCr|6nN4IF0Q*KpO`QZMQYl(<2TwRwE+{O zophGTaBGgNce#jwY0{slp>tm5-ZYFDKjz9&ZJ$gn@yWvKK!_DTZ>y(O#LRImW|wO5 z(iCbk*BB3$E_S!(ZgF($p=aDDsc615^@Q3fdH!YShrkbILjz$R;s@lK%Gugig2zZ3 zS3M!kD(9CFn;Ss1Ra(c^AXu}DcPxK!+h{4TSQO14dqOEn_>H-Y4&%^tMM^OkKbu6l zWo19meoxo)89v>rS|^=(B@UGUEWonu!?ds@N70?rUkvxDXxKB_wE7P%qC5?3?h=qX z3(n)m#HgigWJ)f$YW(z=%r|w-A4H&+v*?Jnnhy`m61TdvugeUEHCeEh}lTE+0 zYdT~**-Npk1x9-cN;c74;m^!;K~TNwo+xGr!0pa{Vm@1EI-gqmjolY5%ewHzwT_H+ zSVaPiXmrgTS07r;hj5c&~*ZZU)b`QB^UnC-i?)vY{ z$TKXx)K;875ORX6)V><=FT$q{DMtJLpntEf z!qeSD(UkE_OoZd{iAGh~>1PZM%~8-oCp!Ex!nL)2L+?JgmsVCDrT#4Q6D+JCH1u`Q z*SuF18gxFRrR(H1Z}%&3uo^D1e&uumxFvpWBcWMSQ8FGWg1QBlz_s=B50b3O#s2`P zZqd+OX_z3$dB5~WD2-G#GR_`n719AFvjB>Q`!N&; zZL|Kvbr3wbL=otxLj`_gd#0U?f0BS|(*h+?eq)ShcSTT&@+;yHtx%$hYv$?z&Eg}- zi7CN#@teb(ky5C&g4%n{Bye&TJeWCgHT(el6@$Ie=R#bW zCDmKjw+A=}&sjtmx9$PC2Dzkv@4ce^LI+Cp0`7Xu0LVu(hvMG_>t5iR_ zQ9lyo1OtpuqNRWd3SO4u`-qnt1>W!V>JYp@0<6PMYM%2JLaWy&-!jn*^>V=|k7;1` zc|X=Bk$FM0aO=WiB{T%SGB3i%oo^=qak`$7IMHUgs8$M=9TJaAeN-7>VDY1#xQ_ax z3(6xux=gquJX1bpa1>7e0OnMPqO~7{uecu2Zr*er^k0a#Tr?S~3at577nS^YnwT95 z%TApA#OWyty?XgRMgq@N9%>xG%f(#LsAbdmmx-wM59IeKVMab8B>^7BhPL_qXWXKQ zgUN**vH`wwbw?00z`ZoQg6Dc|0*W(6>My~`(wV7pz%Ei<(?WR?Sx0y71*xvw^dMZ5 z)=P5jyr3+v9<2kZh3F$?#)AD2JDT8efvz;PO;UlJbDZWOS0elxZl1c| z+%-f^Jy+BbRyA9%65356%Yf`<_bCAy*()O>cp4rLYZKc%oYzQZ3J$myqkf@etPJ#* zLox=G((hJwfyt(V%M2&AODzLV>>wA6f5_~OUyF#SU*$k}3aOnCd<*GwD@EDG0G|F! zh{@Fsm%w`+Kk#%ansVg$N4!eX0JYf)sq;h1aU1(;YhV`dPxMOZX(M-HS3nr&Gf^PW zKeUKdBV-K(9+~^L@f(3K`K-OM=2vPkhPAWd$D7(wTHVf0K2!8cl%N8r0<><{9+kuz z3f^S7a0M90*oL{v9;Z~a##)8mj8`|S%mRKOcSQ4)^C+02!=pf&>whyI#cqHtd-#l| zL;d$2+N>k9_K0n8H@#y0KM+yOGkjATZLCFfmgQmr~eRQZ=gysQF&?X>~?ybL#WzfuwpjS53JQ&BoKNl|; z85;(BZ?eZrpcK}ZMf7a#C~AwuN%NM);SDW`-OMD@) zUe|8CmO`*%=!=0+=2kk5_^421rkdd~*p0M=znBF+4tLf7AiiVMGpIdW0%WBR35jiK%s9*lU3%Br7e%LDT(7^!+}FYkyDw=<$2AYYHvVyn-q zhQW*a->^#x#ygWkl_~6N*AkepDZ(TCn`_L&o!e?3aq}FlRdU(juVjoF7a;UU$~_%s zi-|KM(lkBJ&l*!)e^S1fkX;)S<1gp2d zxXCwd^x7CO08|x0gIjaH{kDU8@St~1BaZQ=>~^TbL>0YglIrRKdfk86U2O%LMz(Wu z_?JK;t|$X|wE?I6V%f}L0@19v)>p6>_XS$eXtvg4#uyNeI|W;>UW}-so@K(NJ)S|) z&-g)BxI=#HOuJJ8WSHD zdnBPXR1BzS`OG~v4wABmI+xm7R1AfIPqZ*2YP}A=qQ1(j>N@;Epu|osZy!pEz7XyH z>h*&F6r$HOk&R?Rv!J&0p_uy=&gy9O8wp#c>Duwvc%`%lmLGQXBC4Tc_iToU;dO6+ z?0W3Ddwj!|lqGL;$;}aYSnywXmOvvJHe>S=jpt9YQipfyQp2@NiZOcs0G=2)JdK{Q zvm~r|M(^_F<}O26p{q~O#9?)!YgT=0G};H!H9-aXwf5mDWeyHwzY5KQ?d4!3Ol&IaT{(5tv|S$gIjkv>}wlDS$j28j^O*1DHCg;pjPWg zrA!@&Y^IJ5{K`YuU@k<%(X__H6_3-du@oE*eM_F8E8{kMqufhyE7;dwkG>`iO`~wN zs*c=CFj#Uq-?dkq!d~8wX)mUXd!qhhg}}zU=AT%DePZW|#1iz2cX0W{^?Mr7`PLcj zP!N@+uWTK2DONO^b<+=Brac7*y{pB-*9S~M&=hnsXVpWv?P;u4c!?lUOKvLiXj}oB zIuP(hDur)f>_%~>xOLyB5Xg9pni%&w`h96DBK~#r?{ufcb`aC@a;z z;~FpdOH+ASIgt{Vu2p$nkpsFTS7kNh{DQ6;uh9Sv>6k9oxbE-Z%M}`^a>K8C2fHc? zv$KHGeol!kmM}gb(CR4n!|x=a5V~|}U2Vi333c+f+Q?0k*dN_zIpk7PR3%rOx3x-%O^&H~?218e0h$30G=5{|ap$7rC z-(4X(Vk%r%M0zB0EBx?r2(7BR)d+US%*1wz&fsIp)&O1AVf8CB>U1z17EFF75rBH3 z6)Px#wSxUh&Vn2{2hkm=l^rZUFa`(vKZsXU>soa`agYw0nT&GfMQM&tABdW9n$u6t zu(;;R7c}&#X|dJNtj#;(I@Bu}jLlhGd98C&_JUB|RATh6Hxxq(IT{a}U?jB1-YlB- zS9A9KMGOGW>z`P7fEGt8)xq9e=$2czW!Au6$a0*0m~7KehtaP@OGN{-`fPYcJlqJb zusvJ+5YU3Eo7f+uXRM3WhtxU@GW7)dvirtCEPaZ7qr)*EeN)${`*uqEdx^XvTFS@pbNZvpn~It+MsgYwWalqdXuj^`h;Q}2=9%jGnCHn{4b3l zqM9YIW4BYZHWIB}I^&74=q;MlUQCv80$DD+)C=V+8t)K#Ui>JpFg%cI8zHxBL5B5H zc%dDi>VcN{_xs9}Gmp>e6`9xP_JG@*c&IBVtGyq;f@y{rHQ#APL4fI~^@zs+F0pnY z;Y)*2Y3?uGN~}nQhzM5q#IMMfcu?mi66j!nQCFbvDZHi#pw?nuFxTRblwda5F&r*U z!mQ^};wVKBry>KTx#(11_f|H_-mc)gm%bafgnl6C7P~k69%3BDBTNqzLj%qK022Z* zCnA1OTI5!(Kz-3M6W%n-Z`!@s1lf3Tq_Dt6?uotk*Su^NF^npz`Q_+CgwWQO!qc(O zxI8!60j&YoUNfDe7^X9?=Y|5=-K=KQJ~;6zMw(smRbQE?!*P`ZYrp1RVZ{b&xi23R zAJa=~ZTY>br8I0ln03FzG-|bZptIhvm2ir=F{<3C5$hNB+hKz!$)U`5rGC9szTL<1!}kSCY~?s2#8+MPNfN58;6zwbYXvM?Jt_Op|l>YrPl*! ztAF@BMq5;a_~@`MQwQN^Wh+iD{W*(``zGG;5_OMsA7baMC|$m8Tn2GarLF$}hU(GF zguZR&rXB~Z-A8*Aw{^~&>RZVyN*K})#vE=!Hk%gdzj%=fR>z1^g`K@hDk=W}kyKcX zg&fyTU(Mm3T5qZ~UgfRWRaP;TzY);3BiL8YvD_0@hT89txkx61qJDKWO+Kd=(khvf zYHbFy1){2h*>BgV=Z%r)!}Ak4fyOZP((IMB)HR9=dbM>3=*dT8@FCs|>ifg@66gZa zgD37HLfq-e27p?-TL)@_@fONE>nW@s>;UsM7-bI<>EqZ^TpFC~rz> z{gEF{XyMedw5r}FQ=#>Tx>?+hGcpk~p1Oaq8#bP3^F`M&K-e95eCrIt1`SHi;dJS2lZpUlCg?gH^$@kUPfx&11wKxMYVH&+UZi7*$11`Skj4D8x@V*X`P4S zFAd@3L##N|g^r*u?#UZ&`9tlsU=1g8u*$EYP*6vs``0 z1uZwo)hBSzh{OtC#i!Bn6C$L&1N782>ssmo@R7qVS9`6~*~2K{g*eWW8h=B-hdq`q zv{X&DxKnVad}&-{^D*X;ViIo2|)9LkLgS4MH) zL)^)j3SDVc;Q@#?Wr5i6A zwyC7kcV5|)lr?r#R~s3aK?fK#KPi8qr+i?4H3_SYbJheZ^RCqY03TCu{{S!ta8X^) z*Xno-J8qZ9Nb>N9l#iBDwv#!#J|;zgtJ;s{JWA)qy+*$$)UTlh;=)ozEj|dJz?gnw zbaS!$GwuzRb#*sAO5he@S^V8y_=DF-UoJi7;04j>j#jOGnnYR6M>UlAi2a3Kg$K4H z*3}qht-m)fn$~ArSME?8L)p?RTkY-wnuJtnf8@$yD!Nj{uMGH(y3P2_h?xfVKT8%1(5-1JCz#36^NxBpRBE&+uP0ub%>e-MS_2!Xco5X2P4S0M zBl82R@1y%4Qp2}`&59Lx!{;!%I9|OM`{EEdhilRLf7nXJS5;O0!o8(wda61ZeKr8s zobMcF6J7VWOzSRF-MUeaGv={FRP=FIH>jgJwqfp4c#0lX{{VL;Bu1K2_2i_2-qzC_ ziodcu0P~HiBz0T1^vrI_(C9(&=F2;2(j&|Qvfi2Z4NB=&)?*zQc7;d}T7oSKClt~C z#umkE5jC?K8VSZ$(->bqRn@Xnka_FnoN2eQ7?k z(rb=#-doci50Mao0l$GoD(<8P(n~iTJ8^@h}|=AI%r0&z8M4sP|!X90LU;;A2EQz zg?iy0RKcz+8kZ%tffFWl9fb=7sU$Fj;olV*qwS!SEW2s+y z^p=9K4vCi@pbkJWt_8mg3&yRtck?Kimpfo|jAbvENBn~opJ=;mU@5A7>FS~?%3k?4 zc@27vJ*9X+=eSH<54#D1fI5sR=AxiOXG{IcUsAW$8`i>A`eKc0X1qdb8bd>B;BjMB z*l`pkN$^^8QhuU%c%+wTNF6H)2C)tTeVyU$ljDXsx7Ew5Zl6C~VLjt39QhVzXkCGlKOtUum~kMbz2% z71s$+t`S>#RBQvxSmhxhOXTOE<{{XSFvcu2pvFp_A+C`cks6g&w7G|?UXL>ikX2gZzUoncXj6PM#nCm0cd4iB^&G$fi`dSk81AgJ z*72o7ci0%nW6UVTMHDEjS)ziCvdc!A^=14;H5P1}4}{Y~N4g6GyqVRF(eDs=D70~_ z=1?}2{Xorx8@Sn6^{oDO?Je4h7pghmq{!e4o7*gz*G5Jh5atb;fLQ8HkG5Xw1Rk!R zGWafC1y2b_afXIS&44S|`I;6s9Z`8y?xV_bzXy5N#d(7T*{`$at|T_Qzydmvk9bWm zO!4s0J=nl6QMG+GuYw=sVy_B$iAAT}m5+ptOcHOL_9J|&F(#kwtg!HW8Fpl8ka zj5bb9^d9JWjI|+LXo0WVqL$+|F>;w43$CaA><4 z(gd<(hMYbiqLzZ5)zz7#iJ)gJ8PRmLs3QqkapK|nt{`33U}(CpTE(wC+n+E}wcuiP zZtlY@(g*?0A{o0=)XDIEVYFx+SSZ9*nb_+wC6P9u6~4q^GK23k774P|^!#?!KSUTbvdob9X z8nU{sni}&Lvlm8|0P^HuI+m^+X5U0L?xvb86UJ^bVxcQaPsG8YU3$7oQnZwpQ0SIH3Mvjv%DGu z0gj*cVrLKB#n%{%#s2_!WMGXYwwh-3Kr|6mkX2Dzq6oH;MR1RiGC=_H4L#5HTa__b z=uC}?u8_Jp>WKG3?T!Bcuql>jCccb5k9|?dtutvuq0OlQ>|?9@m)&6G;NNf5|~A zoL^pLMWe&VNZ}Pk2+}t$7m?4|HIY*A7TqZ*=@+Pp+_X{EKB27|8vWWruI_%Z-i`tf}Zji&SwH>vuJ#hIci4qq|=|j%Vk7;pQ z1Xm4V<8F)qr%-vay`M~>Ua+=`W9J^AMWE!aY0%I9PcHSH#q? zPo;f6J%Z_o*n(yxp=@?j&!_swj;91r&B|_ zv>9euRI9AQ#EGs-(_LWHOvDcl<}pMJQ+>QuOE3T^vzm<~9r{l&Jy zxyE|_BMi{@br!(x!T{X*X;&m-RKurwZP;T2<_KihZ>}>yTqaRsykzwjUJx~MVz&YS z_H46d;Ok?opxM$gtgi2HxF;DirIfzgz=+}2wS7Bc1SK>#IOClC%xy8Fs+Jtwcz_fw z=B9+<(KRo!u8^;hMm#KXs>nyiC5#9lwUHA%)xoE9a_*SVdY49rmG7U4ern>cK&?KA z97=>!cx%Iq^A!3tIu&at1jLw@)1TmJ7a^G!)(K zf7pc-CztgRMZ?bU2e}yS)?Dtf#cf#Z@eJS`n$0C13kcv1wZi@%gqt*j9aO^(6cFch#hETP{dpd=b@yB~JTC{d$&e@IM7 zlxUY@=Gj=L-a`X^GYoiG+}%DYzKH^oQBIBHyd_(-#nlMQ?Rtc`f2Ng;|Ux zx}8aXC|qlRFY_E=5iy!eeAHmiBI>Ndc{3O#k)vAVb8`3Q8)=;9$IQ5EX;p^|-pMbC zXcJ^H{njC&VdYGEzSRV=RP1jl)oqDsCf?v*%U$4zl*U68_umhhb(bmFH2OfO1!(BV z?j}g~pq3fzS7}*|(@|La&r$@opjlYm&mWjpmE3;e2w^tlEPltl2}3a*x)=2nHn%~> zewf!Qb9FZN@(63l>$7^iW?S8Xz}E0DY|PmMi>un=NfZ~{t(2E;l&8!)ieSqSC{4x1 zLI(h7Dl*o`SSsK{q5h#NdR>2nL(67rk#mT?_+1?r9b&mJH=OC1LqJ`YSZZ}IXoxYY zcN656*NUBQF#u#CdAuedf(oL`@#jb=WHi`bmb|ksG)$F%9|X{8aM@KK7`(PoR7BS) z!b2#C^)G3H%Yoir%2wOP==;hVqxBSRkh^-zk+(>}Rc8*Vm%)R~42n)7MNw^MeIF6N z>^W#@=T9WLn&`>3J9U{XorglJ_2L*>08%9?=R*X$w-{rhO56|^rifIxO0!+I>U61h3PU!D1FsVQ z0BLQ}!Q%~ANY0Cx>rnP#?WbY_Jm7klFMoCFt@J)-EgG?mATMfLmwbzA)EAd2QBEv3 zSM;%cBQrzl^}zHMomra7LCq{Z9|?MnqMZcEM@p|TQU%?GKOxP;sX-+)(67E>UJ41l zGsNDkf9#tGz(a;u;tVL{s6|w;xDc^~0J%QYg zv}eph>OnJY)sCgv8i?4v`gjA(*Fd3Lu&=iFWEUpbWyBG6t$h9Y|iU zBXa8R+#U@C9ZSS!GQ>M+p5z?CU5pw0!02sfZ5(oA#6W_YP{r+VnW(`GjnaDhh6ke# zoLw({=Fq?}$jZ+0`GXa{5^7sc#CMhzb>xMhrCJM7=~(kE?{xuMo|IQ;1aFkX&5nYv zatV7#C}_xb0`!MCP8~l;Q?vnm&^1rG{vmqka)9kigouc`79RtP?@+8Mo=l$gm#!gY z2Mx|q>cdNIlWXU(EQ}YpXr+HDTaYK-^J{wxI*5R{?cj zFH)*+t&?oM9`Se=pu4Iz`*tEjBVct2^nA<)a~1iuT5tKw*aq{K9&z zZZZuvqE(!gmE7mF9V)-!e&C0FSAXn68xwW(rekg}#XOQJI4wdfGlP zEJbwkjbIq+Yup&b0mu{+I~gTP0BkhUV~y%#L3Ql|Q&Ua%K&cx!~i z&6$)jj9(AoaRmjS^3kVjzPW?|Mn$HLW2>KtBnr~%DzfyFtgC@*Pz&Hy_?7a33(=+r zhQxZzvK0Zcs>FK_s2tJ4X6+$;63+}Pa008bbSrJ^v_+=S&EQ!I<7d3K3s**{*{%6w zSXPv)V7~-UNK#WvOV^>SZ9u(+Lc5fCiXv-wswsXcA;O7@>TG<{cbbKWXs{aX2l;C@ z*47Q`5f-3RMoLec5<6Pdswe=GBB&Hx8dKEmx{EO%UA^OueoTIox)n5 z6;RV$ZoosuqbExPrK256_>Joz4$BWqzfn+@tAxI>?sqes6vu&lO819-jqhl8V{*w( z)PEGcDmuk)3NP}g>llF0y0lJ+e9TnJY8m7WPUPE%Mzvt}GDTJt^N@Y!Vw9Zq7uiH_ zrULqk&q;~ifa$}Y`Guefd|OSQF+D9x9W=}>VhYhXZ!RNYjw%I7v;i9js##Duj(y`d zQS?fAm0XSU(xOl#n_&ieTFmeuZR(#_8q`}_!K>;mTM6wOF*+SW`9=0^gCR*?VgSkLu*w=q?Im=rc`-U)Tt+A4zaSY{eYpaVILalLpe-iq###L8F z+{DXci&odSxxOF<4;w?E)tc%$b2YACQ=2Pqh-ACn6^$Tzu8o+0B3T?6#8Bi0Yi%vw zQick=U2%H+#z{-14PTi2BrFFYQ?C-i7E)+S!&g|-q)kOBG$t^bRH<3jVfPeB87i5? zTUz%Pd&}Z0D_se{Vm?amfxFrN0A=R~&P%`N^aD8p0jS(KJfVnoWTeb449xEwEIRiB zB_);)bVPzirL`i%4<-`hxnk|Ua(1%F+^J4NP+fD6ae*ro^0p#sTZr$Nc8G{WM^ioK zvlI(wF6~tIBc$14io!bYgloKg*J3og3hJasxhu?v1*S^AmObKz%YnT$rS3HNg}6EC zwrsA~3RQiqX=hQ=c5(F*rntP zJwE1r1k1Ctj=yk?XcMK=>t7QcKo$m7R!mfYFqN&B)}cWG-O$6uHbYPf7PEWf(+zUk zYgFE%yCk$PN&wgwCb}HMK()mI!G8YWl$8~H8+?64Q(&7K3hT((Qh{nwu|{gEU7%#Y z#4d{qeaV3`61ZGGwd)sPIR)a#rF3%%RVj5|;t*^HKhmMsSS;rS7twQ3B?wv@=R>?} zkSw)(a9kp_OMK(vA3+X=D5c*A+$rILP;(WsUv@ZET~_*TEYEoB9Pf-|mvWM2h0P3z z*6NG2b7});EM5xj8kC^AT88^Cxk*>T=+ONTO({yMGGiP!8RIJ}&9B94DA=)F-3>fs zxMB<6Im?GO9?)rGBYFmg`5=XAS<_Ju6wQA-9?z(A78P9MM)J)Dh3TRdFG_2S>%<{CF0^f3vX%oOZEY!SY#Yh87e z)y&WLXg10gQFT}S)&wKa$d+fd3|4x|WLc}8ogjwfXcG4S0D3`8Qsbr5P43I*FlA6~ z+VXoUSCTE~Co0_LC@3zrX?Nc9EsLQ1!#HY0k+;N6P#W%U9C3Jw+@)Uc_9h$zR`?o$ z;h=b5u-rnLwR+v9x7IjTl=l>iVO0>*zf8)stw#&6v_}GTmK_~yZ&5l2AhexgY81d1 zU|dbiml(wmRcMs;LGv1>s5Awjdt2%*L3~*X{{W4XU}VsVtbEN7mz*H5h^8al2O*hwVeSmni|-K_Td&EEHcQJ5ms>zm zyq^h)a4XN>;A#^PyI%Hwpn-$?5p4E-BpG558r5G;lEAG$$S}~0k+_sH+ z>qvt1W8F$1KZ=IXMxM)u`}ZtCHHy*N-~ml)I#qZ0(peR*J+~~VVg@Y7^DHtTx>x$8 zD{hW))z+J04&&&H3Bnq7e=t0c8g_}P)O7P5VIIUSp^Dh)EwK9S70cBE{X`H`x8fGm z1quGCTTk%@sIxlGC2DV0^#mv<|HJ?#5CH)I0|5a600IL60RaI4009vIAu&NwVR3K*G9_sM^Vrvj7`Cbf5>@}C}&BqWhc$;p|_nM7!6hG+1 zQ)fw3yw_JKtkukJuhua20Iy@e!OSuG9`Z%lF}0(5uYZr@1S}zle&Lf&x*wP8+#M5~ zho|wmk_i@}hf@SG=``$Wuj3C3I797hV2Idp2=%WsYLp}U6{q{irSL=3gse*G^b@R_ zgqai+;(68pY(qA9)@(Ovi8i}zeY*iF+_ zDF@sGjr<13Nq75-N$_$g*r}v{oCbx|GC#8fY`4k7{5X6aAL&0T&htgzydl zgZEj`;)`w9<^;Bxp%fHd^8_s`v8OE9I?aFZ2t5eiMFdsY2Sa1`0_|hX5ICH`9*Xh< z;QUP3M$*On^)Qwp0KeNc$H#bV6xwt52QJG$t-$ATpakSUkeBx!)T=X2hxlWHqqG;C z*Yk(^s~k3$YPb21M$}7hc_z7!5ri5aPfwV3W`__ko+-JVYR(br5n>hQLKR$!z5KtN zBZM>ujXHC>N+kIO8BFSpuEW_sF|5{jhf%X}nxfeurf6k@TVS}FC;sKsE%H}eJd^xm z38AQ%` zUCJ5j@%IkL%OZSSJvjscPqoHTQxwG{e*1{XKZ=FBXWVeubOT5R>F#MjsV~8civXwC zY1|KGa{wCNKy!kM-_-vASuB`YvK^=K{;{-a1zo$uDER6jL=>w>xH(`RsGT4q@y5?F zB%lG=;k2VN;}r3H0oKd}itdtPQVmW_GlH0UNw1@X3oMjqpt?b~8nA!n zp>~hB8{)H zH;uAIMMQVvV1-~`x(FgvE*6&n&;(Ku8aUI;e5H!2{+JKe8o4NhW_b=E} zyA`Tx;_7Bv1;&67&-vu2RYdHdCWx+~@}YlplG9*+HuyN=kjt?#n1N|0D`K(F@fv5c^VJ6B`mFR{mdaNd|(aHH*xv8 zB>GV_j*}Aw11MTWDNo!fiFgVq3{GX0HG5@^BT0 zqDMd;sI+kq6b}CY7eEhg4wfl$8Va8^h-i935DTpjxEm<>%`+HVw_226umU(i~oyecf=IOB_Sc16U3m(C$H; zjevWL3Xf~qf=jUk$6as|C1QZ9ns?l7$`uXo$%uPvDyb_6NqxZ?nm>&(9kvMLbF&Be z*1HpvJz>=+PD&6u+1a2~@-nJ_WT>6rS_9T|dELeV;A^C8Skz~s#bZU@If_@R-Z zv&LOq>6qiIaWDwXImp89>WH|7#O7RBN(5f%$`&;*gJ{x4m{pJpk6Vf`4wsw%0P`+s z^YL-H0w8UnJBDDhh1kAn5!sl$(te?oR9H~#hV&s+xxs|7MKN9$OTRiZWuf*bOR=o z2~ck~OZs^Ef&7U2D*FCiV;dS}25Y{p%Q6`#*Y8}v@$Q=xaYlv#0JnPS_Dmfp?O~%q zL*?E8Ikemj71KWOeZ>@r;Y%6mg#_Az;1D<8@|~!RQg+RgUAzDxy!LbYJO>a`OQU_vl5W3%6UqLLRgmAT0^{;(1Vbgqtq-W~zAQFjPYuG-)yv4KfRU zOd+W`<$!MJ)~*n}0KL=r<#Hu4_UuGO(i83)BvMRtkdbxw6{2Khkm+m!HyA>*rFf0n zs4+0B`O!KXDcW*eqW*xeeMtR-=0Bo~ghqdPJlDwtHX$JUibH%CNL}t^!c3OZKDLVr z9Klmu=(hJtW*{{VVN=lQ*z>sKj{v&+G>8eTBkDGk`z>OAm{I(_WM}{aHFb;tu-P>V zOf)+CiY;jp>jdf9<}wm=Kpw04l5_st6bMRECLJP^eJS*w3Q_ZJDKvcwwb zJZFEeKMVR1{_^osc_0Ve==rSMV>FH;K$l^=miO2S0%&*546vnr*v7(hq{1gT#V!a3 z#@)Tc-~#TY0J4#eFPVh^Mnt6sJaF3T6Lg>F3<{K8+=({U_*96o>wV$mS_ zK*ks+vIu-9Pc?)(ImxP2pfH1tP|#@QPoaOj5x@;l+x-D@M|nrH$F3gm35aI=E4;5r zl9VM-DMHL2>^Y=12t6AR#jBxA(L_X7vBj9|{XdzPZ+CM9bi|-t&C8Se!I&d*Xyw_9$*@!HiS}=*woezTn1CBC(Yp4 zjn+~!uE}1^2tzw1U?6V*(YEGxPCkrzhR5N6E|IWpdxv8t>iQFFG$y-LI%)v^v27_O zw@#qn>kwjv#Y8P1dw{^(Aw9!Ns(eecpSl8z}GPGKYIszgfiJjAIZW<_^cw&20D+Wc_8tI6@APZffC=c@D zqaq#NwEln0mcT^|yhwxxjHx}{-mv$V4GHYURt5Rb<2V93J97zN=tg@e{{UGF42#O> zqYSHDgF%~ln?>`3qP2mN?SQDC)22P}0Rg9DxF}{_vlCFF&4KBgO$|yG1JYlHG6FHZhD6EZm`RlFKpI<@zjCkAdIJg`qY%n&wG`u};*F;Mn9@7MBKtGdX?k3RpQwF+qVqjvj3eMkMxt9cR zj*CG$gkRh4NyaRGqFJ?R6E6+6%)@hVe_215$cdyyIUGMueai*FyQG-!0(uG0LhSb= zmMgs%!l)Y&nWlRN3cCTX=Lnry!OaY+5_dL*g}wO*LVdYO_qpK!J<$edUi4_Qgl3Y2?~ea;Bf z!0tNa!Jnl=DG=akG3(NePYonp{K6syPl$mOQp#szBPduKc%!3$!EfNC7jfClGgl!| zV8sFYV$|E)&~%cB_X>h^6&>hA9ckuW9hb2}TP=L1J>z7`0d*BVKbRe%A>~q#5B$c| zV~iK=U<=m~3rPE3$8MkAS3Yo9CKPa-$cuz8uXV*b0fv_*A^ZY`09hN24n%Dn1vTF? z+vx@b2{irtoiwcH3_LzaV_ytC{&YN1k_!WDCr zl{0h9-lf3e`=n3IDB03RhLz~$n)`qaO0?*v50B$I`&1%0y)HuJLAO@~k{+Oy4w`cb z;pzq-H~#?fjF8_=K##9Ak$)y2p+!KA!HYH8Fato*d@$9B0K;bX8h7q!9V!yGnyRaY zbgba3w`;%W1nqUBNvJ2myhvIF#=sY9e_1ZV1cRvUA%Gh10Jj(nyPc(#059BD`b3FD zePO&NkY@nfeT&j_1hf(0C;%Wq-tjFqxNi_8JjTyN=-Ge)quf#FEbD`z`a8HNY!_wv z_Y%!gf;xXNLAFH>M_Gb=5oenZSdnUn#_xAKb7!94@0`&+OrLDv9h6Qjw8H3(02g6Z zRheS{0Ic%B2fa1YmPk73nfGh|RYye>+kGs== z0}<*$A*>$#CqtoEk3h?|&(vtN+|-`9bYFNy{nO9u0kKa!HB~^hd4m7*jPnpWuhX4z)QOFVV4&tfE zf!l@1w;NgG9r_f8!>cmIK*nHJ$n39}FdPNK=FC#?#d$RFV8DS52aRHJ?B(mfWm+rw zjmbz!2t*A#02wNBq`kMK1zTB_kFGSL??}i+mla{?>Lnp=WcEVGS|c9FmbReeBR{K36Nr9xmP*j7S9c0c=+xIG;N zUO)GSDk|t#-|+4~X6v8@bC30bo&i=>y?mZx*F`#!sIz7TCfc#PJa+B{Rsc{4Gwrtv zkp|TqV5P8^rrmJAm;$DRt(#Sc}B}3j2bh@S%oHkVRc%Z4Cp-T3}J5a>G0gK%OMu%ax#nwU7ii!Q4QNp3Ji8wO{4H zHhN+*jS_+1%Y^zt2&mTRg)Vnv$|OLq6??pD#FbPANC>0T%t{FujS?z$iQ}v-CQ<7n zxlr5g49tdwq{?WXIB;>r^r36uTqTKy z(vm_{S8gdrcyDYrlWAJVrtAu5H~9ouOjDxdRGHxxOeh>lCxcrhfXtI9Ey*);N}R^Y zK16=m+~oB$VeKgCJ9=mBu>qnp)VAKIPhg!@IZ#j)|MQ z9+Mi7$d1^_1f(Dzwg`O1rXu8^n=Fz4067HWmJ2tYB922r6-r6~y97yyiDtH&s@2+; z0msr{qev7Mvqi;tQSGFO)A;i>nR#pN6TQ?o<~6V;shWsxXP7n-prRm+S`gpgZKBq;}S*SM{uUk!SLlo^N#;WdcNqr&~fOR##JMOJ@|Ci4uEXbBltAU7a4h7GO8jIj9r69dVD`WGU^vOks)@(50@ zkhQ|=x$NzE`lWEzgI5cYZp5Hx*A)J+X-LhMbq!Nv443wcumsxlj=yntb_Y=u0;hK_ z#t0p#gPA(=X1FJ0j*XBgO<+XS4vch-39jJncv8TiC$U^lHIpq_g>IVs#8^q+QJWe; zS%oX44koL8$A7t2pPK<-)z{8o)^~pT1S$n{&0z*EF_~x9PSHS^6Dx-)i)4A(geqMe zE5;-bHE<3?zyoH$8+PMyE6*#bWT$RkeH17ZFt9%PoI%5-BwMw?fXe)bkpBRNbO9i< zk|qA}*j;k11%JaYz>A2gtk_H-k_+fAPv-@~Pz4#FdN?$@vQxDUyfeBFe02$P<7nP_0 zcy1t>ny#jGL_}rY8+QKy-i9bxrjM8uDu`aZ(jXgS9youXIdmi?64w<8)Lb>N(x5xY zrDx0&qAPdYaEUH&m_ZPzirIo-lUj#kVvUQw;L!L+nL6Ko8FD{VfefYK9RlDgGgHFJ z!$%W)j1g|7C?!}A^225&X@g-HN!J}4rcy~@#M3y!@a=Me9JK07k7CYGT}lZ)9_uJP z8>IOFABl@yY{pSk19zOBz@rse(C%#cfh3}|bc@*?F8hvzt}8Z>HYGM<{GmYTFf5N| z6|$vD5=0hyIbNa=l)s!*gPW&B$OX4eP_mg8-jpGzgwwycu~K19@PSsrvv^QV;L$WS zFh86coG@o#L1E~#CNr7_7(~1(1As;=+ju{`IApeX5Ai$saC>Sh+gAr8c&lz5loDc^ zd7h#%hwMbQ_2yFyvlb);3cg8;@NGB?EtEVc{KtY93}p@8L1yM8G!E)oMH`^H<^n;} zruhLQV#l}-ys17bm})w9AlDzPSM;Znc-CJB+eI z-_enRZ7{M7Dp%a2Sk2r4@vy`bE+7KI8!#Y4fdRAT{{Yq|ylfW!w}E5SVtZE!EO%b^ zQf1Nd5XOA|FdRVCHWQQN!y#yCYhGky3fx~#^*yb|aQkr2J@#7#^IUq(;>6kW$=@%I3MHY>B=v~6@u(1KBCn{1zp853>j!ktM2)TKfj9@ykt#x1cD4k&)lUW(ZwY4 zk*Ge@D#T?eC>WQ$9l%DirPZY_qG{ty^AH&O!y&|4CDsnf^YLv(tsdMpyfmq6K;jd5 zL59mAs)w~hF=xwE#xBplR~vCy`1o-JA2HL;%UUH!N+u{BEJ4-gz#B03ZtDn91!D#* zMA}7klviWSBV8MfvUroMITKou3rfwQ97AEUCYcCoZv?=URG~E+FK#b8280EU#W&;Il4rB=Fz4yZdc}ibJjSX zU^`FufKZ7L_?S!uYIziZy%UXYVN#R*7;5;6>Nnl3B0O2$=8+K`*(7E3qt7YSmb$kk5@tOVdrxH0E*HyQvr0nnD629P57V{ z7&vy8*r0CEoqQ9yx)4+k@rpErmdL7sO?!r(eh<=7A-6UP??`wtZSw&)x-|rrZ0{j} zLimmIW~vpu9kM+kF}?jq2x(ym+*b~5B9ool!{0Pt@Rt^DWT{JI#scbiF6NqUC&Th) z%6vXz`cHa#HAg z&5!1J14Wd5U}hb*l!Xr3R~**Z z3qDe@%kL6w0GAy^^E8l1%rpa*oLVZ8Z>(A61AyfhCLvB?kF5Tp)$II+9~ch3&bFmG23NtHtl9GD3CPJjvC?* zBwt!P3L5t)n-Fa?jk-C2ELO&)R{;{2xm!lT(6Drl$e!W^lq*M8W+Z2hKm1^k0KoqMjQNXK zx}W3CVm(^v>cr!5!_Sh6J6W{Q@x)0PVp(`-2KRbq(=T2&tHOZ3-0EzX8Gr^A6EY6T zmeBPwH?m{tYD;i|HVMppD~5nn0VFi2;kKp@K}1Nr_ZBJ&j|NVoTQ=?yUQlMMYeY3m zh=H<|Dk1e>^OnFMk$;RERgsF>B$2c|W6~y)2979AHwQNqcvfngayQS$1PMhajAvF8 z`vM3NU*i)OvPD4A28dHAzKEjL2FQljMXUZD;(*4$Wrm#; z-FH}uk0rtx5Tg0!3v#n41R{prG0hlax=rLIjo1DipSeSNf%^#ferEIl6#zJXVuDZ5 zNCa!t(|HFa!0;m9-Df?bK_zHGE&j3=ENhv*kONzW^dLW@=db266hTEmnD0=+T+(wT z!!!Ch4B7KLaIGo6CQ8wsy8;$7jc?2m0Bi+%#i+WuLmv#C1p+|`J;oi=!y`6r5Z*zf zCTMQ}KF(wm3Q2n~%mbgHxB)Ngjwp$g96BU=SRz>QU-6I_-$E@6l>i9M!gtzWkB35U03M`(%r4ieXK|$X z&{V${739Y_pGiELtqnBdQHErzwO~HrDxL1tg><)}b&)DAA+#XyjKevF{Ro2& zStlbDi4-c_y~H3=kt7r-AD9pfvqCvFIU8J1q(K=cH3NJvTt>JI;N;Xafa@KciX0li zP#LT!AnH{T2mm(evmiC#lSCST(o1)j-dfZsQb?Uw95pvrm7^9$!eDX{h&)58UHt*4 z9&cy?0R=cmT*9Rst8EiO55!D&G!}hpp$(KV@*xm#+9?+pEw6&+>tuny^Mbeukh(C7 zXne?0-u-BFV)1SbxU;$yHk17f*wL;F?*$d|WTueuIT+~Jhb*cDhZO^|@_o(o>=}nX zr(%K3E2%Vo1rX4hb~5tdZZL|WLTs=!!E*xY5em}}5VnRuiINWRf2oAQ0E8~p!T^x; zKS%L_B)+OYeUJ5;&<6cOJH=Jbl;Cpjcp_l@UbrMn1*e!ILjqX(c7K=(u_ysw+Hl5` zad##E0G4>VR#VH<`Tqc!*ipS|eOJuAt@B;-JJ*ao?gR&L_m+Rz)cnDLrUG0bD;E9t z1LEA|4rxkR{&C%-a)%Q!B8@Q03y$Yy^D&#dZydJ~wh;Y>S1<1|HA|qu_+fx71-#%& z0zDF#sJe!?2AYMl`N7ruK0!mIfp^H*nw7*gRwH}E(*Dv)ASyPdPQ0>OX*vyhiG(!F z)}|z*>t}HhVBSD!!v@ISIcsweixhrq6@?1MQ1m_g{lN_q4g9Hr5=x~x3EToSKU4x= z<$c8VpwISwceu90NMG>(0NeysL?3}c_~sh9wxupJ0zemc&0{qZlbr4guwUXfHqb_$ z8^veje?e)7-sF_C^1&#U=tOXeWd$Kpjc&Io-`ayA^&|Y?lm06akS9>!UZE=;8cs|_ z3J3Ddq*21JL>UM)WNAQmk?dv&T?u%Q=*S~>f}m7r(k3R#ril?fjVl$%@Lse<6eymV zh6tc+PdbQs<|fpx&+zY%`Ra320J%z$Bc8x10wR&o$e8OiI zsYMHF+3oDd5YN;~hz{37ym|5Uu-i>{UvprQwVV0IH=^=|er4#o(9;5qc#3xJ0Lia@ zH1$J0AC_nbNrDOfANGSbOB-#rD4MmYtS}UYM8!d&3Mc5fdBSKPNvQ|BrfF$PyQ$!g z8evHlgM#oVuUu(g*SKNoK)u9>_RbTrfYjzK?MY{CQEyWVK}xzI$F3geC`rCu@?~4r z06OQmpa^R{5cnI8%m}uKTd3?HVWM`u1F|h5p2>?Qvol>Ufuv?k+PadJ8am_V8_}5) z5L6IH;`xIF*%9BQndpu@^P54l%>b0{3c)!XT|pcf6Ez7+kFmTju0QV_rHziv+eodq z2zatJSFKvWnRe+E#YzB04%cgD@iGcLYq3Ah1(=IiRHsjuBx?Nv-i0xOflzsfFcR=) zqdCZ2OImmRa~TtWQsLvxWu&DEc}@Q7CoaKE1Q8%CkK74m5Sy@`V7qb>xxyO3GZi*r zu+r#tDnK9$TaN`7qQVPMcM;xs`RXmxST}sbNZ15K;^RF_8WIW(mHjE3JL4;27UFjk z0H=}Aec-1iuzI^A6vb?Th7HNJYbLz|H2!15!XgZx!fOG8K*kR-sd^%Pj#YyA@D_WE zVCA4_i`|v^gOQymC@11z8e7odu8OI)j3t4zhjTU+5Y={N8?r`10KWeKc~{_QPf+F{ z{ZNg?TOpv=`ODx83xohW^yUEvjR}wG>5Z?Rs=i5)qbO=}VmEw8*6RXnimiEdIE@>5 zjeco3ecC^ZgO4lJfdjh~!bPXCB9Un9Oib1Xq$?@c>49SabOUtexreu$xC^&Tz^aIz zmJgpfk6LjDQ0#P%$%FL1hZ91^y56;i*aSPwCA13|9bdA4v%w;`$pVpH=wo?bwYKsI zue!yBXocc8?*9N8zUoeyS4-eYmax(SaN~c3Dip^IN~%3Kv-CwI#DT~JgAos*%xUHn zstDKjddN1EU;7zcrZT7sZWLusQt8;4b}u90kmK5iy+_FU+|h<1HoCt}zh~u}$4mm0 z7NfVl$LZC6(~K?IZF?=!NqXA!H_UxP5j`Nc}D66nSS8&3{O_+KH_kr1T7!S_XVNr2mJo= znHV&N>;B@5PIer)Ho=c_Yn0glALthiue`i`?jH(60pm=82Ce6_| zycF#U_b4?Zb(@ToN`4>F?j)tCgy0&3c2DOOn`Ww!4hBOtS3ix#eqd%?tVP)TS6jm1 z00e+aKb^z|bn73s`2m$D%dW2YDhvFx?AUZhJ6} z60EU`aGDSY4{K;G?#CH3(!OFbI%U6slv$&t2O~jUHy?@sz{nIaEG5&tH&bbbtms}H zKI2+TfDrJU5f+9_8RB|(%i1o7H01Zd1}ZP$#i_~eMM?BDTdrjcisOK);ulO82;l;h z!v-ZD;t-@UU%c49Pnb$>zyNlu-^`&}8lw64cpWkZ%{^8+z=pPtzTYt;QCkR9`bWRc zaC}e*i4JoUF4|Z(-Qp^WvSUW0g@n~eT7KtPjSKl7&)f(hN;~by)2IXg0PBCo4uHgk zzi>=N*~hrGZVRF*6Z?J2X0%0g4Y&6S(*FR;g87YDSe>sqsAh|%d zRxw1y=MoWb)k8p-f~i`7JpoBIybZK4;C}*U?cp7a5j(RKE1F|4HFvjJ1>r13*`h?( z+1$QjHMZn@G9VbHa)^F#KY9MUO!MSb#@<2ISG~fRKOb5WD2PsXT`J1XwQ=$OHPy2)8 zZCs|J8MF&GiAh&fgCUq&0Or)7k48wMoI5bJ`)sS%d+XtXH6)!${NMyha39<2W*-#- zE8rfi;f$Dwj%h*5omO8G9{&I;Cz&FMYJO-KA+P5QL!DFF=!(*)gN!WfR)MpM5CS&f zPBJG)7r;*@ZK@_5nN>~qIZPgI56OtYBS-`$6A1OX6Yoy2i9DehQTiAJ17S!oeiml4 zEAZLKLQ5Ltf=0J$x1+R*DRnsZ*+giY#|uF%)9A zTpi*paONvYIx`S;2*68&V92TIh{Opo14l4Ze^!?h(@L{^!%7GA;bOJf*{3*2Wjq3& zH*4GyagtsTdjMKM!+H$SmNym8W@z4PXdadoj?5Qx&~~AbW}S4$)y!0|6TvC`da%#yS9`z*e*l6-m>{n{&mrET^92 z%o}-+{{S&k=;(eixh_gLYZ8SI+!M6u^AO)Iqg5`}P+BM?EexvNDf);xuC zuDm+A3Lc~$jGD;cr%b5cllj67uc4T6^fVk;_-pLzLEztT%mUyNA?`6k42g$As3Y?n z;4K;uWEvab5eo8 zAX3MoM^D@NS<5VV%9E4cJWy**DS-F=dw%2uLWWVeIxr>Gn0L`~ipNm}?AA{yF*?4B z5O%@9Q?B{DJO@o68NAn#Y!jb@`7Tx(3cLUjZ+*x7c?5v95-@sX*3pIs2tfR3#xm>P z&_MG-^Ay1~6GR|Xzq>K5YJR((!=K|7+m()BK#m&V{{V+#@vE?J3?8S47$WeaSi z<~8n=Q<|+1RQ~3(UEpFGc5%aJAR#%hF-*EZns)Rz{p5h6K7;*XWCT4N&=teF%BHc1 z*j&=YD}qX;6%`&}_p#wQZAn1$86G7XK|yHK?h_UiGzZL3_W*}Vj@TvraZo0tvnB*M zgQw-ioWhX-3c@}V*@tu_wHk7TRES}T7QNx3jfw`ZaUT!+dw`Z`>U7{>$aoF`;;_zH z{i=_o9B-cDm?rQ^B@|E!8o`nAh#gdFjY9)nWjX}@{{S%r>RoFD)d#Ifjcq|duPg2c z^}jv*=DuM5jJhA#clRO=3b|@l;h)wWt5@!z#H;H{;-*Gk54h>qURF1l02!l(3X}oi zJjHEeuMf=FRVWKx`>YVaI3~;>1%P)50BLkd_w#@25P)n@1FF7fs-Oo#DWLvghzCSq zrH<5W-cl0Ew}+SwDcOi|@*`Y>Uh8e*peQUFjxcYdqY#3OCNp>orU;Qn+}!D?BI@vm zI&r=+Mv)O(so|Ubr>S%eJvD*^BWQKNVvECqK}Uk*W<4}XY3BEV+?=gI>cIsZ%@`=C z04Ft+&vgI*B%)m>vjlD>^kkJ@X4|*GuV5WLG4Kw^`Y3U?`pGWOGdihlIt#461!5&z=td5j{xOn8il+`>%r%i6|S$qQ8 zt}&ZhJHinG!cnkEMk#Uoy!S8-f{=J&8TnNDla&EPCYy`|lCivjLqqT}5B0nesPKKj zQS#=+K)_Y?hGLD{?L}DokMTgD>gqI0t<0@sDs3P7#C02bokqhYuCULF05T=e-|Lvc zm?YeQdA0s-a>Y+BO8YR{mrTOVFGp=;B%(bmym~6-wNOLQo2n$BY2{0Ar zI9Gba%+NS(tDB=Yj{x-w@{0gOJ=SfGxanA8CA%m2DO_lzh)@1!o^q&nZ>#{&vA)Bt z3bEsn^_OQM$?#Cu<^miHZ|c~^B{Am9PlqwjgA8k~K225brStx?m3g!Pv~2yz-Qo?P z0<0j`BPNTT1%Q){Z2B26_SWeDr(D4K#E7P$Evb7PfpsKpd92ZgY#Tr0L`K(6(#>8K^qd0JJ zpaLh{I+U)eJi&Y#&^w37xK)McNooH84jBOsk>|!mZF$ zwAjM`0C@iZhqWTe2555)DJh|_X&V-`+!z_4%0NYr=-iQgRs@n`zg32F99TXLmw(AK zZi%9y_v;+j8I6E$28N>!;$e_Ns>-69aGqje!mFqO=zf^=gK>2Q#vq3UoLzS1P#}Pn zRQ~`|7nXjR4FGz~(u!M`i6H{dQsA5Bz~vBVP}7#zC?$XmyJ~+K!m8w2&WqIZHqsV> zIN2?iPBXS07D(_P9W{lPvW&M^U)&uhF~HI^G-LA|aCs41`f=qdTJMK}p^U7D(a>~6 z8=IJ-%hdI&ujd}~d$xa*2S!wyuqMcuOwMa!>p#3@(xhrS39C{VhoL~#ZP+N=mJ z748=VSAqF-{ZfLa6dDRiU3nXt-i0pnr8##VRlY>2qdGIM2*Z$;%ZkLT~ zjGb?0J(Hg@id;u`1>vSx5hJjxwmEhPN?ldB+;ULZhbeoUD7pn(9_N@A8W7_>ffUzyzdM&Hx!Bft3n0MK%lo%AaFruBiE$f56!^zQITm@G9i zLk;jbKt>^cWl#xM@BaWX{biX<6&)O&;7V8lPlNXagDDLH>kg?1jmI{ANQ+{BoW2-L z9LIo^e=>kqd2~jGThu>h`00PdJVPaTDn zN*?UMP~vr>3Y(|i)3gk7UN6Ktv?LRbNg8N}Uixrq9ENuU?A`l^(@p&CU722nMIliM&NRnIrAdRdxEp zNDh5Am|GzyLj?H!^CFB9#?enWVCV?|3Aabj>kPJ7a@~wUWnLK7rq3a5>JeHykYelM zI&_MCGv~3?ASf=SMR6u_LfEMw!K6UV6y)@b>dSVV%k}YU0RH5ybFfs$*D%i_r_En9 z%x6F)2q?T+?y#f4;K+}ylL>i+VQRm*G4;U7C5F+kC1$d69K-E%2sNgzSJZw!O#n27 zF7k}keF*J=usS@(POCJ*&`3|GIq*W(*$YSO00K-3pf)7WZw$Tpggw&NQl8-!(kw$X z1LhH$>2Uq|mDJ$2j^dyNAQ{XEJcCxh{L3oV0|U+P6_f~qo*0ahIl5{6>+QljUajAl zThx`{mmZNQFR)!L-i967JwDn&A|8>BQzHE+lBFyZ)x%3nfW=m>E6w553uPe$iTz*} zYPG5q=^|o*p1~{vu0CYqb3H4V2J!rcGbG_z_!<$dYgnEQG(uf}+!(_2D0sNy)j~$Z zTW|!Ccp5Qh^MYb)(ONakFUZypw>m-~7yQ5TBW+MMfe0)`E)_y0ola< zhVhcCR5We}Z%Ch9jhWhCl0g=LG3hRQ097R8^8h}|DmRp`Wd4rF~-S6!^@1DU=xNQI~`zh zK+m~Taa;fzm}>y^Z<(t4S}K{00$C8TxQG?s?Wrgh8!(hpL_rv)ikn;vzE`FdVaOGD z9^x^UF<@m{fK6AqCkuETS?fWeGVYfm0uiJEJFVM|EFeY@8v(E`ml0bpNfs3>Jbc9k zK!FlOugiaOrYbcC<&DHNa{!9(WrA!rm+!d!O-bi)5IxS~s5BLN-_ws7Vzy&=DPJjp z%w5vDe-D#;jRcP?o7w_riM-SWInddKN1$SP!R$Mc#an1wg+Nm5L<*Xp+?N0-)QG(c z{o_-r0@ty4T#;KzR;NI&{KO`upiPM=Bo6ltp^m#^PkT93i!c5w%bz zU3nOe=zNc6zGT(*@{ga^9f;f_FpSo1JlIsCJ5VmK8T@+Y9)d7DdxcsNQRN{r8s-M% zpzu5^0UUKR?si%ss{{VoeGZ&a)KX6H%5B(!N@}d#^gB(je5Bs~ zO_APKY1pR6kKSTU2V6V<0Qtm|x2!(|ElRfv6NA=;R0o?DOs=QS(1Mr&X7g$&_eeE$ zb^F}c6K$4A95qnOa+GtFSt=S)W<69?(11x4(XCwVXYBBA&>6Vl(jGKM3Y0Y@U|zwt zMJaV!r7kr$)5Ye67=bSEX|^Si_H_pl#h6dy6c(Gnr7j||^@L57q`f9=c3qxAv!tjv zn>VH!D)4oD#B}1YVFOhJ*SI~;Fl_J|Ygf$B*fxNY6!H9JlE{-30KgUexSC#9$}B=5 zcXf-Gu8^JF4}HQQND2$}uP}wPodm7?f1E2KR*mXV`;Gys6OaDkprYB1jyM3095}+o zwcDVIz7+Ef0nA#JP$*PT#TzeIQ(A(cU^2yS)`-l~B)1sD4PH9X2Y2{uFAtsI{uswR z^lxJ;(4I2o<-DcmCQa1Z>mcP|Ne(sq?8p8k4FDIL#9$j{fyDl^LIR}9FoJGhpW_x> z$7ufm=0zadPn+%_VD(;RgX)6%!Pn{V3<{tU76d>hw8X-#Mbl2PGE5W>vxxK8H4*$_ z^Jyp)R~PW~9bBai$IRF@jsjY88w%Vz7Gj_UkspF!fS~O2Mz%_&aW=)7@OCZWWByiR zZR`S`N|4Wm1CjNaAQVgnyK``(u|FDn!85OZCPumgrIwk(@&&I;D!nO;Y}z$)E0=Z4AOpK3l9e7qUp*7Y0V8M zudnL~kU?Pq1}sn`ac4_~*moj4lyEu3eV~J2Q^W3Glv$lAT(sDXV1Y@321ib=Apuw> zBTbN(Ukd<3U&c30H`W0v9vjGanH~U$pa7eQU_Cj_1PKPmF?KNNm zy#v)buS8VT!2lYL<^aAJR22$Dw{&7qq_q~X^m>bV8P>D)Qd#=6rPxQhEjI`gg|Z7N;}?9O9JCbSpi4{F~dVxCXj0JTqBew!7;EZ zB_P4fI-}k@*ryoS-N3}o+FBLk&?XJ9mkSC@x8ydMON|4jQE?PUJu-qHTt1vwe;Luf z?J5EoS%{UTxD!iX&10yR3Y+y`JSdZ4}M7 zMKabzc228;ff_EA{&J?kL_HAi!}-FHASkFXc41@^1aJJ{VFBX=f_%puHF%8&q9OT> z*uDU>Kuo{D7-8W}V1x(yW2h|%pIH1GM=b+f$SGv?ouOT{8XJYY0>CJLiA_fK4|8Fpp$ZRH_7mSE%CZ@Ii9xBEbxK9w-5Y zU@I${aR)@-YyNRBG?XF)Q2lAGm=3e{b)nIM+&?)2saq!`0@;B>)dk@8I#OVn**S?- zbVu&-&=*O2Fr{grK;k}$e+hsn4p?m-{{VR~%Len_z-R@g^GHvk>am|8;6);@mX zcvfdas(}d=od{$n7ln499j>Y4JOjQf~GhQi6Tr5pJO64Xc&MuPoPjJhi zu=+GcAb+e(rsoUWQUa1(W+a<$*Bhh92YpTc4DaL90poGJ7wby&pqs*HkkR!if^|ER zJN0^r+udf5nA0iL_~0(Se8GiWR^Sb^_PWDB+K76j01o!xW~d^Srhs9r2B>?ALC`?g za&=C6GOJKP+UiUxLMb|nx`hMoXrX3<`ydXm>o_Xs1Vlf}icACwIYvbY2gk8=xAPdo z6&Ap$wt@jMLVXYH0EKlO<5ML%5rEitaYFs80Am&?Zgysh7y#s}4ko4ZD5D=#H``2; z%wVKR1*i?(zPMfJcBWP?`aHQfr~#|4LW0xz#$EK@G7Z~z-TRB+Lbi+~M1obAF;S}s z$u<#!6~R^@taLD{m=5=Z$+=a4tWE1*-X`XTL+8NRKH~HkS;paD%|X1&VKn}Ve-sfh z1=%ykZI!8F^6w$f7~q^bfTTII6X7A9?pTR7X@HBuf$!Zo{_OTeGQ6F zb90p0gCGHKZOxvx)F8%?D)|2Z7)$j|4#vqKqtK+sn#R~sR?-$?4%ov41MnC6!oAl6 zgrq7JbbZR|6@$VJfK6$*RjPv{6;DgJDPWgS3$J zEq`uPWNep~27z2FikMpjY^IT$fFOlZYhsNNQ>bER*Co^uU!tc;$Ud+*a0k2HHC-tFe1(k(@y8Ah#9mC#WTr>o50jv5S6o6 z0^$M2p4Y|GmrslhBoGzb_a-|q2PFa!M;7JH;|7NLRn>mwg<#})P)*uV^9>xpR7H3# z;aJvy92EW$9~!)c%2J+1@(lB9FgKrhCeeC9<4Bi4s$O|nwiMz`Vh6c+;77V5kiX{ zf2jcn1}5OCrb=X?$fR3foHGim#8DnK=xI70^ziBZeQKaXSJ5h!LIP?t?0R zrlBZ9nyo%BmdAgJ4acy{2;wD;^tMG>RMyP13L+M3}ApTJcZ;7#Hlv40%vkR3AS> z&1CKPw!=UGtKct~6kM&U6(Hd*vH9zYs67&it>cM&3b7jjwFFS!Cgv+r0F6sZ0cESi`g??=SFI*nx4_t(W)Rb3mM~8`IyBuoU%`oDdk;u-Bnw}dhNL8f zwo6i$F{T*?y(v@zl(KgWMff(3Y;Xt%R^rHnkS%8=ZvJE@8uA93f~2oZDhM2a14L*8 zH>m^9h=0Qb{{XI(fc6DE11l^fcF$Ng(`3TYF^-2FQ@)~Gn3JNYQoirN2rbSM#_5o% zF##Ge!CZM(s0M&uU?Dxlf>;T*bf5PkN~-Atq-L&M0VDUI(Ez zDXB1@6(IKk-{@s(!qV090~Jf)%uOvFSKGFGkR$6yGevl(EV zD&HYAsy7Md0Vt0`s6TQA+yJRq6>DZT=~Eer$|X&Q^Bn_V1ql&0_aBPl&RPl-L}>jFn7T8YQ1h$W{{T28S##mQhLH|e%)J1FNrhGugM_;S zMc*B&I@%cuAR+tcq!G~K;=!RJnWE6_Y{HW|E97Nx^LQ!{Tft4RfY;nUo}p*JI|okw z-ffFF!SI~pDS@W*KetpI(m z`@yU)He*kqC)|NJRLno^0@h+BzyefT#}M=h4eN3Voo{~t2h3!h7InM^i4P~ScrS( zFd*Ou2VZ9W^dgUja}y+TvSN5eMDo76#HEk-LvB>3T+} z6tGWI7tfYJqoLLP9^s_Os3)TV;#(skz<%k@dfKRu$iL@UnxT4SA_1jG_{MWO%EKGv za;-t&oSiSX3UxzL43Y=Io5ej1;OLMzuerR#a!DJl?rQ)K;3rc7lCE*G;hq<^6$^~hNBv*SzU1Asp zW-wRse5s97SZ}z02inc{(1Kx%dXDSS4tLo zV?yR|VG}G<3S!Ady|2qlbG9@T&;gKTNb%h7zXV z$ZzH)BS;{imMZ*khF%d$G;DpR=LZf25F*Mq{ox1+CC$hdOa}?oeSr~encpNSl@Gdh z;H?`0AZYU#il~)M%- zO9Ie2X)%bA?BbIN5gLh9RZ8(H5-^dWF7XB!EkPO}9pDE?U7`lhr0sc(hpS^xO=J0( zORdm&+ueg=H#-bRY)QmDTrMPRqW8uWT^@jS$w&ZbeZ)3IPp(1A4^^}I{{UQ`UTHU& z{syB)fjrvaP_fRVz%o~X=K z+8v#aAP50UL9k*E>mYerFZvvv%y6b}S}^ED>| zs)&0-tPY*VumQMgOghaeuLos~NE$RkX8K9f`M)Ty>o;hilqif$Quv+burFy9w8Rzn zVgk^*_$*R7aX~>6n=(i3$7!)1oz!I>Ig#k*&J7?wdrvX#mL<3o{IB(r<&Gp(pyR_8 zY9yXOMu_<2DO#_{9}R!U1XwbY3ZukAer3scPk2#N{P0DfUS*^q^Y^%k%0qhRh&0^O zlu&mCQce*TwoF4ai?Rhmx¥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 z%0A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 literal 0 HcmV?d00001 diff --git a/typ/assets/nrf52-spi-frequency-register.png b/typ/assets/nrf52-spi-frequency-register.png new file mode 100644 index 0000000000000000000000000000000000000000..e8eaef8c041d9908db0ea44146f948d68685c314 GIT binary patch literal 70351 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 Sc$>f(EN0G~ks|-*&wm3g@9Q`K literal 0 HcmV?d00001 diff --git a/typ/assets/rust_layers.svg b/typ/assets/rust_layers.svg new file mode 100644 index 00000000..c9e55fff --- /dev/null +++ b/typ/assets/rust_layers.svg @@ -0,0 +1 @@ +Peripheral Access CrateHardware Abstraction Layer ImplDriverDriverApplicationBoard Support CrateUsesImplementsHardware Abstraction Layer TraitsMicrocontroller \ No newline at end of file diff --git a/typ/assets/verify.jpeg b/typ/assets/verify.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..ed4664578352b0ee2490c143057fcb9930d277a6 GIT binary patch literal 100961 zcmeFYby$>N_cuC-iXvUogN$@{4Il_Z2}nqZAl)@|8#DvMzz~v34Xt#CbR#J_NOvRQ z5NG^8zq-!#p7+GR=ecj@o*iqgy*_I%_N*QE?ey(00EsF@1p>gq0RWV-H{f;&5TfW~ zXAJF_$%pl34hPl!^2gQ zkI&hi*X*xcUJDl|J|8nzJ^@~SJ^)zO$JNZj!ODZl+{(tzS(^Evv6Y$0&Qh9LUqqc> z-SxSZt(~$T)Jn%sL)XI3!9v24SyqM#>?7&pK+)ztvNo@tlfAC^+(#-#%q_?*>ueT7d3)F^B zKte)-k6(~aP>=^p!Q<}h>|y4^md{a*@p*Y$O^;?uTrckzT;SYgRQf0Vm7{)a(bT~fu_-NVe;!b(L!ni(sC*UrvTQeFfis3;`<_vRN6 zP*e~X6BdvckPzk<5t0{E5ElDSUIiBmPbVv9kN@Pg{BK_2|4ZJ#ee2|kZCSwzYUgET z34yveG5v#C((ZrgA_!3s6j0!o5P2>vC@3l@ PBqk~^^juy}Ktfbpg84sqEdPfN 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

#|ZOhcOg+er;|b= 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 bCgDqB@Hl}&cH?HEvyX2yhzUOW|6Te&`Td`P literal 0 HcmV?d00001 diff --git a/typ/book.typ b/typ/book.typ new file mode 100644 index 00000000..d1f08d16 --- /dev/null +++ b/typ/book.typ @@ -0,0 +1,350 @@ +#import "config.typ": * +#import "mdbook/lib.typ": book + +#let sources = ( + "intro/index": ( + content: (include "intro/index.typ"), + title: if lang == "en" [Introduction] + else if lang == "de" [Einleitung] + else if lang == "uk" [Вступ] + else if lang == "zh" [引言] + else { todo }, + sub: ( + "intro/hardware": ( + content: (include "intro/hardware.typ"), + title: if lang in ("en", "de") [Hardware] + else if lang == "uk" [Залізо] + else if lang == "zh" [硬件] + else { todo } + ), + "intro/no-std": ( + content: (include "intro/no-std.typ"), + title: `no_std` + ), + "intro/tooling": ( + content: (include "intro/tooling.typ"), + title: if lang == "en" [Tooling] + else if lang == "de" [Werkzeuge] + else if lang == "uk" [Інструменти] + else if lang == "zh" [工具] + else { todo } + ), + "intro/install": ( + content: (include "intro/install.typ"), + title: if lang in ("en", "de") [Installation] + else if lang == "uk" [Встановлення] + else if lang == "zh" [安装] + else { todo }, + sub: ( + "intro/install/linux": ( + content: (include "intro/install/linux.typ"), + title: [Linux] + ), + "intro/install/macos": ( + content: (include "intro/install/macos.typ"), + title: [MacOS] + ), + "intro/install/windows": ( + content: (include "intro/install/windows.typ"), + title: [Windows] + ), + "intro/install/verify": ( + content: (include "intro/install/verify.typ"), + title: if lang == "en" [Verify Installation] + else if lang == "de" [Die Installation überprüfen] + else if lang == "uk" [Перевірка встановлення] + else if lang == "zh" [验证工具链的安装] + else { todo } + ) + ) + ), + ), + ), + "start/index": + ( + content: (include "start/index.typ"), + title: if lang == "en" [Getting started] + else if lang == "de" [Erste Schritte] + else if lang == "uk" [Початок роботи] + else if lang == "zh" [开始] + else { todo }, + sub: ( + "start/qemu": ( + content: (include "start/qemu.typ"), + title: [QEMU] + ), + "start/hardware": ( + content: (include "start/hardware.typ"), + title: if lang in ("en", "de") [Hardware] + else if lang == "uk" [Залізо] + else if lang == "zh" [硬件] + else { todo } + ), + "start/registers": ( + content: (include "start/registers.typ"), + title: if lang == "en" [Memory-mapped Registers] + else if lang == "de" [Im Speicher abgebildete Register] + else if lang == "uk" [Відображені в пам'яті регістри] + else if lang == "zh" [存储映射的寄存器] + else { todo } + ), + "start/semihosting": ( + content: (include "start/semihosting.typ"), + title: if lang in ("en", "de", "uk") [Semihosting] + else if lang == "zh" [半主机模式] + else { todo } + ), + "start/panicking": ( + content: (include "start/panicking.typ"), + title: if lang == "en" [Panicking] + else if lang == "de" [In Panik geraten] + else if lang == "uk" [Паніка] + else if lang == "zh" [运行时恐慌(Panicking)] + else { todo } + ), + "start/exceptions": ( + content: (include "start/exceptions.typ"), + title: if lang in ("en", "de") [Exceptions] + else if lang == "uk" [Виключення] + else if lang == "zh" [异常] + else { todo } + ), + "start/interrupts": ( + content: (include "start/interrupts.typ"), + title: if lang in ("en", "de") [Interrupts] + else if lang == "uk" [Переривання] + else if lang == "zh" [中断] + else { todo } + ), + ) + ), + "peripherals/index": ( + content: (include "peripherals/index.typ"), + title: if lang == "en" [Peripherals] + else if lang == "de" [Peripheriegeräte] + else if lang == "uk" [Периферійні пристрої] + else if lang == "zh" [外设] + else { todo }, + sub: ( + "peripherals/a-first-attempt": ( + content: (include "peripherals/a-first-attempt.typ"), + title: if lang == "en" [A first attempt in Rust] + else if lang == "de" [Ein erster Versuch in Rust] + else if lang == "uk" [Перша спроба на Rust] + else if lang == "zh" [Rust尝鲜] + else { todo } + ), + "peripherals/borrowck": ( + content: (include "peripherals/borrowck.typ"), + title: if lang == "en" [The Borrow Checker] + else if lang == "de" [Der Borrow-Prüfer] + else if lang == "uk" [Перевірка запозичень] + else if lang == "zh" [借用检查器] + else { todo } + ), + "peripherals/singletons": ( + content: (include "peripherals/singletons.typ"), + title: if lang in ("en", "de") [Singletons] + else if lang == "uk" [Одинаки] + else if lang == "zh" [单例] + else { todo } + ) + ) + ), + "static-guarantees/index": ( + content: (include "static-guarantees/index.typ"), + title: if lang == "en" [Static Guarantees] + else if lang == "de" [Statische Garantien] + else if lang == "uk" [Статичні гарантії] + else if lang == "zh" [静态保障(static guarantees)] + else { todo }, + sub: ( + "static-guarantees/typestate-programming": ( + content: (include "static-guarantees/typestate-programming.typ"), + title: if lang == "en" [Typestate Programming] + else if lang == "de" [Typgestützte Programmierung] + else if lang == "uk" [Програмування типів-станів] + else if lang == "zh" [类型状态编程] + else { todo } + ), + "static-guarantees/state-machines": ( + content: (include "static-guarantees/state-machines.typ"), + title: if lang == "en" [Peripherals as State Machines] + else if lang == "de" [Peripheriegeräte als Zustandsmaschinen] + else if lang == "uk" [Периферія як кінцеві автомати] + else if lang == "zh" [把外设当作状态机] + else { todo } + ), + "static-guarantees/design-contracts": ( + content: (include "static-guarantees/design-contracts.typ"), + title: if lang == "en" [Design Contracts] + else if lang == "de" [Designverträge] + else if lang == "uk" [Угоди щодо дизайну] + else if lang == "zh" [设计约定] + else { todo } + ), + "static-guarantees/zero-cost-abstractions": ( + content: (include "static-guarantees/zero-cost-abstractions.typ"), + title: if lang == "en" [Zero Cost Abstractions] + else if lang == "de" [Abstraktionen ohne Kosten] + else if lang == "uk" [Безкоштовні абстракції] + else if lang == "zh" [零成本抽象] + else { todo } + ), + ) + ), + "portability/index": ( + content: (include "portability/index.typ"), + title: if lang == "en" [Portability] + else if lang == "de" [Portabilität] + else if lang == "uk" [Переносимість] + else if lang == "zh" [可移植性] + else { todo } + ), + "concurrency/index": ( + content: (include "concurrency/index.typ"), + title: if lang == "en" [Concurrency] + else if lang == "de" [Nebenläufigkeit] + else if lang == "uk" [Паралелізм] + else if lang == "zh" [并发] + else { todo } + ), + "collections/index": ( + content: (include "collections/index.typ"), + title: if lang == "en" [Collections] + else if lang == "de" [Sammlungen] + else if lang == "uk" [Колекції] + else if lang == "zh" [容器] + else { todo } + ), + "design-patterns/index": ( + content: (include "design-patterns/index.typ"), + title: if lang == "en" [Design Patterns] + else if lang == "de" [Desginmuster] + else if lang == "uk" [Шаблони проектування] + else if lang == "zh" [设计模式] + else { todo }, + sub: ( + "design-patterns/hal/index": ( + content: (include "design-patterns/hal/index.typ"), + title: if lang in ("en", "zh", "de") [HALs] + else if lang == "uk" [HALи] + else { todo }, + sub: ( + "design-patterns/hal/checklist": ( + content: (include "design-patterns/hal/checklist.typ"), + title: if lang == "en" [Checklist] + else if lang == "de" [Checkliste] + else if lang == "uk" [Контрольний список] + else if lang == "zh" [列表] + else { todo } + ), + "design-patterns/hal/naming": ( + content: (include "design-patterns/hal/naming.typ"), + title: if lang == "en" [Naming] + else if lang == "de" [Benennung] + else if lang == "uk" [Найменування] + else if lang == "zh" [命名] + else { todo } + ), + "design-patterns/hal/interoperability": ( + content: (include "design-patterns/hal/interoperability.typ"), + title: if lang == "en" [Interoperability] + else if lang == "de" [Interoperabilität] + else if lang == "uk" [Сумісність] + else if lang == "zh" [互操性] + else { todo } + ), + "design-patterns/hal/predictability": ( + content: (include "design-patterns/hal/predictability.typ"), + title: if lang == "en" [Predictability] + else if lang == "de" [Vorhersehbarkeit] + else if lang == "uk" [Передбачуваність] + else if lang == "zh" [可预见性] + else { todo } + ), + "design-patterns/hal/gpio": ( + content: (include "design-patterns/hal/gpio.typ"), + title: [GPIO] + ), + ) + ) + ) + ), + "c-tips/index": ( + content: (include "c-tips/index.typ"), + title: if lang == "en" [Tips for embedded C developers] + else if lang == "de" [Tipps für Entwickler im Bereich Embedded-C] + else if lang == "uk" [Поради для розробників мовою C] + else if lang == "zh" [给嵌入式C开发者的贴士] + else { todo } + ), + "interoperability/index": ( + content: (include "interoperability/index.typ"), + title: if lang == "en" [Interoperability] + else if lang == "de" [Interoperabilität] + else if lang == "uk" [Сумісність] + else if lang == "zh" [互操性] + else { todo }, + sub: ( + "interoperability/c-with-rust": ( + content: (include "interoperability/c-with-rust.typ"), + title: if lang == "en" [A little C with your Rust] + else if lang == "de" [Ein bisschen C zu Ihrem Rust] + else if lang == "uk" [Трошки C в вашому Rust] + else if lang == "zh" [使用C的Rust] + else { todo } + ), + "interoperability/rust-with-c": ( + content: (include "interoperability/rust-with-c.typ"), + title: if lang == "en" [A little Rust with your C] + else if lang == "de" [Ein bisschen Rust zu Ihrem C] + else if lang == "uk" [Трошки Rust в вашому C] + else if lang == "zh" [使用Rust的C] + else { todo } + ) + ) + ), + "unsorted/index": ( + content: (include "unsorted/index.typ"), + title: if lang == "en" [Unsorted topics] + else if lang == "de" [Unsortierte Themen] + else if lang == "uk" [Невідсортовані теми] + else if lang == "zh" [没有排序的主题] + else { todo }, + sub: ( + "unsorted/speed-vs-size": ( + content: (include "unsorted/speed-vs-size.typ"), + title: if lang == "en" [Optimizations: The speed size tradeoff] + else if lang == "de" [Optimierungen: Der Kompromiss zwischen Geschwindigkeit und Größe] + else if lang == "uk" [Оптимізація: компроміс між швидкістю та розміром] + else if lang == "zh" [优化: 速度与大小间的博弈] + else { todo } + ), + "unsorted/math": ( + content: (include "unsorted/math.typ"), + title: if lang == "en" [Performing Math Functionality] + else if lang == "de" [Ausführung mathematischer Funktionen] + else if lang == "uk" [Виконання математики] + else if lang == "zh" [执行数学运算] + else { todo } + ) + ) + ), + "appendix/glossary": ( + content: (include "appendix/glossary.typ"), + title: if lang == "en" [Appendix A: Glossary] + else if lang == "de" [Anhang A: Glossar] + else if lang == "uk" [Додаток А: Глосарій] + else if lang == "zh" [附录A: 词汇表] + else { todo } + ) +) + +#book( + tgt, + sources, + lang, + languages, + book_title: book_title, +) diff --git a/typ/c-tips/index.typ b/typ/c-tips/index.typ new file mode 100644 index 00000000..9856f74b --- /dev/null +++ b/typ/c-tips/index.typ @@ -0,0 +1,864 @@ +#import "../config.typ": * + +#h1(if lang == "en" [Tips for embedded C developers] + else if lang == "de" [Tipps für Embedded-C-Entwickler] + else { todo }) +#set heading(offset: whole) + +#if lang == "en" [ + This chapter collects a variety of tips that might be useful to + experienced embedded C developers looking to start writing Rust. It will + especially highlight how things you might already be used to in C are + different in Rust. +] else if lang == "de" [ + Dieses Kapitel versammelt eine Reihe von Tipps, die für erfahrene + Embedded-C-Entwickler nützlich sein können, die mit der Programmierung + in Rust beginnen möchten. Dabei wird insbesondere hervorgehoben, wie + sich Dinge, die man aus C bereits kennt, in Rust unterscheiden. +] else { todo } + +== #(if lang == "en" [Preprocessor] + else if lang == "de" [Präprozessor] + else { todo }) + +#if lang == "en" [ + In embedded C it is very common to use the preprocessor for a variety of + purposes, such as: + - Compile-time selection of code blocks with `#ifdef` + - Compile-time array sizes and computations + - Macros to simplify common patterns (to avoid function call overhead) +] else if lang == "de" [ + In der Embedded-C-Programmierung ist es sehr üblich, den Präprozessor + für eine Vielzahl von Zwecken zu nutzen, wie zum Beispiel: + - Auswahl von Codeblöcken zur Kompilierzeit mittels `#ifdef` + - Zur Kompilierzeit festgelegte Array-Größen und Berechnungen + - Makros zur Vereinfachung häufiger Muster (um den Overhead von + Funktionsaufrufen zu vermeiden) +] else { todo } + +#if lang == "en" [ + In Rust there is no preprocessor, and so many of these use cases are + addressed differently. In the rest of this section we cover various + alternatives to using the preprocessor. +] else if lang == "de" [ + In Rust gibt es keinen Präprozessor, weshalb viele dieser + Anwendungsfälle anders gelöst werden. Im weiteren Verlauf dieses + Abschnitts behandeln wir verschiedene Alternativen zur Verwendung des + Präprozessors. +] else { todo } + +=== #(if lang == "en" [Compile-Time Code Selection] + else if lang == "de" [Code-Auswahl zur Kompilierzeit] + else { todo }) + +#let url_features = "https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section" +#if lang == "en" [ + The closest match to `#ifdef ... #endif` in Rust are + #link(url_features)[Cargo features]. + These are a little more formal than the C preprocessor: all possible + features are explicitly listed per crate, and can only be either on or + off. Features are turned on when you list a crate as a dependency, and + are additive: if any crate in your dependency tree enables a feature for + another crate, that feature will be enabled for all users of that crate. +] else if lang == "de" [ + Das Äquivalent zu `#ifdef ... #endif` in Rust sind + #link(url_features)[Cargo-Features]. + Diese sind etwas formaler als der C-Präprozessor: Alle möglichen + Features werden pro Crate explizit aufgelistet und können entweder + aktiviert oder deaktiviert sein. Features werden eingeschaltet, sobald + man ein Crate als Abhängigkeit angibt; sie sind zudem additiv: Wenn + irgendein Crate im Abhängigkeitsbaum ein Feature für ein anderes Crate + aktiviert, ist dieses Feature für alle Nutzer jenes Crates aktiviert. +] else { todo } + +#if lang == "en" [ + For example, you might have a crate which provides a library of signal + processing primitives. Each one might take some extra time to compile or + declare some large table of constants which you'd like to avoid. You + could declare a Cargo feature for each component in your `Cargo.toml`: +] else if lang == "de" [ + Angenommen, du hast ein Crate, das eine Bibliothek von Grundbausteinen + für die Signalverarbeitung bereitstellt. Jeder dieser Bausteine ​​könnte + zusätzliche Kompilierzeit beanspruchen oder eine umfangreiche Tabelle + von Konstanten definieren, die du gerne vermeiden möchtest. Du könntest + für jede Komponente in deiner `Cargo.toml` ein Cargo-Feature definieren: +] else { todo } + +```toml +[features] +FIR = [] +IIR = [] +``` + +#if lang == "en" [ + Then, in your code, use `#[cfg(feature="FIR")]` to control what is + included. +] else if lang == "de" [ + Verwenden Sie dann in Ihrem Code `#[cfg(feature="FIR")]`, um zu steuern, + was einbezogen wird. +] else { todo } + +#raw(block: true, lang: "rust", +"/// " + if lang == "en" { + "In your top-level lib.rs" + } else if lang == "de" { + "In deiner lib.rs auf oberster Ebene" + } else { todos } + " + +#[cfg(feature=\"FIR\")] +pub mod fir; + +#[cfg(feature=\"IIR\")] +pub mod iir; +") + +#if lang == "en" [ + You can similarly include code blocks only if a feature is _not_ + enabled, or if any combination of features are or are not enabled. +] else if lang == "de" [ + Sie können Codeblöcke analog dazu nur dann einfügen, wenn eine Funktion + _nicht_ aktiviert ist oder wenn eine beliebige Kombination von + Funktionen aktiviert oder deaktiviert ist. +] else { todo } + +#let url_conditional = "https://doc.rust-lang.org/reference/conditional-compilation.html" +#if lang == "en" [ + Additionally, Rust provides a number of automatically-set conditions you + can use, such as `target_arch` to select different code based on + architecture. For full details of the conditional compilation support, + refer to the #link(url_conditional)[conditional compilation] + chapter of the Rust reference. +] else if lang == "de" [ + Darüber hinaus bietet Rust eine Reihe automatisch gesetzter Bedingungen, + die Sie verwenden können, beispielsweise `target_arch`, um je nach + Architektur unterschiedlichen Code auszuwählen. Ausführliche + Informationen zur bedingten Kompilierung finden Sie im Kapitel + #link(url_conditional)[Bedingte Kompilierung] + der Rust-Referenz. +] else { todo } + +#if lang == "en" [ + The conditional compilation will only apply to the next statement or + block. If a block can not be used in the current scope then the `cfg` + attribute will need to be used multiple times. It's worth noting that + most of the time it is better to simply include all the code and allow + the compiler to remove dead code when optimising: it's simpler for you + and your users, and in general the compiler will do a good job of + removing unused code. +] else if lang == "de" [ + Die bedingte Kompilierung bezieht sich nur auf die unmittelbar folgende + Anweisung oder den folgenden Block. Kann im aktuellen Gültigkeitsbereich + kein Block verwendet werden, muss das `cfg`-Attribut mehrfach eingesetzt + werden. Es sei darauf hingewiesen, dass es meist besser ist, den + gesamten Code einzubeziehen und den Compiler bei der Optimierung nicht + mehr benötigten Code („Dead Code") entfernen zu lassen: Dies ist für Sie + und Ihre Nutzer einfacher, und der Compiler leistet im Allgemeinen gute + Arbeit beim Entfernen von ungenutztem Code. +] else { todo } + +=== #(if lang == "en" [Compile-Time Sizes and Computation] + else if lang == "de" [Größen und Berechnungen zur Kompilierzeit] + else { todo }) + +#if lang == "en" [ + Rust supports `const fn`, functions which are guaranteed to be evaluable + at compile-time and can therefore be used where constants are required, + such as in the size of arrays. This can be used alongside features + mentioned above, for example: +] else if lang == "de" [ + Rust unterstützt `const fn` -- Funktionen, die garantiert zur + Kompilierzeit ausgewertet werden können und sich daher überall dort + einsetzen lassen, wo Konstanten erforderlich sind, etwa bei der Größe + von Arrays. Dies lässt sich mit den zuvor genannten Funktionen + kombinieren, zum Beispiel: +] else { todo } + +```rust +const fn array_size() -> usize { + #[cfg(feature="use_more_ram")] + { 1024 } + #[cfg(not(feature="use_more_ram"))] + { 128 } +} + +static BUF: [u32; array_size()] = [0u32; array_size()]; +``` + +#if lang == "en" [ + These are new to stable Rust as of 1.31, so documentation is still + sparse. The functionality available to `const fn` is also very limited + at the time of writing; in future Rust releases it is expected to expand + on what is permitted in a `const fn`. +] else if lang == "de" [ + Diese Funktionen sind seit Version 1.31 in Stable Rust verfügbar, + weshalb die Dokumentation noch spärlich ist. Auch der für `const fn` + verfügbare Funktionsumfang ist zum Zeitpunkt der Erstellung dieses + Textes stark eingeschränkt; es ist jedoch zu erwarten, dass die in einer + `const fn` zulässigen Operationen in künftigen Rust-Versionen erweitert werden. +] else { todo } + +=== #(if lang == "en" [Macros] + else if lang == "de" [Makros] + else { todo }) + +#let url_macros = "https://doc.rust-lang.org/book/ch19-06-macros.html" +#if lang == "en" [ + Rust provides an extremely powerful #link(url_macros)[macro system]. + While the C preprocessor operates almost directly on the text of your + source code, the Rust macro system operates at a higher level. There are + two varieties of Rust macro: _macros by example_ and + _procedural macros_. The former are simpler and most common; they + look like function calls and can expand to a complete expression, + statement, item, or pattern. Procedural macros are more complex but + permit extremely powerful additions to the Rust language: they can + transform arbitrary Rust syntax into new Rust syntax. +] else if lang == "de" [ + Rust bietet ein äußerst leistungsfähiges #link(url_macros)[Makrosystem]. + Während der C-Präprozessor fast direkt auf dem Text des Quellcodes + arbeitet, operiert das Rust-Makrosystem auf einer höheren Ebene. Es gibt + zwei Arten von Rust-Makros: _Macros by Example_ (Makros nach + Muster) und _prozedurale Makros_. Erstere sind einfacher und weiter + verbreitet; sie sehen wie Funktionsaufrufe aus und können zu einem + vollständigen Ausdruck, einer Anweisung, einem Element oder einem Muster + expandiert werden. Prozedurale Makros sind komplexer, ermöglichen jedoch + äußerst leistungsfähige Erweiterungen der Sprache Rust: Sie können + beliebige Rust-Syntax in neue Rust-Syntax umwandeln. +] else { todo } + +#if lang == "en" [ + In general, where you might have used a C preprocessor macro, you + probably want to see if a macro-by-example can do the job instead. They + can be defined in your crate and easily used by your own crate or + exported for other users. Be aware that since they must expand to + complete expressions, statements, items, or patterns, some use cases of + C preprocessor macros will not work, for example a macro that expands to + part of a variable name or an incomplete set of items in a list. +] else if lang == "de" [ + Wenn Sie normalerweise ein C-Präprozessor-Makro verwenden würden, + sollten Sie im Allgemeinen prüfen, ob stattdessen ein „Macro-by-Example" + (Makro-durch-Beispiel) die Aufgabe erfüllen kann. Solche Makros lassen + sich in Ihrem Crate definieren und sowohl intern verwenden als auch für + andere Nutzer exportieren. Beachten Sie jedoch, dass sie zu + vollständigen Ausdrücken, Anweisungen, Elementen (Items) oder Mustern + expandieren müssen; bestimmte Anwendungsfälle von C-Präprozessor-Makros + funktionieren daher nicht -- etwa Makros, die nur einen Teil eines + Variablennamens oder unvollständige Listenelemente erzeugen. +] else { todo } + +#let url_inline = "https://doc.rust-lang.org/reference/attributes.html#inline-attribute" +#if lang == "en" [ + As with Cargo features, it is worth considering if you even need the + macro. In many cases a regular function is easier to understand and will + be inlined to the same code as a macro. The `#[inline]` and + `#[inline(always)]` #link(url_inline)[attributes] + give you further control over this process, although care should be + taken here as well --- the compiler will automatically inline functions + from the same crate where appropriate, so forcing it to do so + inappropriately might actually lead to decreased performance. +] else if lang == "de" [ + Wie bei Cargo-Features lohnt es sich auch hier zu überlegen, ob das + Makro überhaupt erforderlich ist. Oftmals ist eine gewöhnliche Funktion + leichter verständlich und wird vom Compiler zu demselben Maschinencode + expandiert (geinlined) wie ein Makro. Die Attribute `#[inline]` und + `#[inline(always)]`-#link(url_inline)[Attribute] + bieten Ihnen zusätzliche Kontrolle über diesen Vorgang, wobei jedoch + Vorsicht geboten ist: Der Compiler inlined Funktionen aus demselben + Crate ohnehin automatisch, wenn dies sinnvoll ist; ein erzwungenes + Inlining in ungeeigneten Fällen kann die Leistung sogar verschlechtern. +] else { todo } + +#if lang == "en" [ + Explaining the entire Rust macro system is out of scope for this tips + page, so you are encouraged to consult the Rust documentation for full + details. +] else if lang == "de" [ + Eine Erläuterung des gesamten Rust-Makrosystems würde den Rahmen dieser + Tipps-Seite sprengen; für alle Einzelheiten sei daher auf die + Rust-Dokumentation verwiesen. +] else { todo } + +== #(if lang in ("en", "de") [Build System] + else { todo }) + +#let url_build_scripts = "https://doc.rust-lang.org/cargo/reference/build-scripts.html" +#if lang == "en" [ + Most Rust crates are built using Cargo (although it is not required). + This takes care of many difficult problems with traditional build + systems. However, you may wish to customise the build process. Cargo + provides + #link(url_build_scripts)[`build.rs` scripts] + for this purpose. They are Rust scripts which can interact with the + Cargo build system as required. +] else if lang == "de" [ + Die meisten Rust-Crates werden mit Cargo erstellt (auch wenn dies nicht + zwingend erforderlich ist). Cargo löst dabei viele der schwierigen + Probleme, die mit herkömmlichen Build-Systemen verbunden sind. Dennoch + kann es sinnvoll sein, den Build-Prozess individuell anzupassen. Hierfür + stellt Cargo #link(url_build_scripts)[`build.rs`-Skripte] + bereit. Dabei handelt es sich um Rust-Skripte, die bei Bedarf mit dem + Cargo-Build-System interagieren können. +] else { todo } + +#if lang == "en" [ + Common use cases for build scripts include: + - provide build-time information, for example statically embedding the + build date or Git commit hash into your executable + - generate linker scripts at build time depending on selected features + or other logic + - change the Cargo build configuration + - add extra static libraries to link against +] else if lang == "de" [ + Zu den häufigen Anwendungsfällen für Build-Skripte gehören: + - Informationen zum Build-Zeitpunkt bereitstellen, zum Beispiel durch + das statische Einbetten des Build-Datums oder des Git-Commit-Hashs in + die ausführbare Datei. + - Linker-Skripte zur Build-Zeit generieren, abhängig von ausgewählten + Funktionen oder anderer Logik. + - die Cargo-Build-Konfiguration ändern + - Zusätzliche statische Bibliotheken für den Linkvorgang hinzufügen +] else { todo } + +#if lang == "en" [ + At present there is no support for post-build scripts, which you might + traditionally have used for tasks like automatic generation of binaries + from the build objects or printing build information. +] else if lang == "de" [ + Derzeit gibt es keine Unterstützung für Post-Build-Skripte, wie man sie + üblicherweise für Aufgaben wie die automatische Erstellung von + Binärdateien aus den Build-Objekten oder die Ausgabe von + Build-Informationen verwendet hat. +] else { todo } + +=== #(if lang == "en" [Cross-Compiling] + else if lang == "de" [Cross-Kompilierung] + else { todo }) + +#if lang == "en" [ + Using Cargo for your build system also simplifies cross-compiling. In + most cases it suffices to tell Cargo `--target thumbv6m-none-eabi` and + find a suitable executable in `target/thumbv6m-none-eabi/debug/myapp`. +] else if lang == "de" [ + Die Verwendung von Cargo als Build-System vereinfacht auch die + Cross-Kompilierung. In den meisten Fällen genügt es, Cargo die Option + `--target thumbv6m-none-eabi` mitzugeben und die entsprechende + ausführbare Datei unter `target/thumbv6m-none-eabi/debug/myapp` zu finden. +] else { todo } + +#if lang == "en" [ + For platforms not natively supported by Rust, you will need to build + `libcore` for that target yourself. On such platforms, + #link("https://github.com/japaric/xargo")[Xargo] can be used as a + stand-in for Cargo which automatically builds `libcore` for you. +] else if lang == "de" [ + Für Plattformen, die nicht nativ von Rust unterstützt werden, müssen Sie + `libcore` für das jeweilige Zielsystem selbst kompilieren. Auf solchen + Plattformen lässt sich #link("https://github.com/japaric/xargo")[Xargo] + als Ersatz für Cargo verwenden, da es `libcore` automatisch für Sie + erstellt. +] else { todo } + +== #(if lang == "en" [Iterators vs Array Access] + else if lang == "de" [Iteratoren vs.~Array-Zugriff] + else { todo }) + +#if lang == "en" [ + In C you are probably used to accessing arrays directly by their index: +] else if lang == "de" [ + In C sind Sie es wahrscheinlich gewohnt, direkt über den Index auf + Arrays zuzugreifen: +] else { todo } + +```c +int16_t arr[16]; +int i; +for(i=0; i)[Concurrency chapter]). +] else if lang == "de" [ + Es ist selten erforderlich, diese Funktionen direkt im eigenen Code zu + verwenden, da sie üblicherweise von höherwertigen Bibliotheken für Sie + übernommen werden. Bei speicherabgebildeten Peripheriekomponenten + implementieren die entsprechenden „Peripheral Access Crates" den + volatilen Zugriff automatisch, während für Nebenläufigkeits-Primitive + bessere Abstraktionen zur Verfügung stehen (siehe das Kapitel + #link()[Nebenläufigkeit]). +] else { todo } + +== #(if lang == "en" [Packed and Aligned Types] + else if lang == "de" [Gepackte und ausgerichtete Datentypen] + else { todo }) + +#if lang == "en" [ + In embedded C it is common to tell the compiler a variable must have a + certain alignment or a struct must be packed rather than aligned, + usually to meet specific hardware or protocol requirements. +] else if lang == "de" [ + In der Embedded-Programmierung mit C ist es üblich, dem Compiler + vorzugeben, dass eine Variable eine bestimmte Ausrichtung (Alignment) + aufweisen oder eine Struktur „gepackt" (packed) statt ausgerichtet sein + muss -- meist, um spezifische Hardware- oder Protokollanforderungen zu + erfüllen. +] else { todo } + +#if lang == "en" [ + In Rust this is controlled by the `repr` attribute on a struct or union. + The default representation provides no guarantees of layout, so should + not be used for code that interoperates with hardware or C. The compiler + may re-order struct members or insert padding and the behaviour may + change with future versions of Rust. +] else if lang == "de" [ + In Rust wird dies über das `repr`-Attribut an einer `struct` oder + `union` gesteuert. Die Standarddarstellung macht keine Zusagen über das + Speicherlayout und sollte daher nicht für Code verwendet werden, der mit + Hardware oder C interagiert. Der Compiler kann die Reihenfolge der + Strukturmitglieder ändern oder Füllbytes (Padding) einfügen, und das + Verhalten kann sich in zukünftigen Rust-Versionen ändern. +] else { todo } + +#raw(block: true, lang: "rust", +"struct Foo { + x: u16, + y: u8, + z: u16, +} + +fn main() { + let v = Foo { x: 0, y: 0, z: 0 }; + println!(\"{:p} {:p} {:p}\", &v.x, &v.y, &v.z); +} + +// 0x7ffecb3511d0 0x7ffecb3511d4 0x7ffecb3511d2 +// " + if lang == "en" { + "Note ordering has been changed to x, z, y to improve packing." + } else if lang == "de" { + "Die Reihenfolge der Noten wurde auf x, z, y geaendert, um die Packdichte zu +// verbessern." + } else { todos } + " +") + +#if lang == "en" [ + To ensure layouts that are interoperable with C, use `repr(C)`: +] else if lang == "de" [ + Um Layouts zu gewährleisten, die mit C interoperabel sind, verwenden Sie `repr(C)`: +] else { todo } + +#raw(block: true, lang: "rust", +"#[repr(C)] +struct Foo { + x: u16, + y: u8, + z: u16, +} + +fn main() { + let v = Foo { x: 0, y: 0, z: 0 }; + println!(\"{:p} {:p} {:p}\", &v.x, &v.y, &v.z); +} + +// 0x7fffd0d84c60 0x7fffd0d84c62 0x7fffd0d84c64 +// " + if lang == "en" { + "Ordering is preserved and the layout will not change over time. +// `z` is two-byte aligned so a byte of padding exists between `y` and `z`." + } else if lang == "de" { + "Die Reihenfolge bleibt erhalten und das Layout veraendert sich im Laufe der +// Zeit nicht. +// `z` ist auf Zwei-Byte-Grenzen ausgerichtet, sodass sich zwischen `y` und `z` +// ein Padding-Byte befindet." + } else { todos } + " +") + +#if lang == "en" [ + To ensure a packed representation, use `repr(packed)`: +] else if lang == "de" [ + Um eine kompakte Darstellung zu gewährleisten, verwenden Sie `repr(packed)`: +] else { todo } + +#raw(block: true, lang: "rust", +"#[repr(packed)] +struct Foo { + x: u16, + y: u8, + z: u16, +} + +fn main() { + let v = Foo { x: 0, y: 0, z: 0 }; + // " + if lang == "en" { + "References must always be aligned, so to check the addresses of the + // struct's fields, we use `std::ptr::addr_of!()` to get a raw pointer + // instead of just printing `&v.x`." + } else if lang == "de" { + "Referenzen muessen stets korrekt ausgerichtet sein; um also die + // Adressen der Struct-Felder zu ueberpruefen, verwenden wir + // `std::ptr::addr_of!()`, um einen Rohzeiger (Raw Pointer) zu erhalten, + // anstatt einfach `&v.x` auszugeben." + } else { todos } + " + let px = std::ptr::addr_of!(v.x); + let py = std::ptr::addr_of!(v.y); + let pz = std::ptr::addr_of!(v.z); + println!(\"{:p} {:p} {:p}\", px, py, pz); +} + +// 0x7ffd33598490 0x7ffd33598492 0x7ffd33598493 +// " + if lang == "en" { + "No padding has been inserted between `y` and `z`, so now `z` is unaligned." + } else if lang == "de" { + "Zwischen `y` und `z` wurde kein Padding eingefuegt, daher ist `z` nun +// nicht ausgerichtet." + } else { todos } + " +") + +#if lang == "en" [ + Note that using `repr(packed)` also sets the alignment of the type to `1`. +] else if lang == "de" [ + Beachten Sie, dass die Verwendung von `repr(packed)` die Ausrichtung des + Typs ebenfalls auf `1` setzt. +] else { todo } + +#if lang == "en" [ + Finally, to specify a specific alignment, use `repr(align(n))`, where + `n` is the number of bytes to align to (and must be a power of two): +] else if lang == "de" [ + Um eine bestimmte Ausrichtung festzulegen, verwenden Sie schließlich + `repr(align(n))`, wobei `n` die Anzahl der auszurichtenden Bytes ist + (und eine Zweierpotenz sein muss). +] else { todo } + +#raw(block: true, lang: "rust", +"#[repr(C)] +#[repr(align(4096))] +struct Foo { + x: u16, + y: u8, + z: u16, +} + +fn main() { + let v = Foo { x: 0, y: 0, z: 0 }; + let u = Foo { x: 0, y: 0, z: 0 }; + println!(\"{:p} {:p} {:p}\", &v.x, &v.y, &v.z); + println!(\"{:p} {:p} {:p}\", &u.x, &u.y, &u.z); +} + +// 0x7ffec909a000 0x7ffec909a002 0x7ffec909a004 +// 0x7ffec909b000 0x7ffec909b002 0x7ffec909b004 +// " + if lang == "en" { + "The two instances `u` and `v` have been placed on 4096-byte alignments, +// evidenced by the `000` at the end of their addresses." + } else if lang == "de" { + "Die beiden Instanzen `u` und `v` wurden an 4096-Byte-Grenzen ausgerichtet, +// was an den `000` am Ende ihrer Adressen erkennbar ist." + } else { todos } + " +") + +#if lang == "en" [ + Note we can combine `repr(C)` with `repr(align(n))` to obtain an aligned + and C-compatible layout. It is not permissible to combine + `repr(align(n))` with `repr(packed)`, since `repr(packed)` sets the + alignment to `1`. It is also not permissible for a `repr(packed)` type + to contain a `repr(align(n))` type. +] else if lang == "de" [ + Beachten Sie, dass wir `repr(C)` mit `repr(align(n))` kombinieren + können, um ein ausgerichtetes und C-kompatibles Layout zu erhalten. Die + Kombination von `repr(align(n))` mit `repr(packed)` ist nicht zulässig, + da `repr(packed)` die Ausrichtung auf `1` setzt. Ebenso ist es nicht + zulässig, dass ein `repr(packed)`-Typ einen `repr(align(n))`-Typ + enthält. +] else { todo } + +#let url_layout = "https://doc.rust-lang.org/reference/type-layout.html" +#if lang == "en" [ + For further details on type layouts, refer to the #link(url_layout)[type layout] + chapter of the Rust Reference. +] else if lang == "de" [ + Weitere Details zu Typ-Layouts finden Sie im Kapitel + #link(url_layout)[Typ-Layout] + der Rust-Referenz. +] else { todo } + +== Other Resources + +#let url_faq = "https://docs.rust-embedded.org/faq.html" +#let url_for_c = "http://blahg.josefsipek.net/?p=580" +#let url_pointers = "https://github.com/diwic/reffers-rs/blob/master/docs/Pointers.html" +#if lang == "en" [ + - In this book: + - #link()[A little C with your Rust] + - #link()[A little Rust with your C] + - #link(url_faq)[The Rust Embedded FAQs] + - #link(url_for_c)[Rust Pointers for C Programmers] + - #link(url_pointers)[I used to use pointers - now what?] +] else if lang == "de" [ + - In diesem Buch: + - #link()[Ein bisschen C zu Ihrem Rust] + - #link()[Ein bisschen Rust zu Ihrem C] + - #link(url_faq)[Die Rust-Embedded-FAQs] + - #link(url_for_c)[Rust-Pointer für C-Programmierer] + - #link(url_pointers)[Früher habe ich Pointer verwendet -- und jetzt?] +] else { todo } diff --git a/typ/collections/index.typ b/typ/collections/index.typ new file mode 100644 index 00000000..acdaada7 --- /dev/null +++ b/typ/collections/index.typ @@ -0,0 +1,526 @@ +#import "../config.typ": * + +#h1(if lang == "en" [Collections] + else if lang == "de" [Sammlungen (Collections)] + else { todo }) +#set heading(offset: whole) + +#let ln_vec = link("https://doc.rust-lang.org/std/vec/struct.Vec.html")[`Vec`] +#let ln_string = link("https://doc.rust-lang.org/std/string/struct.String.html")[`String`] +#let ln_map = link("https://doc.rust-lang.org/std/collections/struct.HashMap.html")[`HashMap`] +#if lang == "en" [ + Eventually you'll want to use dynamic data structures (AKA collections) + in your program. `std` provides a set of common collections: + #ln_vec, #ln_string, #ln_map, etc. + All the collections implemented in `std` use a global dynamic + memory allocator (AKA the heap). +] else if lang == "de" [ + Irgendwann wirst du in deinem Programm dynamische Datenstrukturen (auch + bekannt als Collections) verwenden wollen. Die Standardbibliothek + (`std`) stellt eine Reihe gängiger Collections bereit: + #ln_vec, #ln_string, #ln_map usw. + Alle in `std` implementierten Collections nutzen einen globalen + dynamischen Speicherverwalter (den sogenannten Heap). +] else { todo } + +#if lang == "en" [ + As `core` is, by definition, free of memory allocations these + implementations are not available there, but they can be found in the + `alloc` crate that's shipped with the compiler. +] else if lang == "de" [ + Da `core` definitionsgemäß frei von Speicherzuweisungen ist, stehen + diese Implementierungen dort nicht zur Verfügung; sie sind jedoch im + `alloc`-Crate zu finden, das mit dem Compiler ausgeliefert wird. +] else { todo } + +#let ln_heapless = link("https://crates.io/crates/heapless")[`heapless`] +#if lang == "en" [ + If you need collections, a heap allocated implementation is not your + only option. You can also use _fixed capacity_ collections; one + such implementation can be found in the #ln_heapless crate. +] else if lang == "de" [ + Wenn Sie Sammlungen benötigen, ist eine auf dem Heap alloziierte + Implementierung nicht Ihre einzige Option. Sie können auch Sammlungen + mit _fester Kapazität_ verwenden; eine solche Implementierung + findet sich im #ln_heapless;-Crate. +] else { todo } + +#if lang == "en" [ + In this section, we'll explore and compare these two implementations. +] else if lang == "de" [ + In diesem Abschnitt werden wir diese beiden Implementierungen + untersuchen und vergleichen. +] else { todo } + +== #(if lang == "en" [Using `alloc`] + else if lang == "de" [Verwendung von `alloc`] + else { todo }) + +#if lang == "en" [ + The `alloc` crate is shipped with the standard Rust distribution. To + import the crate you can directly `use` it _without_ declaring it + as a dependency in your `Cargo.toml` file. +] else if lang == "de" [ + Die `alloc`-Crate ist in der Standard-Rust-Distribution enthalten. Um + das Crate zu importieren, können Sie es direkt per `use` einbinden, + _ohne_ es als Abhängigkeit in Ihrer `Cargo.toml`-Datei zu deklarieren. +] else { todo } + +```rust +#![feature(alloc)] + +extern crate alloc; + +use alloc::vec::Vec; +``` + +#let ln_alloc = link("https://doc.rust-lang.org/core/alloc/trait.GlobalAlloc.html")[`GlobalAlloc`] +#if lang == "en" [ + To be able to use any collection you'll first need use the + `global_allocator` attribute to declare the global allocator your + program will use. It's required that the allocator you select implements + the #ln_alloc trait. +] else if lang == "de" [ + Um eine beliebige Collection nutzen zu können, müssen Sie zunächst das + Attribut `global_allocator` verwenden, um den globalen Allocator zu + deklarieren, den Ihr Programm einsetzen soll. Der gewählte Allocator + muss zwingend das Trait #ln_alloc implementieren. +] else { todo } + +#if lang == "en" [ + For completeness and to keep this section as self-contained as possible + we'll implement a simple bump pointer allocator and use that as the + global allocator. However, we _strongly_ suggest you use a battle + tested allocator from crates.io in your program instead of this allocator. +] else if lang == "de" [ + Der Vollständigkeit halber und um diesen Abschnitt so in sich + abgeschlossen wie möglich zu halten, implementieren wir einen einfachen + Bump-Pointer-Allokator und verwenden diesen als globalen Allokator. Wir + empfehlen Ihnen jedoch _dringend_, in Ihrem Programm stattdessen + einen praxiserprobten Allokator von crates.io zu verwenden. +] else { todo } + +#raw(block: true, lang: "rust", +"// " + if lang == "en" { + "Bump pointer allocator implementation" + } else if lang == "de" { + "Implementierung eines Bump-Pointer-Allocators" + } else { todos } + " + +use core::alloc::{GlobalAlloc, Layout}; +use core::cell::UnsafeCell; +use core::ptr; + +use cortex_m::interrupt; + +// " + if lang == "en" { + "Bump pointer allocator for *single* core systems" + } else if lang == "de" { + "Bump-Pointer-Allocator fuer Systeme mit *einem* Rechenkern" + } else { todos } + " +struct BumpPointerAlloc { + head: UnsafeCell, + end: usize, +} + +unsafe impl Sync for BumpPointerAlloc {} + +unsafe impl GlobalAlloc for BumpPointerAlloc { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + // " + if lang == "en" { + "`interrupt::free` is a critical section that makes our allocator safe + // to use from within interrupts" + } else if lang == "de" { + "`interrupt::free` ist ein kritischer Abschnitt, der die sichere + // Verwendung unseres Allocators aus Interrupts heraus ermoeglicht." + } else { todos } + " + interrupt::free(|_| { + let head = self.head.get(); + let size = layout.size(); + let align = layout.align(); + let align_mask = !(align - 1); + + // " + if lang == "en" { + "move start up to the next alignment boundary" + } else if lang == "de" { + "Verschiebe den Startpunkt zur naechsten Ausrichtungsgrenze." + } else { todos } + " + let start = (*head + align - 1) & align_mask; + + if start + size > self.end { + // " + if lang == "en" { + "a null pointer signal an Out Of Memory condition" + } else if lang == "de" { + "ein Nullzeiger signalisiert einen „Out of Memory“-Zustand" + } else { todos } + " + ptr::null_mut() + } else { + *head = start + size; + start as *mut u8 + } + }) + } + + unsafe fn dealloc(&self, _: *mut u8, _: Layout) { + // " + if lang == "en" { + "this allocator never deallocates memory" + } else if lang == "de" { + "Dieser Allokator gibt niemals Speicher frei." + } else { todos } + " + } +} + +// " + if lang == "en" { + "Declaration of the global memory allocator +// NOTE the user must ensure that the memory region `[0x2000_0100, 0x2000_0200]` +// is not used by other parts of the program" + } else if lang == "de" { + "Deklaration des globalen Speicher-Allocators +// HINWEIS: Der Benutzer muss sicherstellen, dass der Speicherbereich +// `[0x2000_0100, 0x2000_0200]` nicht von anderen Teilen des Programms +// verwendet wird." + } else { todos } + " +#[global_allocator] +static HEAP: BumpPointerAlloc = BumpPointerAlloc { + head: UnsafeCell::new(0x2000_0100), + end: 0x2000_0200, +}; +") + +#if lang == "en" [ + Apart from selecting a global allocator the user will also have to + define how Out Of Memory (OOM) errors are handled using the + _unstable_ `alloc_error_handler` attribute. +] else if lang == "de" [ + Neben der Auswahl eines globalen Allocators muss der Benutzer auch + festlegen, wie mit „Out of Memory" (OOM)-Fehlern umgegangen wird; + hierfür wird das _instabile_ Attribut `alloc_error_handler` + verwendet. +] else { todo } + +```rust +#![feature(alloc_error_handler)] + +use cortex_m::asm; + +#[alloc_error_handler] +fn on_oom(_layout: Layout) -> ! { + asm::bkpt(); + + loop {} +} +``` + +#if lang == "en" [ + Once all that is in place, the user can finally use the collections in `alloc`. +] else if lang == "de" [ + Sobald alles vorhanden ist, kann der Benutzer endlich die Sammlungen in + „alloc" verwenden. +] else { todo } + +```rust +#[entry] +fn main() -> ! { + let mut xs = Vec::new(); + + xs.push(42); + assert!(xs.pop(), Some(42)); + + loop { + // .. + } +} +``` + +#if lang == "en" [ + If you have used the collections in the `std` crate then these will be + familiar as they are exact same implementation. +] else if lang == "de" [ + Wenn Sie die Sammlungen aus dem `std`-Crate bereits verwendet haben, + werden Ihnen diese vertraut vorkommen, da es sich um exakt dieselbe + Implementierung handelt. +] else { todo } + +== #(if lang == "en" [Using `heapless`] + else if lang == "de" [Verwendung von `heapless`] + else { todo }) + +#if lang == "en" [ + `heapless` requires no setup as its collections don't depend on a global + memory allocator. Just `use` its collections and proceed to instantiate them: +] else if lang == "de" [ + `heapless` erfordert keine Einrichtung, da seine Datenstrukturen nicht + von einem globalen Speicher-Allocator abhängen. Binde die + Datenstrukturen einfach per `use` ein und instanziiere sie: +] else { todo } + +```rust +// heapless version: v0.4.x +use heapless::Vec; +use heapless::consts::*; + +#[entry] +fn main() -> ! { + let mut xs: Vec<_, U8> = Vec::new(); + + xs.push(42).unwrap(); + assert_eq!(xs.pop(), Some(42)); + loop {} +} +``` + +#if lang == "en" [ + You'll note two differences between these collections and the ones in `alloc`. +] else if lang == "de" [ + Sie werden zwei Unterschiede zwischen diesen Sammlungen und denen in + `alloc` feststellen. +] else { todo } + +#let ln_typenum = link("https://crates.io/crates/typenum")[`typenum`] +#if lang == "en" [ + First, you have to declare upfront the capacity of the collection. + `heapless` collections never reallocate and have fixed capacities; this + capacity is part of the type signature of the collection. In this case + we have declared that `xs` has a capacity of 8 elements that is the + vector can, at most, hold 8 elements. This is indicated by the `U8` + (see #ln_typenum) in the type signature. +] else if lang == "de" [ + Erstens muss die Kapazität der Sammlung im Voraus deklariert werden. + `heapless`-Sammlungen werden nie neu allokiert und haben eine feste + Kapazität; diese Kapazität ist Teil der Typsignatur der Sammlung. In + diesem Fall haben wir deklariert, dass `xs` eine Kapazität von 8 + Elementen hat, d.~h. der Vektor kann maximal 8 Elemente aufnehmen. Dies + wird durch das `u8` (siehe #ln_typenum) in der Typsignatur + angezeigt. +] else { todo } + +#if lang == "en" [ + Second, the `push` method, and many other methods, return a `Result`. + Since the `heapless` collections have fixed capacity all operations that + insert elements into the collection can potentially fail. The API + reflects this problem by returning a `Result` indicating whether the + operation succeeded or not. In contrast, `alloc` collections will + reallocate themselves on the heap to increase their capacity. +] else if lang == "de" [ + Zweitens geben die Methode `push` sowie viele weitere Methoden ein + `Result` zurück. Da `heapless`-Datenstrukturen über eine feste Kapazität + verfügen, können alle Operationen, die Elemente in die Struktur + einfügen, potenziell fehlschlagen. Die API trägt diesem Umstand + Rechnung, indem sie ein `Result` liefert, das anzeigt, ob die Operation + erfolgreich war oder nicht. Im Gegensatz dazu passen + `alloc`-Datenstrukturen ihre Kapazität durch eine erneute + Speicherzuweisung auf dem Heap an. +] else { todo } + +#if lang == "en" [ + As of version v0.4.x all `heapless` collections store all their elements + inline. This means that an operation like + `let x = heapless::Vec::new();` will allocate the collection on the + stack, but it's also possible to allocate the collection on a `static` + variable, or even on the heap (`Box>`). +] else if lang == "de" [ + Seit Version v0.4.x speichern alle `heapless`-Datenstrukturen ihre + Elemente direkt „inline". Das bedeutet, dass eine Operation wie + `let x = heapless::Vec::new();` die Datenstruktur auf dem Stack anlegt; + es ist jedoch auch möglich, sie in einer `static`-Variablen oder sogar + auf dem Heap (`Box>`) zu speichern. +] else { todo } + +== #(if lang == "en" [Trade-offs] + else if lang == "de" [Abwägungen] + else { todo }) + +#if lang == "en" [ + Keep these in mind when choosing between heap allocated, relocatable + collections and fixed capacity collections. +] else if lang == "de" [ + Berücksichtigen Sie diese Punkte, wenn Sie zwischen auf dem Heap + alloziierten, verschiebbaren Sammlungen und Sammlungen mit fester + Kapazität wählen. +] else { todo } + +=== #(if lang == "en" [Out Of Memory and error handling] + else if lang == "de" [„Out of Memory" und Fehlerbehandlung] + else { todo }) + +#if lang == "en" [ + With heap allocations Out Of Memory is always a possibility and can + occur in any place where a collection may need to grow: for example, all + `alloc::Vec.push` invocations can potentially generate an OOM condition. + Thus some operations can _implicitly_ fail. Some `alloc` + collections expose `try_reserve` methods that let you check for + potential OOM conditions when growing the collection but you need be + proactive about using them. +] else if lang == "de" [ + Bei Heap-Allokationen ist ein „Out of Memory" (OOM) -- also ein + Speichermangel -- immer möglich; er kann überall dort auftreten, wo eine + Collection wachsen muss: So können beispielsweise alle Aufrufe von + `alloc::Vec.push` potenziell zu einer OOM-Situation führen. Folglich + können manche Operationen _implizit_ fehlschlagen. Einige + `alloc`-Collections bieten `try_reserve`-Methoden an, mit denen sich + potenzielle OOM-Situationen beim Vergrößern der Collection überprüfen + lassen; man muss diese Methoden jedoch aktiv einsetzen. +] else { todo } + +#if lang == "en" [ + If you exclusively use `heapless` collections and you don't use a memory + allocator for anything else then an OOM condition is impossible. + Instead, you'll have to deal with collections running out of capacity on + a case by case basis. That is you'll have deal with _all_ the + `Result`s returned by methods like `Vec.push`. +] else if lang == "de" [ + Wenn man ausschließlich `heapless`-Collections verwendet und keinen + Speicher-Allocator für andere Zwecke nutzt, ist eine OOM-Situation + ausgeschlossen. Stattdessen muss man im Einzelfall damit umgehen, wenn + die Kapazität einer Collection erschöpft ist. Das bedeutet, man muss + _alle_ `Result`-Werte behandeln, die von Methoden wie `Vec.push` + zurückgegeben werden. +] else { todo } + +#if lang == "en" [ + OOM failures can be harder to debug than say `unwrap`-ing on all + `Result`s returned by `heapless::Vec.push` because the observed location + of failure may _not_ match with the location of the cause of the + problem. For example, even `vec.reserve(1)` can trigger an OOM if the + allocator is nearly exhausted because some other collection was leaking + memory (memory leaks are possible in safe Rust). +] else if lang == "de" [ + Fehler durch Speichermangel (OOM) können schwieriger zu debuggen sein + als etwa das Aufrufen von `unwrap` auf allen `Result`-Werten von + `heapless::Vec.push`, da die Stelle, an der der Fehler sichtbar wird, + nicht unbedingt mit der Ursache des Problems übereinstimmen muss. So + kann beispielsweise selbst `vec.reserve(1)` ein OOM auslösen, wenn der + Allocator nahezu erschöpft ist, weil eine andere Collection Speicher + verloren hat (Speicherlecks sind auch in „Safe Rust" möglich). +] else { todo } + +=== #(if lang == "en" [Memory usage] + else if lang == "de" [Speichernutzung] + else { todo }) + +#if lang == "en" [ + Reasoning about memory usage of heap allocated collections is hard + because the capacity of long lived collections can change at runtime. + Some operations may implicitly reallocate the collection increasing its + memory usage, and some collections expose methods like `shrink_to_fit` + that can potentially reduce the memory used by the collection -- + ultimately, it's up to the allocator to decide whether to actually + shrink the memory allocation or not. Additionally, the allocator may + have to deal with memory fragmentation which can increase the + _apparent_ memory usage. +] else if lang == "de" [ + Die Einschätzung des Speicherverbrauchs von auf dem Heap allozierten + Collections ist schwierig, da sich die Kapazität langlebiger Collections + zur Laufzeit ändern kann. Manche Operationen führen möglicherweise zu + einer impliziten Neuzuweisung des Speichers, wodurch der Speicherbedarf + steigt; andere Collections bieten Methoden wie `shrink_to_fit` an, die + den genutzten Speicher potenziell verringern können -- wobei letztlich + der Allocator entscheidet, ob die Speicherzuweisung tatsächlich + reduziert wird. Zudem muss der Allocator unter Umständen mit + Speicherfragmentierung umgehen, was den _scheinbaren_ + Speicherverbrauch erhöhen kann. +] else { todo } + +#if lang == "en" [ + On the other hand if you exclusively use fixed capacity collections, + store most of them in `static` variables and set a maximum size for the + call stack then the linker will detect if you try to use more memory + than what's physically available. +] else if lang == "de" [ + Verwendet man hingegen ausschließlich Collections mit fester Kapazität, + legt die meisten davon in `static`-Variablen ab und legt eine maximale + Größe für den Aufruf-Stack (Call Stack) fest, so erkennt der Linker, + wenn mehr Speicher beansprucht wird, als physisch verfügbar ist. +] else { todo } + +#let ln_z_emit = link("https://doc.rust-lang.org/beta/unstable-book/compiler-flags/emit-stack-sizes.html")[`-Z emit-stack-sizes`] +#let ln_stack_sizes = link("https://crates.io/crates/stack-sizes")[`stack-sizes`] +#if lang == "en" [ + Furthermore, fixed capacity collections allocated on the stack will be + reported by #ln_z_emit + flag which means that tools that analyze stack usage + (like #ln_stack_sizes) will include them in their analysis. +] else if lang == "de" [ + Darüber hinaus werden auf dem Stack alloziierte Collections mit fester + Kapazität durch das Flag #ln_z_emit + erfasst; das bedeutet, dass Werkzeuge zur Analyse der Stack-Nutzung + (wie #ln_stack_sizes) diese in ihre Auswertung einbeziehen. +] else { todo } + +#if lang == "en" [ + However, fixed capacity collections can _not_ be shrunk which can + result in lower load factors (the ratio between the size of the + collection and its capacity) than what relocatable collections can achieve. +] else if lang == "de" [ + Sammlungen mit fester Kapazität können jedoch _nicht_ verkleinert + werden, was zu niedrigeren Auslastungsgraden (dem Verhältnis zwischen + der Größe der Sammlung und ihrer Kapazität) führen kann, als sie bei + Sammlungen mit veränderbarer Kapazität möglich sind. +] else { todo } + +=== #(if lang == "en" [Worst Case Execution Time (WCET)] + else if lang == "de" [Maximale Ausführungszeit (WCET)] + else { todo }) + +#if lang == "en" [ + If you are building time sensitive applications or hard real time + applications then you care, maybe a lot, about the worst case execution + time of the different parts of your program. +] else if lang == "de" [ + Wenn Sie zeitkritische oder Echtzeitanwendungen entwickeln, ist Ihnen + die Worst-Case-Ausführungszeit (WCET) der verschiedenen Programmteile + wahrscheinlich sehr wichtig. +] else { todo } + +#if lang == "en" [ + The `alloc` collections can reallocate so the WCET of operations that + may grow the collection will also include the time it takes to + reallocate the collection, which itself depends on the _runtime_ + capacity of the collection. This makes it hard to determine the WCET of, + for example, the `alloc::Vec.push` operation as it depends on both the + allocator being used and its runtime capacity. +] else if lang == "de" [ + Da `alloc`-Collections Speicher neu allokieren können, umfasst die WCET + von Operationen, die die Collection vergrößern, auch die Zeit für die + Neuallokation. Diese hängt wiederum von der _Laufzeitkapazität_ der + Collection ab. Daher ist es schwierig, die WCET beispielsweise der + Operation `alloc::Vec.push` zu bestimmen, da sie sowohl vom verwendeten + Allokator als auch von dessen Laufzeitkapazität abhängt. +] else { todo } + +#if lang == "en" [ + On the other hand fixed capacity collections never reallocate so all + operations have a predictable execution time. For example, + `heapless::Vec.push` executes in constant time. +] else if lang == "de" [ + Collections mit fester Kapazität hingegen allokieren nie Speicher neu, + sodass alle Operationen eine vorhersehbare Ausführungszeit haben. + Beispielsweise wird `heapless::Vec.push` in konstanter Zeit ausgeführt. +] else { todo } + +=== #(if lang == "en" [Ease of use] + else if lang == "de" [Benutzerfreundlichkeit] + else { todo }) + +#if lang == "en" [ + `alloc` requires setting up a global allocator whereas `heapless` does + not. However, `heapless` requires you to pick the capacity of each + collection that you instantiate. +] else if lang == "de" [ + Für `alloc` muss ein globaler Allocator eingerichtet werden, für + `heapless` hingegen nicht. Allerdings erfordert `heapless`, dass man bei + der Instanziierung jeder Collection deren Kapazität festlegt. +] else { todo } + +#if lang == "en" [ + The `alloc` API will be familiar to virtually every Rust developer. The + `heapless` API tries to closely mimic the `alloc` API but it will never + be exactly the same due to its explicit error handling -- some developers + may feel the explicit error handling is excessive or too cumbersome. +] else if lang == "de" [ + Die `alloc`-API dürfte so gut wie jedem Rust-Entwickler vertraut sein. + Die `heapless`-API orientiert sich zwar eng an der `alloc`-API, + unterscheidet sich jedoch aufgrund der expliziten Fehlerbehandlung -- + manche Entwickler empfinden diese explizite Fehlerbehandlung womöglich + als übertrieben oder zu umständlich. +] else { todo } diff --git a/typ/concurrency/index.typ b/typ/concurrency/index.typ new file mode 100644 index 00000000..d62795d9 --- /dev/null +++ b/typ/concurrency/index.typ @@ -0,0 +1,1291 @@ +#import "../config.typ": * + +#h1(if lang == "en" [Concurrency] + else if lang == "de" [Nebenläufigkeit] + else { todo }) + +#set heading(offset: whole) + +#if lang == "en" [ + Concurrency happens whenever different parts of your program might + execute at different times or out of order. In an embedded context, this + includes: + - interrupt handlers, which run whenever the associated interrupt + happens, + - various forms of multithreading, where your microprocessor regularly + swaps between parts of your program, + - and in some systems, multiple-core microprocessors, where each core + can be independently running a different part of your program at the + same time. +] else if lang == "de" [ + Nebenläufigkeit tritt immer dann auf, wenn verschiedene Teile Ihres + Programms zu unterschiedlichen Zeiten oder in einer anderen als der + vorgesehenen Reihenfolge ausgeführt werden können. Im Kontext + eingebetteter Systeme umfasst dies: + - Interrupt-Handler, die immer dann ausgeführt werden, wenn der + zugehörige Interrupt auftritt, + - verschiedene Formen des Multithreadings, bei denen Ihr Mikroprozessor + regelmäßig zwischen Teilen Ihres Programms wechselt, + - und in einigen Systemen Mehrkern-Mikroprozessoren, bei denen jeder + Kern gleichzeitig und unabhängig voneinander einen anderen Teil Ihres + Programms ausführen kann. +] else { todo } + +#if lang == "en" [ + Since many embedded programs need to deal with interrupts, concurrency + will usually come up sooner or later, and it's also where many subtle + and difficult bugs can occur. Luckily, Rust provides a number of + abstractions and safety guarantees to help us write correct code. +] else if lang == "de" [ + Da viele eingebettete Programme mit Interrupts umgehen müssen, spielt + Nebenläufigkeit früher oder später meist eine Rolle -- und genau hier + können auch viele schwer zu findende und komplexe Fehler auftreten. + Glücklicherweise bietet Rust eine Reihe von Abstraktionen und + Sicherheitsgarantien, die uns dabei helfen, korrekten Code zu schreiben. +] else { todo } + +== #(if lang == "en" [No Concurrency] + else if lang == "de" [Keine Nebenläufigkeit] + else { todo }) + +#if lang == "en" [ + The simplest concurrency for an embedded program is no concurrency: your + software consists of a single main loop which just keeps running, and + there are no interrupts at all. Sometimes this is perfectly suited to + the problem at hand! Typically your loop will read some inputs, perform + some processing, and write some outputs. +] else if lang == "de" [ + Die einfachste Form der Nebenläufigkeit für ein Embedded-Programm ist + der Verzicht darauf: Die Software besteht aus einer einzigen + Hauptschleife, die kontinuierlich durchlaufen wird, und es kommen + keinerlei Interrupts zum Einsatz. Manchmal ist genau dieser Ansatz für + die vorliegende Aufgabenstellung ideal! Typischerweise liest die + Schleife Eingabewerte ein, führt Berechnungen oder Verarbeitungen durch + und gibt Ergebnisse aus. +] else { todo } + +```rust +#[entry] +fn main() { + let peripherals = setup_peripherals(); + loop { + let inputs = read_inputs(&peripherals); + let outputs = process(inputs); + write_outputs(&peripherals, outputs); + } +} +``` + +#if lang == "en" [ + Since there's no concurrency, there's no need to worry about sharing + data between parts of your program or synchronising access to + peripherals. If you can get away with such a simple approach this can be + a great solution. +] else if lang == "de" [ + Da keine Nebenläufigkeit vorliegt, müssen Sie sich keine Gedanken über + die gemeinsame Datennutzung zwischen verschiedenen Programmteilen oder + die Synchronisierung des Zugriffs auf Peripheriegeräte machen. Wenn ein + solch einfacher Ansatz ausreicht, kann dies eine hervorragende Lösung sein. +] else { todo } + +== #(if lang == "en" [Global Mutable Data] + else if lang == "de" [Globale veränderliche Daten] + else { todo }) + +#if lang == "en" [ + Unlike non-embedded Rust, we will not usually have the luxury of + creating heap allocations and passing references to that data into a + newly-created thread. Instead, our interrupt handlers might be called at + any time and must know how to access whatever shared memory we are + using. At the lowest level, this means we must have _statically allocated_ + mutable memory, which both the interrupt handler and the main + code can refer to. +] else if lang == "de" [ + Im Gegensatz zu Rust-Anwendungen außerhalb des Embedded-Bereichs haben + wir meist nicht den Luxus, Speicher auf dem Heap zu reservieren und + Referenzen auf diese Daten an einen neu erstellten Thread zu übergeben. + Stattdessen können unsere Interrupt-Handler jederzeit aufgerufen werden + und müssen wissen, wie sie auf den jeweils genutzten gemeinsamen + Speicher zugreifen können. Auf unterster Ebene bedeutet dies, dass wir + über _statisch reservierten_, veränderbaren Speicher verfügen + müssen, auf den sowohl der Interrupt-Handler als auch der + Hauptprogrammcode zugreifen können. +] else { todo } + +#let ln_staticmut = link("https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable")[`static mut`] +#if lang == "en" [ + In Rust, such #ln_staticmut + variables are always unsafe to read or write, because without taking + special care, you might trigger a race condition, where your access to + the variable is interrupted halfway through by an interrupt which also + accesses that variable. +] else if lang == "de" [ + In Rust ist der Lese- oder Schreibzugriff auf solche + #ln_staticmut;-Variablen + stets als `unsafe` (unsicher) eingestuft. Ohne besondere + Vorsichtsmaßnahmen könnte es nämlich zu einer sogenannten Race Condition + (Wettlaufsituation) kommen: Der Zugriff auf die Variable wird mitten im + Vorgang durch einen Interrupt unterbrochen, der seinerseits ebenfalls + auf diese Variable zugreift. +] else { todo } + +#if lang == "en" [ + For an example of how this behaviour can cause subtle errors in your + code, consider an embedded program which counts rising edges of some + input signal in each one-second period (a frequency counter): +] else if lang == "de" [ + Um zu veranschaulichen, wie dieses Verhalten subtile Fehler in Ihrem + Code verursachen kann, betrachten Sie ein eingebettetes Programm, das + die steigenden Flanken eines Eingangssignals innerhalb jedes + Ein-Sekunden-Intervalls zählt (einen Frequenzzähler): +] else { todo } + +#raw(block: true, lang: "rust", +"static mut COUNTER: u32 = 0; + +#[entry] +fn main() -> ! { + set_timer_1hz(); + let mut last_state = false; + loop { + let state = read_signal_level(); + if state && !last_state { + // " + if lang == "en" { + "DANGER - Not actually safe! Could cause data races." + } else if lang == "de" { + "GEFAHR – Nicht wirklich sicher! Koennte zu Datenwettlaeufen + // fuehren." + } else { todos } + " + unsafe { COUNTER += 1 }; + } + last_state = state; + } +} + +#[interrupt] +fn timer() { + unsafe { COUNTER = 0; } +} +") + +#if lang == "en" [ + Each second, the timer interrupt sets the counter back to 0. Meanwhile, + the main loop continually measures the signal, and incremements the + counter when it sees a change from low to high. We've had to use + `unsafe` to access `COUNTER`, as it's `static mut`, and that means we're + promising the compiler we won't cause any undefined behaviour. Can you + spot the race condition? The increment on `COUNTER` is _not_ + guaranteed to be atomic --- in fact, on most embedded platforms, it will + be split into a load, then the increment, then a store. If the interrupt + fired after the load but before the store, the reset back to 0 would be + ignored after the interrupt returns --- and we would count twice as many + transitions for that period. +] else if lang == "de" [ + Sekündlich setzt der Timer-Interrupt den Zähler auf 0 zurück. + Währenddessen misst die Hauptschleife kontinuierlich das Signal und + erhöht den Zähler, sobald ein Wechsel von „Low" auf „High" erkannt wird. + Wir mussten das Schlüsselwort `unsafe` verwenden, um auf `COUNTER` + zuzugreifen, da es als `static mut` deklariert ist; damit versichern wir + dem Compiler, dass wir kein undefiniertes Verhalten verursachen. + Erkennst du die Race Condition? Die Inkrementierung von `COUNTER` ist + _nicht_ garantiert atomar -- tatsächlich wird sie auf den meisten + Embedded-Plattformen in die Schritte Laden, Inkrementieren und Speichern + aufgeteilt. Würde der Interrupt nach dem Laden, aber vor dem Speichern + ausgelöst, ginge das Zurücksetzen auf 0 nach der Rückkehr aus dem + Interrupt verloren -- und wir würden für diesen Zeitraum doppelt so + viele Signalwechsel zählen. +] else { todo } + +== #(if lang == "en" [Critical Sections] + else if lang == "de" [Kritische Abschnitte] + else { todo }) + +#if lang == "en" [ + So, what can we do about data races? A simple approach is to use + _critical sections_, a context where interrupts are disabled. By + wrapping the access to `COUNTER` in `main` in a critical section, we can + be sure the timer interrupt will not fire until we're finished + incrementing `COUNTER`: +] else if lang == "de" [ + Was können wir also gegen Data Races unternehmen? Ein einfacher Ansatz + ist die Verwendung von _kritischen Abschnitten_ -- also Bereichen, + in denen Interrupts deaktiviert sind. Indem wir den Zugriff auf + `COUNTER` in der Funktion `main` in einen kritischen Abschnitt + einbetten, stellen wir sicher, dass der Timer-Interrupt erst ausgelöst + wird, nachdem wir das Inkrementieren von `COUNTER` abgeschlossen haben: +] else { todo } + +#raw(block: true, lang: "rust", +"static mut COUNTER: u32 = 0; + +#[entry] +fn main() -> ! { + set_timer_1hz(); + let mut last_state = false; + loop { + let state = read_signal_level(); + if state && !last_state { + // " + if lang == "en" { + "New critical section ensures synchronised access to COUNTER" + } else if lang == "de" { + "Ein neuer kritischer Abschnitt gewaehrleistet den + // synchronisierten Zugriff auf COUNTER." + } else { todos } + " + cortex_m::interrupt::free(|_| { + unsafe { COUNTER += 1 }; + }); + } + last_state = state; + } +} + +#[interrupt] +fn timer() { + unsafe { COUNTER = 0; } +} +") + +#if lang == "en" [ + In this example, we use `cortex_m::interrupt::free`, but other platforms + will have similar mechanisms for executing code in a critical section. + This is also the same as disabling interrupts, running some code, and + then re-enabling interrupts. +] else if lang == "de" [ + In diesem Beispiel verwenden wir `cortex_m::interrupt::free`, doch auch + andere Plattformen verfügen über ähnliche Mechanismen, um Code in einem + kritischen Abschnitt auszuführen. Dies entspricht im Grunde dem + Deaktivieren von Interrupts, der Ausführung von Code und dem + anschließenden erneuten Aktivieren der Interrupts. +] else { todo } + +#if lang == "en" [ + Note we didn't need to put a critical section inside the timer + interrupt, for two reasons: + - Writing 0 to `COUNTER` can't be affected by a race since we don't read it + - It will never be interrupted by the `main` thread anyway +] else if lang == "de" [ + Beachten Sie, dass wir innerhalb des Timer-Interrupts keinen kritischen + Abschnitt benötigten; dies hat zwei Gründe: + - Das Schreiben von 0 in `COUNTER` kann nicht von einer Race-Condition + betroffen sein, da wir den Wert nicht lesen. + - Es wird ohnehin niemals vom `main`-Thread unterbrochen werden. +] else { todo } + +#if lang == "en" [ + If `COUNTER` was being shared by multiple interrupt handlers that might + _preempt_ each other, then each one might require a critical + section as well. +] else if lang == "de" [ + Wenn `COUNTER` von mehreren Interrupt-Handlern gemeinsam genutzt würde, + die sich gegenseitig _unterbrechen_ könnten, müsste jeder von ihnen + ebenfalls einen kritischen Abschnitt verwenden. +] else { todo } + +#if lang == "en" [ + This solves our immediate problem, but we're still left writing a lot of + unsafe code which we need to carefully reason about, and we might be + using critical sections needlessly. Since each critical section + temporarily pauses interrupt processing, there is an associated cost of + some extra code size and higher interrupt latency and jitter (interrupts + may take longer to be processed, and the time until they are processed + will be more variable). Whether this is a problem depends on your + system, but in general, we'd like to avoid it. +] else if lang == "de" [ + Dies löst zwar unser unmittelbares Problem, doch wir müssen weiterhin + viel unsicheren Code schreiben, dessen Verhalten wir sorgfältig + analysieren müssen; zudem setzen wir möglicherweise unnötigerweise + kritische Abschnitte ein. Da jeder kritische Abschnitt die + Interrupt-Verarbeitung vorübergehend aussetzt, entstehen Kosten in Form + von zusätzlichem Codeumfang sowie höherer Interrupt-Latenz und stärkerem + Jitter (die Verarbeitung von Interrupts kann länger dauern, und die Zeit + bis zur Verarbeitung schwankt stärker). Ob dies ein Problem darstellt, + hängt vom jeweiligen System ab, doch im Allgemeinen sollten wir dies + vermeiden. +] else { todo } + +#if lang == "en" [ + It's worth noting that while a critical section guarantees no interrupts + will fire, it does not provide an exclusivity guarantee on multi-core + systems! The other core could be happily accessing the same memory as + your core, even without interrupts. You will need stronger + synchronisation primitives if you are using multiple cores. +] else if lang == "de" [ + Es ist wichtig zu beachten: Auch wenn ein kritischer Abschnitt + garantiert, dass keine Interrupts ausgelöst werden, bietet er auf + Mehrkernsystemen keine Exklusivitätsgarantie! Der andere Rechenkern + könnte -- selbst ohne Interrupts -- problemlos auf denselben Speicher + zugreifen wie Ihr eigener Kern. Wenn Sie mehrere Rechenkerne nutzen, + benötigen Sie daher leistungsfähigere Synchronisationsmechanismen. +] else { todo } + +== #(if lang == "en" [Atomic Access] + else if lang == "de" [Atomarer Zugriff] + else { todo }) + +#if lang == "en" [ + On some platforms, special atomic instructions are available, which + provide guarantees about read-modify-write operations. Specifically for + Cortex-M: `thumbv6` (Cortex-M0, Cortex-M0+) only provide atomic load and + store instructions, while `thumbv7` (Cortex-M3 and above) provide full + Compare and Swap (CAS) instructions. These CAS instructions give an + alternative to the heavy-handed disabling of all interrupts: we can + attempt the increment, it will succeed most of the time, but if it was + interrupted it will automatically retry the entire increment operation. + These atomic operations are safe even across multiple cores. +] else if lang == "de" [ + Auf einigen Plattformen stehen spezielle atomare Befehle zur Verfügung, + die Garantien für Lese-Modifizier-Schreib-Operationen + (Read-Modify-Write) bieten. Speziell für Cortex-M gilt: `thumbv6` + (Cortex-M0, Cortex-M0+) bietet lediglich atomare Lade- und + Speicherbefehle, während `thumbv7` (Cortex-M3 und höher) vollständige + „Compare-and-Swap"-Befehle (CAS) bereitstellt. Diese CAS-Befehle bieten + eine Alternative zur drastischen Maßnahme, sämtliche Interrupts zu + deaktivieren: Man kann versuchen, den Inkrementierungsvorgang + durchzuführen -- was meistens gelingt --, doch sollte eine Unterbrechung + auftreten, wird der gesamte Vorgang automatisch erneut versucht. Diese + atomaren Operationen sind auch in Mehrkernsystemen sicher. +] else { todo } + +#raw(block: true, lang: "rust", +"use core::sync::atomic::{AtomicUsize, Ordering}; + +static COUNTER: AtomicUsize = AtomicUsize::new(0); + +#[entry] +fn main() -> ! { + set_timer_1hz(); + let mut last_state = false; + loop { + let state = read_signal_level(); + if state && !last_state { + // " + if lang == "en" { + "Use `fetch_add` to atomically add 1 to COUNTER" + } else if lang == "de" { + "Verwenden Sie `fetch_add`, um 1 atomar zu COUNTER zu addieren." + } else { todos } + " + COUNTER.fetch_add(1, Ordering::Relaxed); + } + last_state = state; + } +} + +#[interrupt] +fn timer() { + // " + if lang == "en" { + "Use `store` to write 0 directly to COUNTER" + } else if lang == "de" { + "Verwenden Sie `store`, um 0 direkt in COUNTER zu schreiben." + } else { todos } + " + COUNTER.store(0, Ordering::Relaxed) +} +") + +#if lang == "en" [ + This time `COUNTER` is a safe `static` variable. Thanks to the + `AtomicUsize` type `COUNTER` can be safely modified from both the + interrupt handler and the main thread without disabling interrupts. When + possible, this is a better solution --- but it may not be supported on + your platform. +] else if lang == "de" [ + Diesmal ist `COUNTER` eine sichere `static`-Variable. Dank des Typs + `AtomicUsize` kann `COUNTER` sowohl vom Interrupt-Handler als auch vom + Hauptthread aus sicher geändert werden, ohne Interrupts zu deaktivieren. + Wenn möglich, ist dies die bessere Lösung -- wird aber möglicherweise + von Ihrer Plattform nicht unterstützt. +] else { todo } + +#let ln_ordering = link("https://doc.rust-lang.org/core/sync/atomic/enum.Ordering.html")[`Ordering`] +#if lang == "en" [ + A note on #ln_ordering: + this affects how the compiler and hardware may reorder instructions, and + also has consequences on cache visibility. Assuming that the target is a + single core platform `Relaxed` is sufficient and the most efficient + choice in this particular case. Stricter ordering will cause the + compiler to emit memory barriers around the atomic operations; depending + on what you're using atomics for you may or may not need this! The + precise details of the atomic model are complicated and best described + elsewhere. +] else if lang == "de" [ + Hinweis zur #ln_ordering: + Diese beeinflusst, wie Compiler und Hardware Anweisungen neu anordnen, + und hat auch Auswirkungen auf die Cache-Sichtbarkeit. Bei einer + Einkernplattform ist die Option `Relaxed` ausreichend und in diesem Fall + die effizienteste Wahl. Eine strengere Befehlsreihenfolge führt dazu, + dass der Compiler Speicherbarrieren um die atomaren Operationen erzeugt. + Je nachdem, wofür Sie atomare Operationen verwenden, benötigen Sie dies + möglicherweise nicht. Die genauen Details des atomaren Modells sind + komplex und werden am besten an anderer Stelle beschrieben. +] else { todo } + +#let url_atomics = "https://doc.rust-lang.org/nomicon/atomics.html" +#if lang == "en" [ + For more details on atomics and ordering, see the + #link(url_atomics)[nomicon]. +] else if lang == "de" [ + Weitere Informationen zu atomaren Operationen und deren Reihenfolge + finden Sie im #link(url_atomics)[nomicon]. +] else { todo } + +== #(if lang == "en" [Abstractions, Send, and Sync] + else if lang == "de" [Abstraktionen, Send und Sync] + else { todo }) + +#if lang == "en" [ + None of the above solutions are especially satisfactory. They require + `unsafe` blocks which must be very carefully checked and are not + ergonomic. Surely we can do better in Rust! +] else if lang == "de" [ + Keine der oben genannten Lösungen ist wirklich zufriedenstellend. Sie + erfordern unsichere Blöcke, die sehr sorgfältig geprüft werden müssen + und nicht ergonomisch sind. In Rust geht das doch bestimmt besser! +] else { todo } + +#if lang == "en" [ + We can abstract our counter into a safe interface which can be safely + used anywhere else in our code. For this example, we'll use the + critical-section counter, but you could do something very similar with + atomics. +] else if lang == "de" [ + Wir können unseren Zähler in eine sichere Schnittstelle abstrahieren, + die überall im Code sicher verwendet werden kann. In diesem Beispiel + verwenden wir den Zähler für kritische Abschnitte, aber mit atomaren + Operationen ließe sich etwas sehr Ähnliches realisieren. +] else { todo } + +#raw(block: true, lang: "rust", +"use core::cell::UnsafeCell; +use cortex_m::interrupt; + +// " + if lang == "en" { + "Our counter is just a wrapper around UnsafeCell, which is the heart +// of interior mutability in Rust. By using interior mutability, we can have +// COUNTER be `static` instead of `static mut`, but still able to mutate +// its counter value." + } else if lang == "de" { + "Unser Zaehler ist lediglich ein Wrapper um `UnsafeCell`, das Herzstueck +// der „Interior Mutability“ in Rust. Dank dieses Konzepts können wir `COUNTER` +// als `static` statt als `static mut` definieren und dennoch den Zaehlerwert +// veraendern." + } else { todos } + " +struct CSCounter(UnsafeCell); + +const CS_COUNTER_INIT: CSCounter = CSCounter(UnsafeCell::new(0)); + +impl CSCounter { + pub fn reset(&self, _cs: &interrupt::CriticalSection) { + // " + if lang == "en" { + "By requiring a CriticalSection be passed in, we know we must + // be operating inside a CriticalSection, and so can confidently + // use this unsafe block (required to call UnsafeCell::get)." + } else if lang == "de" { + "Indem wir die Uebergabe einer `CriticalSection` voraussetzen, wissen + // wir, dass wir uns innerhalb einer `CriticalSection` befinden; daher + // koennen wir bedenkenlos diesen `unsafe`-Block verwenden (der für den + // Aufruf von `UnsafeCell::get` erforderlich ist)." + } else { todos } + " + unsafe { *self.0.get() = 0 }; + } + + pub fn increment(&self, _cs: &interrupt::CriticalSection) { + unsafe { *self.0.get() += 1 }; + } +} + +// " + if lang == "en" { + "Required to allow static CSCounter. See explanation below." + } else if lang == "de" { + "Erforderlich, um ein statisches CSCounter zu ermoeglichen. Siehe Erlaeuterung +// unten." + } else { todos } + " +unsafe impl Sync for CSCounter {} + +// " + if lang == "en" { + "COUNTER is no longer `mut` as it uses interior mutability; +// therefore it also no longer requires unsafe blocks to access." + } else if lang == "de" { + "COUNTER ist nicht mehr `mut`, da es „Interior Mutability“ verwendet; daher +// sind fuer den Zugriff auch keine `unsafe`-Bloecke mehr erforderlich." + } else { todos } + " +static COUNTER: CSCounter = CS_COUNTER_INIT; + +#[entry] +fn main() -> ! { + set_timer_1hz(); + let mut last_state = false; + loop { + let state = read_signal_level(); + if state && !last_state { + // " + if lang in ("en", "de") { + "No unsafe here!" + } else { todos } + " + interrupt::free(|cs| COUNTER.increment(cs)); + } + last_state = state; + } +} + +#[interrupt] +fn timer() { + // " + if lang == "en" { + "We do need to enter a critical section here just to obtain a valid + // cs token, even though we know no other interrupt could pre-empt + // this one." + } else if lang == "de" { + "Wir muessen hier tatsaechlich einen kritischen Abschnitt betreten, nur um + // ein gueltiges CS-Token zu erhalten, auch wenn wir wissen, dass kein + // anderer Interrupt diesen unterbrechen koennte." + } else { todos } + " + interrupt::free(|cs| COUNTER.reset(cs)); + + // " + if lang == "en" { + "We could use unsafe code to generate a fake CriticalSection if we + // really wanted to, avoiding the overhead:" + } else if lang == "de" { + "Wir koennten „unsicheren“ Code (unsafe code) verwenden, um eine + // gefaelschte CriticalSection zu erzeugen, falls wir das wirklich wollten, + // und so den Overhead vermeiden:" + } else { todos } + " + // let cs = unsafe { interrupt::CriticalSection::new() }; +} +") + +#if lang == "en" [ + We've moved our `unsafe` code to inside our carefully-planned + abstraction, and now our application code does not contain any `unsafe` + blocks. +] else if lang == "de" [ + Wir haben unseren `unsafe`-Code in unsere sorgfältig entworfene + Abstraktion verlagert; nun enthält unser Anwendungscode keine + `unsafe`-Blöcke mehr. +] else { todo } + +#if lang == "en" [ + This design requires that the application pass a `CriticalSection` token + in: these tokens are only safely generated by `interrupt::free`, so by + requiring one be passed in, we ensure we are operating inside a critical + section, without having to actually do the lock ourselves. This + guarantee is provided statically by the compiler: there won't be any + runtime overhead associated with `cs`. If we had multiple counters, they + could all be given the same `cs`, without requiring multiple nested + critical sections. +] else if lang == "de" [ + Dieser Entwurf setzt voraus, dass die Anwendung ein + `CriticalSection`-Token übergibt: Da diese Tokens nur sicher durch + `interrupt::free` erzeugt werden können, stellen wir durch die + Anforderung eines solchen Tokens sicher, dass wir uns innerhalb eines + kritischen Abschnitts befinden, ohne die Sperre selbst implementieren zu + müssen. Diese Garantie wird statisch vom Compiler gewährleistet; es + entsteht also kein Laufzeit-Overhead durch `cs`. Hätten wir mehrere + Zähler, könnten diese alle dasselbe `cs` verwenden, ohne dass mehrere + verschachtelte kritische Abschnitte erforderlich wären. +] else { todo } + +#let url_sendsync = "https://doc.rust-lang.org/nomicon/send-and-sync.html" +#if lang == "en" [ + This also brings up an important topic for concurrency in Rust: the + #link(url_sendsync)[`Send` and `Sync`] + traits. To summarise the Rust book, a type is Send when it can safely be + moved to another thread, while it is Sync when it can be safely shared + between multiple threads. In an embedded context, we consider interrupts + to be executing in a separate thread to the application code, so + variables accessed by both an interrupt and the main code must be Sync. +] else if lang == "de" [ + Dies führt zu einem wichtigen Thema der Nebenläufigkeit in Rust: den + Traits + #link(url_sendsync)[`Send` und `Sync`]. + Um das „Rust Book" zusammenzufassen: Ein Typ ist `Send`, wenn er sicher + in einen anderen Thread verschoben werden kann, während er `Sync` ist, + wenn er sicher zwischen mehreren Threads geteilt werden kann. Im + Embedded-Kontext betrachten wir Interrupts als Prozesse, die in einem + vom Anwendungscode getrennten Thread ausgeführt werden; daher müssen + Variablen, auf die sowohl ein Interrupt als auch der Hauptcode zugreift, + `Sync` implementieren. +] else { todo } + +#let ln_unsafecell = link("https://doc.rust-lang.org/core/cell/struct.UnsafeCell.html")[`UnsafeCell`] +#if lang == "en" [ + For most types in Rust, both of these traits are automatically derived + for you by the compiler. However, because `CSCounter` contains an #ln_unsafecell, + it is not Sync, and therefore we could not make a `static CSCounter`: + `static` variables _must_ be Sync, since they can be accessed by + multiple threads. +] else if lang == "de" [ + Für die meisten Typen in Rust werden diese beiden Traits automatisch vom + Compiler für dich abgeleitet. Da `CSCounter` jedoch eine #ln_unsafecell + enthält, ist der Typ nicht `Sync`\; folglich konnten wir keinen + `static CSCounter` definieren, denn `static`-Variablen _müssen_ + `Sync` sein, da von mehreren Threads aus auf sie zugegriffen werden + kann. +] else { todo } + +#if lang == "en" [ + To tell the compiler we have taken care that the `CSCounter` is in fact + safe to share between threads, we implement the Sync trait explicitly. + As with the previous use of critical sections, this is only safe on + single-core platforms: with multiple cores, you would need to go to + greater lengths to ensure safety. +] else if lang == "de" [ + Um dem Compiler mitzuteilen, dass wir sichergestellt haben, dass der + `CSCounter` tatsächlich gefahrlos zwischen Threads geteilt werden kann, + implementieren wir das `Sync`-Trait explizit. Wie schon bei der früheren + Verwendung kritischer Abschnitte ist dies nur auf + Single-Core-Plattformen sicher; bei mehreren Kernen wäre ein deutlich + höherer Aufwand erforderlich, um die Sicherheit zu gewährleisten. +] else { todo } + +== #(if lang == "en" [Mutexes] + else if lang == "de" [Mutexe] + else { todo }) + +#if lang == "en" [ + We've created a useful abstraction specific to our counter problem, but + there are many common abstractions used for concurrency. +] else if lang == "de" [ + Wir haben eine nützliche, speziell auf unser Zählerproblem + zugeschnittene Abstraktion entwickelt; es gibt jedoch viele gängige + Abstraktionen für Nebenläufigkeit. +] else { todo } + +#let ln_drop = link("https://doc.rust-lang.org/core/ops/trait.Drop.html")[`Drop`] +#if lang == "en" [ + One such _synchronisation primitive_ is a mutex, short for mutual + exclusion. These constructs ensure exclusive access to a variable, such + as our counter. A thread can attempt to _lock_ (or _acquire_) + the mutex, and either succeeds immediately, or blocks waiting for the + lock to be acquired, or returns an error that the mutex could not be + locked. While that thread holds the lock, it is granted access to the + protected data. When the thread is done, it _unlocks_ (or + _releases_) the mutex, allowing another thread to lock it. In Rust, + we would usually implement the unlock using the #ln_drop + trait to ensure it is always released when the mutex goes out of scope. +] else if lang == "de" [ + Ein solches _Synchronisationsprimitiv_ ist der Mutex (kurz für + „mutual exclusion", also gegenseitiger Ausschluss). Diese Konstrukte + gewährleisten den exklusiven Zugriff auf eine Variable, wie etwa unseren + Zähler. Ein Thread kann versuchen, den Mutex zu _sperren_ (oder zu + _erwerben_); dabei ist er entweder sofort erfolgreich, blockiert, + bis die Sperre verfügbar ist, oder erhält eine Fehlermeldung, dass der + Mutex nicht gesperrt werden konnte. Solange der Thread die Sperre hält, + hat er Zugriff auf die geschützten Daten. Ist der Thread fertig, + _entsperrt_ (oder _gibt_) er den Mutex _frei_, sodass ein + anderer Thread ihn sperren kann. In Rust würden wir die Freigabe + üblicherweise mithilfe des + #ln_drop;-Traits + implementieren; so ist sichergestellt, dass der Mutex immer freigegeben + wird, sobald er den Gültigkeitsbereich (Scope) verlässt. +] else { todo } + +#if lang == "en" [ + Using a mutex with interrupt handlers can be tricky: it is not normally + acceptable for the interrupt handler to block, and it would be + especially disastrous for it to block waiting for the main thread to + release a lock, since we would then _deadlock_ (the main thread + will never release the lock because execution stays in the interrupt + handler). Deadlocking is not considered unsafe: it is possible even in + safe Rust. +] else if lang == "de" [ + Die Verwendung eines Mutex in Interrupt-Handlern kann tückisch sein: + Normalerweise darf ein Interrupt-Handler nicht blockieren. Besonders + fatal wäre es, wenn er blockieren würde, während er darauf wartet, dass + der Haupt-Thread eine Sperre (Lock) freigibt, da dies zu einem + _Deadlock_ führen würde (der Haupt-Thread würde die Sperre niemals + freigeben, da die Ausführung im Interrupt-Handler verbleibt). Ein + Deadlock gilt nicht als „unsicher" (unsafe): Er ist selbst in sicherem + Rust möglich. +] else { todo } + +#if lang == "en" [ + To avoid this behaviour entirely, we could implement a mutex which + requires a critical section to lock, just like our counter example. So + long as the critical section must last as long as the lock, we can be + sure we have exclusive access to the wrapped variable without even + needing to track the lock/unlock state of the mutex. +] else if lang == "de" [ + Um dieses Verhalten vollständig zu vermeiden, könnten wir einen Mutex + implementieren, der für das Sperren eine kritische Sektion erfordert -- + genau wie in unserem Zähler-Beispiel. Solange die Dauer der kritischen + Sektion der Dauer der Sperre entspricht, ist sichergestellt, dass wir + exklusiven Zugriff auf die gekapselte Variable haben, ohne den + Sperrstatus des Mutex explizit nachverfolgen zu müssen. +] else { todo } + +#if lang == "en" [ + This is in fact done for us in the `cortex_m` crate! We could have + written our counter using it: +] else if lang == "de" [ + Genau dies wird uns bereits durch die `cortex_m`-Crate abgenommen! Wir + hätten unseren Zähler auch unter Verwendung dieser Crate implementieren können: +] else { todo } + +#raw(block: true, lang: "rust", +"use core::cell::Cell; +use cortex_m::interrupt::Mutex; + +static COUNTER: Mutex> = Mutex::new(Cell::new(0)); + +#[entry] +fn main() -> ! { + set_timer_1hz(); + let mut last_state = false; + loop { + let state = read_signal_level(); + if state && !last_state { + interrupt::free(|cs| + COUNTER.borrow(cs).set(COUNTER.borrow(cs).get() + 1)); + } + last_state = state; + } +} + +#[interrupt] +fn timer() { + // " + if lang == "en" { + "We still need to enter a critical section here to satisfy the Mutex." + } else if lang == "de" { + "Wir muessen hier noch einen kritischen Abschnitt betreten, um die + // Mutex-Bedingung zu erfuellen." + } else { todos } + " + interrupt::free(|cs| COUNTER.borrow(cs).set(0)); +} +") + +#let ln_cell = link("https://doc.rust-lang.org/core/cell/struct.Cell.html")[`Cell`] +#if lang == "en" [ + We're now using #ln_cell, + which along with its sibling `RefCell` is used to provide safe interior + mutability. We've already seen `UnsafeCell` which is the bottom layer of + interior mutability in Rust: it allows you to obtain multiple mutable + references to its value, but only with unsafe code. A `Cell` is like an + `UnsafeCell` but it provides a safe interface: it only permits taking a + copy of the current value or replacing it, not taking a reference, and + since it is not Sync, it cannot be shared between threads. These + constraints mean it's safe to use, but we couldn't use it directly in a + `static` variable as a `static` must be Sync. +] else if lang == "de" [ + Wir verwenden nun #ln_cell;; + dieses bietet -- ebenso wie sein Pendant `RefCell` -- eine sichere Form + der „Interior Mutability" (internen Veränderbarkeit). Wir haben bereits + `UnsafeCell` kennengelernt, die unterste Ebene der internen + Veränderbarkeit in Rust: Sie ermöglicht es, mehrere veränderbare + Referenzen auf den enthaltenen Wert zu erhalten, allerdings nur mittels + `unsafe`-Code. Eine `Cell` ähnelt einer `UnsafeCell`, stellt jedoch eine + sichere Schnittstelle bereit: Sie erlaubt lediglich das Kopieren des + aktuellen Werts oder dessen Austausch, nicht jedoch den Zugriff über + eine Referenz; zudem ist sie nicht `Sync` und kann daher nicht zwischen + Threads geteilt werden. Aufgrund dieser Einschränkungen ist ihre + Verwendung sicher, allerdings lässt sie sich nicht direkt in einer + `static`-Variablen einsetzen, da `static`-Elemente die Eigenschaft + `Sync` erfüllen müssen. +] else { todo } + +#if lang == "en" [ + So why does the example above work? The `Mutex` implements Sync for + any `T` which is Send --- such as a `Cell`. It can do this safely + because it only gives access to its contents during a critical section. + We're therefore able to get a safe counter with no unsafe code at all! +] else if lang == "de" [ + Warum funktioniert das obige Beispiel? Der `Mutex` implementiert + `Sync` für jedes `T`, das `Send` ist -- wie beispielsweise eine `Cell`. + Dies ist sicher möglich, da der Zugriff auf den Inhalt nur während eines + kritischen Abschnitts gewährt wird. Dadurch erhalten wir einen sicheren + Zähler ohne jeglichen unsicheren Code! +] else { todo } + +#if lang == "en" [ + This is great for simple types like the `u32` of our counter, but what + about more complex types which are not Copy? An extremely common example + in an embedded context is a peripheral struct, which generally is not + Copy. For that, we can turn to `RefCell`. +] else if lang == "de" [ + Das ist ideal für einfache Typen wie den `u32` unseres Zählers. Was aber + ist mit komplexeren Typen, die nicht `Copy` sind? Ein sehr häufiges + Beispiel in eingebetteten Systemen ist eine Peripheriestruktur, die in + der Regel nicht `Copy` ist. Für diesen Fall können wir `RefCell` + verwenden. +] else { todo } + +== #(if lang == "en" [Sharing Peripherals] + else if lang == "de" [Gemeinsame Nutzung von Peripheriegeräten] + else { todo }) + +#if lang == "en" [ + Device crates generated using `svd2rust` and similar abstractions + provide safe access to peripherals by enforcing that only one instance + of the peripheral struct can exist at a time. This ensures safety, but + makes it difficult to access a peripheral from both the main thread and + an interrupt handler. +] else if lang == "de" [ + Mit `svd2rust` und ähnlichen Abstraktionen erzeugte Device-Crates + ermöglichen einen sicheren Zugriff auf Peripheriekomponenten, indem sie + sicherstellen, dass zu jedem Zeitpunkt nur eine einzige Instanz der + entsprechenden Peripherie-Struktur existiert. Dies gewährleistet zwar + die Sicherheit, erschwert jedoch den gleichzeitigen Zugriff auf eine + Peripheriekomponente sowohl aus dem Haupt-Thread als auch aus einem + Interrupt-Handler heraus. +] else { todo } + +#let ln_refcell = link("https://doc.rust-lang.org/core/cell/struct.RefCell.html")[`RefCell`] +#if lang == "en" [ + To safely share peripheral access, we can use the `Mutex` we saw before. + We'll also need to use #ln_refcell, + which uses a runtime check to ensure only one reference to a peripheral + is given out at a time. This has more overhead than the plain `Cell`, + but since we are giving out references rather than copies, we must be + sure only one exists at a time. +] else if lang == "de" [ + Um den Zugriff auf Peripheriekomponenten sicher zu teilen, können wir + den bereits bekannten `Mutex` verwenden. Zudem benötigen wir + #link("https://doc.rust-lang.org/core/cell/struct.RefCell.html")[`RefCell`]\; + dieses stellt mittels einer Laufzeitprüfung sicher, dass jeweils nur + eine einzige Referenz auf eine Peripheriekomponente ausgegeben wird. + Dies ist zwar mit einem höheren Overhead verbunden als bei einem + einfachen `Cell`, doch da wir Referenzen anstatt Kopien weitergeben, + müssen wir sicherstellen, dass zu jedem Zeitpunkt nur eine einzige + Referenz existiert. +] else { todo } + +#if lang == "en" [ + Finally, we'll also have to account for somehow moving the peripheral + into the shared variable after it has been initialised in the main code. + To do this we can use the `Option` type, initialised to `None` and later + set to the instance of the peripheral. +] else if lang == "de" [ + Schließlich müssen wir auch berücksichtigen, wie die + Peripheriekomponente in die gemeinsam genutzte Variable übertragen + werden kann, nachdem sie im Hauptcode initialisiert wurde. Hierfür + können wir den Typ `Option` verwenden, der zunächst mit `None` + initialisiert und später auf die Instanz der Peripheriekomponente + gesetzt wird. +] else { todo } + +#raw(block: true, lang: "rust", +"use core::cell::RefCell; +use cortex_m::interrupt::{self, Mutex}; +use stm32f4::stm32f405; + +static MY_GPIO: Mutex>> = + Mutex::new(RefCell::new(None)); + +#[entry] +fn main() -> ! { + // " + if lang == "en" { + "Obtain the peripheral singletons and configure it. + // This example is from an svd2rust-generated crate, but + // most embedded device crates will be similar." + } else if lang == "de" { + "Die Peripherie-Singletons abrufen und konfigurieren. + // Dieses Beispiel stammt aus einer mit svd2rust erstellten Crate, die + // meisten Crates für eingebettete Geräte sind jedoch ähnlich." + } else { todos } + " + let dp = stm32f405::Peripherals::take().unwrap(); + let gpioa = &dp.GPIOA; + + // " + if lang == "en" { + "Some sort of configuration function. + // Assume it sets PA0 to an input and PA1 to an output." + } else if lang == "de" { + "Eine Art Konfigurationsfunktion. + // Gehen wir davon aus, dass es PA0 als Eingang und PA1 als Ausgang + // konfiguriert." + } else { todos } + " + configure_gpio(gpioa); + + // " + if lang == "en" { + "Store the GPIOA in the mutex, moving it." + } else if lang == "de" { + "Speichere GPIOA im Mutex und verschiebe es dabei." + } else { todos } + " + interrupt::free(|cs| MY_GPIO.borrow(cs).replace(Some(dp.GPIOA))); + // " + if lang == "en" { + "We can no longer use `gpioa` or `dp.GPIOA`, and instead have to" + } else if lang == "de" { + "Wir koennen `gpioa` oder `dp.GPIOA` nicht mehr verwenden, sondern + // muessen stattdessen ueber den Mutex darauf zugreifen." + } else { todos } + " + // access it via the mutex. + + // " + if lang == "en" { + "Be careful to enable the interrupt only after setting MY_GPIO: + // otherwise the interrupt might fire while it still contains None, + // and as-written (with `unwrap()`), it would panic." + } else if lang == "de" { + "Achten Sie darauf, den Interrupt erst nach dem Setzen von MY_GPIO zu + // aktivieren: + // Andernfalls koennte der Interrupt ausgeloest werden, solange MY_GPIO + // noch None enthaelt, was – wie implementiert (mit `unwrap()`) – zu einer + // Panic fuehren wuerde." + } else { todos } + " + set_timer_1hz(); + let mut last_state = false; + loop { + // " + if lang == "en" { + "We'll now read state as a digital input, via the mutex" + } else if lang == "de" { + "Wir lesen den Status nun als digitalen Eingang ueber den Mutex aus." + } else { todos } + " + let state = interrupt::free(|cs| { + let gpioa = MY_GPIO.borrow(cs).borrow(); + gpioa.as_ref().unwrap().idr.read().idr0().bit_is_set() + }); + + if state && !last_state { + // " + if lang == "en" { + "Set PA1 high if we've seen a rising edge on PA0." + } else if lang == "de" { + "Setze PA1 auf High, wenn an PA0 eine steigende Flanke erkannt + // wurde." + } else { todos } + " + interrupt::free(|cs| { + let gpioa = MY_GPIO.borrow(cs).borrow(); + gpioa.as_ref().unwrap().odr.modify(|_, w| w.odr1().set_bit()); + }); + } + last_state = state; + } +} + +#[interrupt] +fn timer() { + // " + if lang == "en" { + "This time in the interrupt we'll just clear PA0." + } else if lang == "de" { + "Diesmal setzen wir im Interrupt lediglich PA0 zurueck." + } else { todos } + " + interrupt::free(|cs| { + // " + if lang == "en" { + "We can use `unwrap()` because we know the interrupt wasn't enabled + // until after MY_GPIO was set; otherwise we should handle the potential + // for a None value." + } else if lang == "de" { + "Wir koennen `unwrap()` verwenden, da wir wissen, dass der Interrupt + // erst aktiviert wurde, nachdem `MY_GPIO` gesetzt war; andernfalls + // muessten wir den moeglichen Fall eines `None`-Werts behandeln." + } else { todos } + " + let gpioa = MY_GPIO.borrow(cs).borrow(); + gpioa.as_ref().unwrap().odr.modify(|_, w| w.odr1().clear_bit()); + }); +} +") + +#if lang == "en" [ + That's quite a lot to take in, so let's break down the important lines. +] else if lang == "de" [ + Das ist eine ganze Menge, die man erst einmal verarbeiten muss -- + schauen wir uns also die wichtigen Zeilen genauer an. +] else { todo } + +```rust +static MY_GPIO: Mutex>> = + Mutex::new(RefCell::new(None)); +``` + +#if lang == "en" [ + Our shared variable is now a `Mutex` around a `RefCell` which contains + an `Option`. The `Mutex` ensures we only have access during a critical + section, and therefore makes the variable Sync, even though a plain + `RefCell` would not be Sync. The `RefCell` gives us interior mutability + with references, which we'll need to use our `GPIOA`. The `Option` lets + us initialise this variable to something empty, and only later actually + move the variable in. We cannot access the peripheral singleton + statically, only at runtime, so this is required. +] else if lang == "de" [ + Unsere gemeinsam genutzte Variable ist nun ein `Mutex`, der einen + `RefCell` umschließt, welcher wiederum ein `Option` enthält. Der `Mutex` + stellt sicher, dass der Zugriff nur innerhalb eines kritischen + Abschnitts erfolgt, und macht die Variable dadurch `Sync` -- auch wenn + ein einfacher `RefCell` dies nicht wäre. Der `RefCell` ermöglicht uns + „Interior Mutability" (interne Veränderbarkeit) bei Verwendung von + Referenzen, was wir für unseren `GPIOA` benötigen. Das `Option` erlaubt + es uns, die Variable zunächst leer zu initialisieren und den + eigentlichen Wert erst später hineinzubewegen. Da wir nicht statisch, + sondern nur zur Laufzeit auf das Peripherie-Singleton zugreifen können, + ist dieses Vorgehen erforderlich. +] else { todo } + +```rust +interrupt::free(|cs| MY_GPIO.borrow(cs).replace(Some(dp.GPIOA))); +``` + +#if lang == "en" [ + Inside a critical section we can call `borrow()` on the mutex, which + gives us a reference to the `RefCell`. We then call `replace()` to move + our new value into the `RefCell`. +] else if lang == "de" [ + Innerhalb eines kritischen Abschnitts können wir `borrow()` auf dem + Mutex aufrufen, was uns eine Referenz auf die `RefCell` liefert. + Anschließend rufen wir `replace()` auf, um unseren neuen Wert in die + `RefCell` zu verschieben. +] else { todo } + +```rust +interrupt::free(|cs| { + let gpioa = MY_GPIO.borrow(cs).borrow(); + gpioa.as_ref().unwrap().odr.modify(|_, w| w.odr1().set_bit()); +}); +``` + +#if lang == "en" [ + Finally, we use `MY_GPIO` in a safe and concurrent fashion. The critical + section prevents the interrupt firing as usual, and lets us borrow the + mutex. The `RefCell` then gives us an `&Option`, and tracks how + long it remains borrowed - once that reference goes out of scope, the + `RefCell` will be updated to indicate it is no longer borrowed. +] else if lang == "de" [ + Schließlich verwenden wir `MY_GPIO` auf sichere und nebenläufige Weise. + Der kritische Abschnitt verhindert wie üblich das Auslösen von + Interrupts und ermöglicht es uns, den Mutex auszuleihen. Die `RefCell` + liefert uns dann ein `&Option` und überwacht die Dauer der + Ausleihe; sobald die Referenz ihren Gültigkeitsbereich verlässt, wird + der Status der `RefCell` aktualisiert, um anzuzeigen, dass sie nicht + mehr ausgeliehen ist. +] else { todo } + +#if lang == "en" [ + Since we can't move the `GPIOA` out of the `&Option`, we need to convert + it to an `&Option<&GPIOA>` with `as_ref()`, which we can finally + `unwrap()` to obtain the `&GPIOA` which lets us modify the peripheral. +] else if lang == "de" [ + Da wir das `GPIOA`-Objekt nicht aus dem `&Option` herausverschieben + können, müssen wir es mittels `as_ref()` in ein `&Option<&GPIOA>` + umwandeln. Dieses können wir schließlich mit `unwrap()` auflösen, um das + `&GPIOA` zu erhalten, das uns den Zugriff zur Modifikation der + Peripherieeinheit ermöglicht. +] else { todo } + +#if lang == "en" [ + If we need a mutable reference to a shared resource, then `borrow_mut` + and `deref_mut` should be used instead. The following code shows an + example using the TIM2 timer. +] else if lang == "de" [ + Wenn wir eine veränderbare Referenz auf eine gemeinsam genutzte + Ressource benötigen, sollten stattdessen `borrow_mut` und `deref_mut` + verwendet werden. Der folgende Code zeigt ein Beispiel unter Verwendung + des TIM2-Timers. +] else { todo } + +#raw(block: true, lang: "rust", +"use core::cell::RefCell; +use core::ops::DerefMut; +use cortex_m::interrupt::{self, Mutex}; +use cortex_m::asm::wfi; +use stm32f4::stm32f405; + +static G_TIM: Mutex>>> = + Mutex::new(RefCell::new(None)); + +#[entry] +fn main() -> ! { + let mut cp = cm::Peripherals::take().unwrap(); + let dp = stm32f405::Peripherals::take().unwrap(); + + // " + if lang == "en" { + "Some sort of timer configuration function. + // Assume it configures the TIM2 timer, its NVIC interrupt, + // and finally starts the timer." + } else if lang == "de" { + "Eine Art Timer-Konfigurationsfunktion. + // Angenommen, sie konfiguriert den TIM2-Timer sowie dessen NVIC-Interrupt + // und startet schließlich den Timer." + } else { todos } + " + let tim = configure_timer_interrupt(&mut cp, dp); + + interrupt::free(|cs| { + G_TIM.borrow(cs).replace(Some(tim)); + }); + + loop { + wfi(); + } +} + +#[interrupt] +fn timer() { + interrupt::free(|cs| { + if let Some(ref mut tim)) = G_TIM.borrow(cs).borrow_mut().deref_mut() { + tim.start(1.hz()); + } + }); +} +") + +#if lang == "en" [ + Whew! This is safe, but it is also a little unwieldy. Is there anything + else we can do? +] else if lang == "de" [ + Puh! Das ist zwar sicher, aber auch etwas unhandlich. Können wir sonst + noch etwas tun? +] else { todo } + +== RTIC + +#let url_rtic = "https://rtic.rs" +#if lang == "en" [ + One alternative is the #link(url_rtic)[RTIC framework], short for Real Time + Interrupt-driven Concurrency. It enforces static priorities and tracks + accesses to `static mut` variables ("resources") to statically ensure + that shared resources are always accessed safely, without requiring the + overhead of always entering critical sections and using reference + counting (as in `RefCell`). This has a number of advantages such as + guaranteeing no deadlocks and giving extremely low time and memory + overhead. +] else if lang == "de" [ + Eine Alternative ist das + #link(url_rtic)[RTIC-Framework] (kurz für _Real + Time Interrupt-driven Concurrency_). Es erzwingt statische Prioritäten + und überwacht Zugriffe auf `static mut`-Variablen („Ressourcen"), um + statisch sicherzustellen, dass auf gemeinsam genutzte Ressourcen stets + sicher zugegriffen wird -- ohne den Overhead, der durch das ständige + Betreten kritischer Abschnitte oder die Verwendung von Referenzzählung + (wie bei `RefCell`) entstünde. Dies bietet eine Reihe von Vorteilen, wie + etwa die Garantie von Deadlock-Freiheit sowie einen extrem geringen + Zeit- und Speicheraufwand. +] else { todo } + +#if lang == "en" [ + RTIC comes with as asynchronous executor, so your software tasks are + `async` functions where you can use `async` APIs in addition to regular + synchronous APIs. +] else if lang == "de" [ + RTIC bietet einen asynchronen Executor, sodass Ihre Software-Tasks als + `async`-Funktionen implementiert sind; darin können Sie neben + herkömmlichen synchronen APIs auch `async`-APIs nutzen. +] else { todo } + +#if lang == "en" [ + The framework also includes other features like message passing, which + reduces the need for explicit shared state, and the ability to schedule + tasks to run at a given time, which can be used to implement periodic + tasks. Check out #link(url_rtic)[the documentation] for more + information! +] else if lang == "de" [ + Das Framework umfasst zudem Funktionen wie Message Passing -- was den + Bedarf an explizit gemeinsam genutztem Zustand (Shared State) verringert + -- sowie die Möglichkeit, Tasks für einen bestimmten Zeitpunkt zu + planen, womit sich beispielsweise periodische Aufgaben realisieren + lassen. Weitere Informationen finden Sie in + #link(url_rtic)[der Dokumentation]! +] else { todo } + +== Embassy + +#let url_executor = "https://docs.rs/embassy-executor/latest/embassy_executor/" +#if lang == "en" [ + Embassy is an ecosystem of libraries which focus on using the `async` / + `await` syntax included in Rust for concurrency. The core of embassy is + its #link(url_executor)[asynchronous executor] + which supports most common MCU architectures. +] else if lang == "de" [ + Embassy ist ein Ökosystem von Bibliotheken, die sich auf die Nutzung der + in Rust enthaltenen `async`/`await`-Syntax für Nebenläufigkeit + konzentrieren. Das Herzstück von Embassy ist sein + #link(url_executor)[asynchroner Executor], + der die gängigsten MCU-Architekturen unterstützt. +] else { todo } + +#let url_em_time = "https://docs.rs/embassy-time/latest/embassy_time/" +#let ln_en_sync = link("https://docs.embassy.dev/embassy-sync/git/default/index.html")[embassy-sync] +#if lang == "en" [ + embassy also takes a battery-included approach and offers many other + components, for example: + - #link(url_em_time)[Time library] + - Various HAL libraries which also provide the time library support. + - #ln_en_sync for synchronization primitives +] else if lang == "de" [ + Embassy verfolgt zudem einen „Batteries-included"-Ansatz und bietet + viele weitere Komponenten an, zum Beispiel: + - #link(url_em_time)[Zeit-Bibliothek] + - Verschiedene HAL-Bibliotheken, die auch Unterstützung für die + Zeitbibliothek bieten. + - #ln_en_sync für Synchronisationsprimitive +] else { todo } + +#let url_em_site = "https://embassy.dev/" +#let url_em_book = "https://embassy.dev/book/" +#if lang == "en" [ + You can check the #link(url_em_site)[website] and the + #link(url_em_book)[book] for more information. +] else if lang == "de" [ + Weitere Informationen finden Sie auf der + #link(url_em_site)[Website] und im #link(url_em_book)[Buch]. +] else { todo } + +== #(if lang == "en" [Real Time Operating Systems] + else if lang == "de" [Echtzeitbetriebssysteme] + else { todo }) + +#let ln_freertos = link("https://freertos.org/")[FreeRTOS] +#let ln_chibi = link("http://chibios.org/")[ChibiOS] +#if lang == "en" [ + Another common model for embedded concurrency is the real-time operating + system (RTOS). While currently less well explored in Rust, they are + widely used in traditional embedded development. Open source examples + include #ln_freertos and #ln_chibi. These RTOSs provide support for + running multiple application threads which the CPU swaps between, either + when the threads yield control (called cooperative multitasking) or + based on a regular timer or interrupts (preemptive multitasking). The + RTOS typically provide mutexes and other synchronisation primitives, and + often interoperate with hardware features such as DMA engines. +] else if lang == "de" [ + Ein weiteres verbreitetes Modell für Nebenläufigkeit in eingebetteten + Systemen ist das Echtzeitbetriebssystem (RTOS). Während diese in Rust + bislang weniger stark erforscht sind, kommen sie in der klassischen + Entwicklung eingebetteter Systeme häufig zum Einsatz. Zu den + Open-Source-Beispielen zählen #ln_freertos und #ln_chibi. + Solche Echtzeitbetriebssysteme unterstützen die Ausführung mehrerer + Anwendungsthreads, zwischen denen die CPU wechselt -- entweder wenn die + Threads die Kontrolle freiwillig abgeben (kooperatives Multitasking) + oder gesteuert durch Timer bzw. Interrupts (präemptives Multitasking). + Typischerweise stellen RTOS Mechanismen wie Mutexe und andere + Synchronisationsprimitive bereit und interagieren häufig mit + Hardwarefunktionen wie DMA-Controllern. +] else { todo } + +#if lang == "en" [ + At the time of writing, there are not many Rust RTOS examples to point + to, but it's an interesting area so watch this space! +] else if lang == "de" [ + Zum jetzigen Zeitpunkt gibt es noch nicht viele Beispiele für Rust-RTOS, + auf die man verweisen könnte; es handelt sich jedoch um ein + interessantes Gebiet -- bleiben Sie also dran! +] else { todo } + +== #(if lang == "en" [Multiple Cores] + else if lang == "de" [Mehrere Kerne] + else { todo }) + +#if lang == "en" [ + It is becoming more common to have two or more cores in embedded + processors, which adds an extra layer of complexity to concurrency. All + the examples using a critical section (including the + `cortex_m::interrupt::Mutex`) assume the only other execution thread is + the interrupt thread, but on a multi-core system that's no longer true. + Instead, we'll need synchronisation primitives designed for multiple + cores (also called SMP, for symmetric multi-processing). +] else if lang == "de" [ + Es wird immer üblicher, eingebettete Prozessoren mit zwei oder mehr + Kernen auszustatten, was die Parallelverarbeitung zusätzlich + verkompliziert. Alle Beispiele mit kritischen Abschnitten + (einschließlich `cortex_m::interrupt::Mutex`) gehen davon aus, dass der + einzige weitere Ausführungsthread der Interrupt-Thread ist. Auf einem + Mehrkernsystem trifft dies jedoch nicht mehr zu. Stattdessen benötigen + wir Synchronisierungsprimitive, die speziell für Mehrkernsysteme (auch + SMP, für symmetrisches Multiprocessing, genannt) entwickelt wurden. +] else { todo } + +#if lang == "en" [ + These typically use the atomic instructions we saw earlier, since the + processing system will ensure that atomicity is maintained over all cores. +] else if lang == "de" [ + Diese verwenden typischerweise die bereits erwähnten atomaren Befehle, + da das Verarbeitungssystem die Atomarität über alle Kerne hinweg + gewährleistet. +] else { todo } + +#if lang == "en" [ + Covering these topics in detail is currently beyond the scope of this + book, but the general patterns are the same as for the single-core case. +] else if lang == "de" [ + Eine detaillierte Behandlung dieser Themen würde den Rahmen dieses + Buches sprengen, die allgemeinen Muster sind jedoch dieselben wie im + Einzelkernfall. +] else { todo } diff --git a/typ/config.typ b/typ/config.typ new file mode 100644 index 00000000..0b85463e --- /dev/null +++ b/typ/config.typ @@ -0,0 +1,42 @@ +#let default_lang = sys.inputs.at("default-lang", default: "en") +#let lang = sys.inputs.at("lang", default: default_lang) +#let languages = ( + "en": [English], + "de": [German], + "uk": [Ukrainian], + "zh": [Chinese] +) +#let tgt = sys.inputs.at("target", default: "html") +#let whole = int(tgt == "pdf") + +// str: use in code blocks +#let todos = (if lang == "de" { "unübersetzt" } + else if lang == "uk" { "не перекладено" } + else if lang == "zh" { "未翻译" } + else { "untranslated" } +) +// content: use in other places +#let todo = text( + fill: red, + todos +) +// str: use in code blocks +#let todoupds(l) = { + assert(l in languages, message: "add language name") + if lang == "de" { "die Übersetzung ist veraltet" } + else if lang == "uk" { "переклад застарів" } + else if lang == "zh" { "翻译已过时" } + else { "translation is outdated" } +} // content: use in other places +#let todoupd(l) = text( + fill: orange, + todoupds(l) +) + +#let book_title = "The Embedded Rust Book" + +#let h1(it, offset: 0) = if tgt == "pdf" { + heading(it, depth: 1, offset: offset) +} else { + title(it) +} diff --git a/typ/design-patterns/hal/checklist.typ b/typ/design-patterns/hal/checklist.typ new file mode 100644 index 00000000..0a620f90 --- /dev/null +++ b/typ/design-patterns/hal/checklist.typ @@ -0,0 +1,36 @@ +#import "../../config.typ": * + +#h1(offset: whole*2, + if lang == "en" [HAL Design Patterns Checklist] + else if lang == "de" [Checkliste HAL-Design-Muster] + else { todo }) + +#set heading(offset: whole*3) + +#if lang == "en" [ + *Naming* (_crate aligns with Rust naming conventions_) + - ☐ The crate is named appropriately (#link()[C-CRATE-NAME]) + - *Interoperability* (_crate interacts nicely with other library functionality_) + - ☐ Wrapper types provide a destructor method (#link()[C-FREE]) + - ☐ HALs reexport their register access crate (#link()[C-REEXPORT-PAC]) + - ☐ Types implement the `embedded-hal` traits (#link()[C-HAL-TRAITS]) + - *Predictability* (_crate enables legible code that acts how it looks_) + - ☐ Constructors are used instead of extension traits (#link()[C-CTOR]) + - *GPIO Interfaces* (_GPIO Interfaces follow a common pattern_) + - ☐ Pin types are zero-sized by default (#link()[C-ZST-PIN]) + - ☐ Pin types provide methods to erase pin and port (#link()[C-ERASED-PIN]) + - ☐ Pin state should be encoded as type parameters (#link()[C-PIN-STATE]) +] else if lang == "de" [ + - *Benennung* (_Crate richtet sich nach den Namenskonventionen von Rust_) + - ☐ Das Crate trägt einen passenden Namen. (#link()[C-CRATE-NAME]) + - *Interoperabilität* (_das Crate harmoniert gut mit der Funktionalität anderer Bibliotheken_) + - ☐ Wrapper-Typen stellen eine Destruktor-Methode bereit. (#link()[C-FREE]) + - ☐ HALs exportieren ihr Registerzugriffs-Crate erneut. (#link()[C-REEXPORT-PAC]) + - ☐ Typen implementieren die `embedded-hal`-Traits. (#link()[C-HAL-TRAITS]) + - *Vorhersehbarkeit* (_Das Crate ermöglicht gut lesbaren Code, der sich so verhält, wie er aussieht_) + - ☐ Anstelle von Extension Traits werden Konstruktoren verwendet. (#link()[C-CTOR]) + - *GPIO-Schnittstellen* (_GPIO-Schnittstellen folgen einem einheitlichen Muster_) + - ☐ Pin-Typen haben standardmäßig die Größe Null. (#link()[C-ZST-PIN]) + - ☐ Pin-Typen stellen Methoden zum Löschen von Pins und Ports bereit. (#link()[C-ERASED-PIN]) + - ☐ Der Pin-Zustand sollte als Typparameter kodiert werden. (#link()[C-PIN-STATE]) +] else { todo } diff --git a/typ/design-patterns/hal/gpio.typ b/typ/design-patterns/hal/gpio.typ new file mode 100644 index 00000000..c5cc691b --- /dev/null +++ b/typ/design-patterns/hal/gpio.typ @@ -0,0 +1,293 @@ +#import "../../config.typ": * + +#h1(offset: whole*2, + if lang == "en" [Recommendations for GPIO Interfaces] + else if lang == "de" [Empfehlungen für GPIO-Schnittstellen] + else { todo }) + +#set heading(offset: whole*3) + += #(if lang == "en" [Pin types are zero-sized by default] + else if lang == "de" [Pin-Typen haben standardmäßig die Größe Null] + else { todo }) (C-ZST-PIN) + + +#if lang == "en" [ + GPIO Interfaces exposed by the HAL should provide dedicated zero-sized + types for each pin on every interface or port, resulting in a zero-cost + GPIO abstraction when all pin assignments are statically known. +] else if lang == "de" [ + Die von der HAL bereitgestellten GPIO-Schnittstellen sollten für jeden + Pin jeder Schnittstelle oder jedes Ports einen dedizierten Datentyp der + Größe Null bereitstellen. Dies führt zu einer kostenlosen + GPIO-Abstraktion, wenn alle Pinbelegungen statisch bekannt sind. +] else { todo } + +#if lang == "en" [ + Each GPIO Interface or Port should implement a `split` method returning + a struct with every pin. +] else if lang == "de" [ + Jede GPIO-Schnittstelle oder jeder Port sollte eine `split`-Methode + implementieren, die eine Struktur mit allen Pins zurückgibt. +] else { todo } + +#if lang == "en" [ + Example: +] else if lang == "de" [ + Beispiel: +] else { todo } + +```rust +pub struct PA0; +pub struct PA1; +// ... + +pub struct PortA; + +impl PortA { + pub fn split(self) -> PortAPins { + PortAPins { + pa0: PA0, + pa1: PA1, + // ... + } + } +} + +pub struct PortAPins { + pub pa0: PA0, + pub pa1: PA1, + // ... +} +``` + += #(if lang == "en" [Pin types provide methods to erase pin and port] + else if lang == "de" [Pin-Typen stellen Methoden zum Löschen von Pins und Ports bereit] + else { todo }) (C-ERASED-PIN) + + +#if lang == "en" [ + Pins should provide type erasure methods that move their properties from + compile time to runtime, and allow more flexibility in applications. +] else if lang == "de" [ + Pins sollten Methoden zur Typeliminierung (Type Erasure) bereitstellen, + die ihre Eigenschaften von der Kompilierzeit in die Laufzeit verlagern + und so mehr Flexibilität in Anwendungen ermöglichen. +] else { todo } + +#if lang == "en" [ + Example: +] else if lang == "de" [ + Beispiel: +] else { todo } + +#raw(block: true, lang: "rust", +"/// Port A, pin 0. +pub struct PA0; + +impl PA0 { + pub fn erase_pin(self) -> PA { + PA { pin: 0 } + } +} + +/// A pin on port A. +pub struct PA { + /// " + if lang == "en" { + "The pin number." + } else { todos } + " + pin: u8, +} + +impl PA { + pub fn erase_port(self) -> Pin { + Pin { + port: Port::A, + pin: self.pin, + } + } +} + +pub struct Pin { + port: Port, + pin: u8, + // " + if lang == "en" { + "(these fields can be packed to reduce the memory footprint)" + } else if lang == "de" { + "(Diese Felder koennen gepackt werden, um den Speicherbedarf zu + // verringern.)" + } else { todos } + " +} + +enum Port { + A, + B, + C, + D, +} +") + += #(if lang == "en" [Pin state should be encoded as type parameters] + else if lang == "de" [Der Pin-Zustand sollte als Typparameter kodiert werden] + else { todo }) (C-PIN-STATE) + + +#if lang == "en" [ + Pins may be configured as input or output with different characteristics + depending on the chip or family. This state should be encoded in the + type system to prevent use of pins in incorrect states. +] else if lang == "de" [ + Je nach Chip oder Chipfamilie können Pins als Eingang oder Ausgang mit + unterschiedlichen Eigenschaften konfiguriert werden. Dieser Zustand + sollte im Typsystem abgebildet werden, um eine Verwendung der Pins in + einem falschen Zustand zu verhindern. +] else { todo } + +#if lang == "en" [ + Additional, chip-specific state (eg. drive strength) may also be encoded + in this way, using additional type parameters. +] else if lang == "de" [ + Auch zusätzliche, chip-spezifische Zustände (z. B. die Treiberstärke) + lassen sich auf diese Weise mittels zusätzlicher Typparameter kodieren. +] else { todo } + +#if lang == "en" [ + Methods for changing the pin state should be provided as `into_input` + and `into_output` methods. +] else if lang == "de" [ + Methoden zur Änderung des Pin-Zustands sollten als `into_input` und + `into_output` bereitgestellt werden. +] else { todo } + +#if lang == "en" [ + Additionally, `with_{input,output}_state` methods should be provided + that temporarily reconfigure a pin in a different state without moving it. +] else if lang == "de" [ + Zusätzlich sollten Methoden wie `with_{input,output}_state` angeboten + werden, die einen Pin vorübergehend in einen anderen Zustand versetzen, + ohne ihn zu verschieben (d.~h. ohne Eigentumsübergang). +] else { todo } + +#if lang == "en" [ + The following methods should be provided for every pin type (that is, + both erased and non-erased pin types should provide the same API): +] else if lang == "de" [ + Für jeden Pin-Typ sollten die folgenden Methoden bereitgestellt werden + (das heißt, sowohl „erased" als auch „non-erased" Pin-Typen müssen + dieselbe API bieten): +] else { todo } +- `pub fn into_input(self, input: N) -> Pin` +- `pub fn into_output(self, output: N) -> Pin` +- ```ignore + pub fn with_input_state( + &mut self, + input: N, + f: impl FnOnce(&mut PA1) -> R, + ) -> R + ``` +- ```ignore + pub fn with_output_state( + &mut self, + output: N, + f: impl FnOnce(&mut PA1) -> R, + ) -> R + ``` + +#if lang == "en" [ + Pin state should be bounded by sealed traits. Users of the HAL should + have no need to add their own state. The traits can provide HAL-specific + methods required to implement the pin state API. +] else if lang == "de" [ + Der Pin-Zustand sollte durch „Sealed Traits" eingeschränkt sein. Nutzer + des HAL sollten keinen eigenen Zustand hinzufügen müssen. Die Traits + können HAL-spezifische Methoden bereitstellen, die für die + Implementierung der Pin-Zustands-API erforderlich sind. +] else { todo } + +#if lang == "en" [ + Example: +] else if lang == "de" [ + Beispiel: +] else { todo } + +#raw(block: true, lang: "rust", +"# use std::marker::PhantomData; +mod sealed { + pub trait Sealed {} +} + +pub trait PinState: sealed::Sealed {} +pub trait OutputState: sealed::Sealed {} +pub trait InputState: sealed::Sealed { + // ... +} + +pub struct Output { + _p: PhantomData, +} + +impl PinState for Output {} +impl sealed::Sealed for Output {} + +pub struct PushPull; +pub struct OpenDrain; + +impl OutputState for PushPull {} +impl OutputState for OpenDrain {} +impl sealed::Sealed for PushPull {} +impl sealed::Sealed for OpenDrain {} + +pub struct Input { + _p: PhantomData, +} + +impl PinState for Input {} +impl sealed::Sealed for Input {} + +pub struct Floating; +pub struct PullUp; +pub struct PullDown; + +impl InputState for Floating {} +impl InputState for PullUp {} +impl InputState for PullDown {} +impl sealed::Sealed for Floating {} +impl sealed::Sealed for PullUp {} +impl sealed::Sealed for PullDown {} + +pub struct PA1 { + _p: PhantomData, +} + +impl PA1 { + pub fn into_input(self, input: N) -> PA1> { + todo!() + } + + pub fn into_output(self, output: N) -> PA1> { + todo!() + } + + pub fn with_input_state( + &mut self, + input: N, + f: impl FnOnce(&mut PA1) -> R, + ) -> R { + todo!() + } + + pub fn with_output_state( + &mut self, + output: N, + f: impl FnOnce(&mut PA1) -> R, + ) -> R { + todo!() + } +} + +// " + if lang == "en" { + "Same for `PA` and `Pin`, and other pin types." + } else if lang == "de" { + "Dasselbe gilt fuer `PA` und `Pin` sowie andere Pin-Typen." + } else { todos } + " +") diff --git a/typ/design-patterns/hal/index.typ b/typ/design-patterns/hal/index.typ new file mode 100644 index 00000000..d5408bba --- /dev/null +++ b/typ/design-patterns/hal/index.typ @@ -0,0 +1,32 @@ +#import "../../config.typ": * + +#h1(offset: whole, + if lang == "en" [HAL Design Patterns] + else if lang == "de" [HAL-Design-Muster] + else { todo }) +#set heading(offset: whole*2) + +#let url_quide = "https://rust-lang.github.io/api-guidelines/" +#if lang == "en" [ + This is a set of common and recommended patterns for writing hardware + abstraction layers (HALs) for microcontrollers in Rust. These patterns + are intended to be used in addition to the existing + #link(url_quide)[Rust API Guidelines] + when writing HALs for microcontrollers. + - #link()[Checklist] + - #link()[Naming] + - #link()[Interoperability] + - #link()[Predictability] + - #link()[GPIO] +] else if lang == "de" [ + Hierbei handelt es sich um eine Sammlung bewährter und empfohlener + Muster für die Implementierung von Hardware-Abstraktionsschichten (HALs) + für Mikrocontroller in Rust. Diese Muster sind als Ergänzung zu den + bestehenden #link(url_quide)[Rust-API-Richtlinien] + für die Entwicklung von HALs für Mikrocontroller gedacht. + #link()[Checkliste] + - #link()[Benennung] + - #link()[Interoperabilität] + - #link()[Vorhersehbarkeit] + - #link()[GPIO] +] else { todo } diff --git a/typ/design-patterns/hal/interoperability.typ b/typ/design-patterns/hal/interoperability.typ new file mode 100644 index 00000000..680adde0 --- /dev/null +++ b/typ/design-patterns/hal/interoperability.typ @@ -0,0 +1,116 @@ +#import "../../config.typ": * + +#h1(offset: whole*2, + if lang == "en" [Interoperability] + else if lang == "de" [Interoperabilität] + else { todo }) + +#set heading(offset: whole*3) + += #(if lang == "en" [Wrapper types provide a destructor method] + else if lang == "de" [Wrapper-Typen stellen eine Destruktor-Methode bereit] + else { todo }) (C-FREE) + + +#if lang == "en" [ + Any non-`Copy` wrapper type provided by the HAL should provide a `free` + method that consumes the wrapper and returns back the raw peripheral + (and possibly other objects) it was created from. +] else if lang == "de" [ + Jeder vom HAL bereitgestellte Wrapper-Typ, der nicht `Copy` + implementiert, sollte eine `free`-Methode anbieten; diese verbraucht den + Wrapper und gibt das rohe Peripheriegerät (sowie gegebenenfalls weitere + Objekte), aus dem er erstellt wurde, wieder frei. +] else { todo } + +#if lang == "en" [ + The method should shut down and reset the peripheral if necessary. + Calling `new` with the raw peripheral returned by `free` should not fail + due to an unexpected state of the peripheral. +] else if lang == "de" [ + Die Methode sollte das Peripheriegerät bei Bedarf deaktivieren und + zurücksetzen. Ein Aufruf von `new` mit dem durch `free` zurückgegebenen + rohen Peripheriegerät darf nicht aufgrund eines unerwarteten Zustands + des Geräts fehlschlagen. +] else { todo } + +#if lang == "en" [ + If the HAL type requires other non-`Copy` objects to be constructed (for + example I/O pins), any such object should be released and returned by + `free` as well. `free` should return a tuple in that case. +] else if lang == "de" [ + Falls für die Konstruktion des HAL-Typs weitere Objekte erforderlich + sind, die nicht `Copy` implementieren (zum Beispiel I/O-Pins), sollten + auch diese Objekte durch `free` freigegeben und zurückgegeben werden. In + diesem Fall sollte `free` ein Tupel zurückgeben. +] else { todo } + +#if lang == "en" [ + For example: +] else if lang == "de" [ + Zum Beispiel: +] else { todo } + +```rust +# pub struct TIMER0; +pub struct Timer(TIMER0); + +impl Timer { + pub fn new(periph: TIMER0) -> Self { + Self(periph) + } + + pub fn free(self) -> TIMER0 { + self.0 + } +} +``` + += #(if lang == "en" [HALs reexport their register access crate] + else if lang == "de" [HALs exportieren ihr Registerzugriffs-Crate erneut] + else { todo }) (C-REEXPORT-PAC) + + +#let ln_svd2rust = link("https://github.com/rust-embedded/svd2rust")[svd2rust] +#if lang == "en" [ + HALs can be written on top of #ln_svd2rust;-generated + PACs, or on top of other crates that provide raw register access. HALs + should always reexport the register access crate they are based on in + their crate root. +] else if lang == "de" [ + HALs können auf der Basis von mit + #link("https://github.com/rust-embedded/svd2rust")[svd2rust] generierten + PACs oder anderen Crates implementiert werden, die einen direkten + Registerzugriff ermöglichen. HALs sollten das Crate für den + Registerzugriff, auf dem sie aufbauen, stets an der Wurzel ihres eigenen + Crates erneut exportieren (re-exportieren). +] else { todo } + +#if lang == "en" [ + A PAC should be reexported under the name `pac`, regardless of the + actual name of the crate, as the name of the HAL should already make it + clear what PAC is being accessed. +] else if lang == "de" [ + Ein PAC sollte unter dem Namen `pac` re-exportiert werden -- unabhängig + vom eigentlichen Namen des Crates --, da bereits der Name der HAL + verdeutlichen sollte, auf welches PAC zugegriffen wird. +] else { todo } + += #(if lang == "en" [Types implement the `embedded-hal` traits (C-HAL-TRAITS)] + else if lang == "de" [Typen implementieren die `embedded-hal`-Traits] + else { todo }) + +#let ln_hal = link("https://github.com/rust-embedded/embedded-hal")[`embedded-hal`] +#if lang == "en" [ + Types provided by the HAL should implement all applicable traits + provided by the #ln_hal crate. +] else if lang == "de" [ + Vom HAL bereitgestellte Typen sollten alle anwendbaren Traits + implementieren, die vom #ln_hal;-Crate bereitgestellt werden. +] else { todo } + +#if lang == "en" [ + Multiple traits may be implemented for the same type. +] else if lang == "de" [ + Für denselben Typ können mehrere Traits implementiert werden. +] else { todo } diff --git a/typ/design-patterns/hal/naming.typ b/typ/design-patterns/hal/naming.typ new file mode 100644 index 00000000..12fb6ef9 --- /dev/null +++ b/typ/design-patterns/hal/naming.typ @@ -0,0 +1,25 @@ +#import "../../config.typ": * + +#h1(offset: whole*2, + if lang == "en" [Naming] + else if lang == "de" [Benennung] + else { todo }) + +#set heading(offset: whole*3) + += #(if lang == "en" [The crate is named appropriately] + else if lang == "de" [Das Crate trägt einen passenden Namen] + else { todo }) (C-CRATE-NAME) + + +#if lang == "en" [ + HAL crates should be named after the chip or family of chips they aim to + support. Their name should end with `-hal` to distinguish them from + register access crates. The name should not contain underscores (use + dashes instead). +] else if lang == "de" [ + HAL-Crates sollten nach dem Chip oder der Chip-Familie benannt werden, + die sie unterstützen sollen. Ihr Name sollte auf `-hal` enden, um sie + von Registerzugriffs-Crates zu unterscheiden. Der Name sollte keine + Unterstriche enthalten (stattdessen sind Bindestriche zu verwenden). +] else { todo } diff --git a/typ/design-patterns/hal/predictability.typ b/typ/design-patterns/hal/predictability.typ new file mode 100644 index 00000000..e8be020c --- /dev/null +++ b/typ/design-patterns/hal/predictability.typ @@ -0,0 +1,65 @@ +#import "../../config.typ": * + +#h1(offset: whole*2, + if lang == "en" [Predictability] + else if lang == "de" [Vorhersehbarkeit] + else { todo }) + +#set heading(offset: whole*3) + += #(if lang == "en" [Constructors are used instead of extension traits] + else if lang == "de" [Anstelle von Extension Traits werden Konstruktoren verwendet] + else { todo }) (C-CTOR) + + +#if lang == "en" [ + All peripherals to which the HAL adds functionality should be wrapped in + a new type, even if no additional fields are required for that + functionality. +] else if lang == "de" [ + Alle Peripheriegeräte, denen die HAL Funktionalität hinzufügt, sollten + in einen neuen Typ gekapselt werden -- selbst dann, wenn für diese + Funktionalität keine zusätzlichen Felder erforderlich sind. +] else { todo } + +#if lang == "en" [ + Extension traits implemented for the raw peripheral should be avoided. +] else if lang == "de" [ + Die Implementierung von Extension Traits für das rohe Peripheriegerät + (Raw Peripheral) sollte vermieden werden. +] else { todo } + += #(if lang == "en" [Methods are decorated with `#[inline]` where appropriate] + else if lang == "de" [Methoden werden dort, wo es angebracht ist, mit `#[inline]` + versehen] + else { todo }) (C-INLINE) + + +#if lang == "en" [ + The Rust compiler does not by default perform full inlining across crate + boundaries. As embedded applications are sensitive to unexpected code + size increases, `#[inline]` should be used to guide the compiler as + follows: + - All "small" functions should be marked `#[inline]`. What qualifies as + "small" is subjective, but generally all functions that are expected + to compile down to single-digit instruction sequences qualify as + small. + - Functions that are very likely to take constant values as parameters + should be marked as `#[inline]`. This enables the compiler to compute + even complicated initialization logic at compile time, provided the + function inputs are known. +] else if lang == "de" [ + Der Rust-Compiler führt standardmäßig kein vollständiges Inlining über + Crate-Grenzen hinweg durch. Da eingebettete Anwendungen empfindlich auf + unerwartete Vergrößerungen des Codes reagieren, sollte `#[inline]` + verwendet werden, um den Compiler wie folgt zu steuern: + - Alle „kleinen" Funktionen sollten mit `#[inline]` gekennzeichnet + werden. Was als „klein" gilt, ist subjektiv; im Allgemeinen zählen + jedoch alle Funktionen dazu, die voraussichtlich zu einer Sequenz von + nur wenigen Maschinenbefehlen kompiliert werden. + - Funktionen, die sehr wahrscheinlich konstante Werte als Parameter + erhalten, sollten mit `#[inline]` gekennzeichnet werden. Dies + ermöglicht es dem Compiler, selbst komplexe Initialisierungslogik zur + Kompilierzeit zu berechnen, sofern die Eingabewerte der Funktion + bekannt sind. +] else { todo } diff --git a/typ/design-patterns/index.typ b/typ/design-patterns/index.typ new file mode 100644 index 00000000..aa788c45 --- /dev/null +++ b/typ/design-patterns/index.typ @@ -0,0 +1,13 @@ +#import "../config.typ": * + +#h1(if lang == "en" [Design Patterns] + else if lang == "de" [Design-Muster] + else { todo }) +#set heading(offset: whole) + +#if lang == "en" [ + This chapter aims to collect various useful design patterns for embedded Rust. +] else if lang == "de" [ + Dieses Kapitel zielt darauf ab, verschiedene nützliche Entwurfsmuster + für Embedded-Rust zusammenzustellen. +] else { todo } diff --git a/typ/interoperability/c-with-rust.typ b/typ/interoperability/c-with-rust.typ new file mode 100644 index 00000000..5414c188 --- /dev/null +++ b/typ/interoperability/c-with-rust.typ @@ -0,0 +1,426 @@ +#import "../config.typ": * + +#h1(offset: whole, + if lang == "en" [A little C with your Rust] + else if lang == "de" [Ein bisschen C zu Ihrem Rust] + else { todo }) + +#set heading(offset: whole*2) + +#if lang == "en" [ + Using C or C++ inside of a Rust project consists of two major parts: + - Wrapping the exposed C API for use with Rust + - Building your C or C++ code to be integrated with the Rust code +] else if lang == "de" [ + Die Verwendung von C oder C++ innerhalb eines Rust-Projekts umfasst zwei + wesentliche Aspekte: + - Einkapselung der bereitgestellten C-API für die Verwendung mit Rust + - Erstellen Ihres C- oder C++-Codes zur Integration mit dem Rust-Code +] else { todo } + +#if lang == "en" [ + As C++ does not have a stable ABI for the Rust compiler to target, it is + recommended to use the `C` ABI when combining Rust with C or C++. +] else if lang == "de" [ + Da C++ über keine stabile ABI verfügt, auf die der Rust-Compiler + abzielen könnte, wird empfohlen, bei der Kombination von Rust mit C oder + C++ die `C`-ABI zu verwenden. +] else { todo } + += #(if lang == "en" [Defining the interface] + else if lang == "de" [Definition der Schnittstelle] + else { todo }) + +#if lang == "en" [ + Before consuming C or C++ code from Rust, it is necessary to define (in + Rust) what data types and function signatures exist in the linked code. + In C or C++, you would include a header (`.h` or `.hpp`) file which + defines this data. In Rust, it is necessary to either manually translate + these definitions to Rust, or use a tool to generate these definitions. +] else if lang == "de" [ + Bevor C- oder C++-Code aus Rust heraus verwendet werden kann, muss (in + Rust) definiert werden, welche Datentypen und Funktionssignaturen im + verlinkten Code vorhanden sind. In C oder C++ würde man eine + Header-Datei (`.h` oder `.hpp`) einbinden, die diese Daten definiert. In + Rust ist es erforderlich, diese Definitionen entweder manuell nach Rust + zu übertragen oder ein Werkzeug zu ihrer Generierung zu verwenden. +] else { todo } + +#if lang == "en" [ + First, we will cover manually translating these definitions from C/C++ + to Rust. +] else if lang == "de" [ + Zunächst behandeln wir die manuelle Übertragung dieser Definitionen von + C/C++ nach Rust. +] else { todo } + +== #(if lang == "en" [Wrapping C functions and Datatypes] + else if lang == "de" [Einbinden von C-Funktionen und -Datentypen] + else { todo }) + +#if lang == "en" [ + Typically, libraries written in C or C++ will provide a header file + defining all types and functions used in public interfaces. An example + file may look like this: +] else if lang == "de" [ + Typischerweise stellen in C oder C++ geschriebene Bibliotheken eine + Header-Datei bereit, die alle in öffentlichen Schnittstellen verwendeten + Typen und Funktionen definiert. Eine Beispieldatei könnte wie folgt + aussehen: +] else { todo } + +```c +/* File: cool.h */ +typedef struct CoolStruct { + int x; + int y; +} CoolStruct; + +void cool_function(int i, char c, CoolStruct* cs); +``` + +#if lang == "en" [ + When translated to Rust, this interface would look as such: +] else if lang == "de" [ + Nach Rust übertragen, sähe diese Schnittstelle folgendermaßen aus: +] else { todo } + +```rust +/* File: cool_bindings.rs */ +#[repr(C)] +pub struct CoolStruct { + pub x: cty::c_int, + pub y: cty::c_int, +} + +extern "C" { + pub fn cool_function( + i: cty::c_int, + c: cty::c_char, + cs: *mut CoolStruct + ); +} +``` + +#if lang == "en" [ + Let's take a look at this definition one piece at a time, to explain + each of the parts. +] else if lang == "de" [ + Schauen wir uns diese Definition Schritt für Schritt an, um die + einzelnen Bestandteile zu erläutern. +] else { todo } + +```rust +#[repr(C)] +pub struct CoolStruct { ... } +``` + +#if lang == "en" [ + By default, Rust does not guarantee order, padding, or the size of data + included in a `struct`. In order to guarantee compatibility with C code, + we include the `#[repr(C)]` attribute, which instructs the Rust compiler + to always use the same rules C does for organizing data within a struct. +] else if lang == "de" [ + Standardmäßig garantiert Rust weder die Reihenfolge noch das Padding + oder die Größe der in einer `struct` enthaltenen Daten. Um die + Kompatibilität mit C-Code zu gewährleisten, verwenden wir das Attribut + `#[repr(C)]`; dieses weist den Rust-Compiler an, für die Anordnung der + Daten innerhalb der Struktur stets dieselben Regeln wie C anzuwenden. +] else { todo } + + +```rust +pub x: cty::c_int, +pub y: cty::c_int, +``` + +#if lang == "en" [ + Due to the flexibility of how C or C++ defines an `int` or `char`, it is + recommended to use primitive data types defined in `cty`, which will map + types from C to types in Rust. +] else if lang == "de" [ + Aufgrund der Flexibilität, wie C oder C++ ein „int" oder „char" + definiert, wird empfohlen, in „cty" definierte primitive Datentypen zu + verwenden, die Typen von C auf Typen in Rust abbilden. +] else { todo } + +```rust +extern "C" { pub fn cool_function( ... ); } +``` + +#if lang == "en" [ + This statement defines the signature of a function that uses the C ABI, + called `cool_function`. By defining the signature without defining the + body of the function, the definition of this function will need to be + provided elsewhere, or linked into the final library or binary from a + static library. +] else if lang == "de" [ + Diese Anweisung definiert die Signatur einer Funktion namens + `cool_function`, die die C-ABI verwendet. Da die Signatur ohne den + Funktionsrumpf definiert wird, muss die eigentliche Funktionsdefinition + an anderer Stelle bereitgestellt oder aus einer statischen Bibliothek in + die endgültige Bibliothek bzw. das fertige Binärprogramm eingebunden + werden. +] else { todo } + +```rust + i: cty::c_int, + c: cty::c_char, + cs: *mut CoolStruct +``` + +#if lang == "en" [ + Similar to our datatype above, we define the datatypes of the function + arguments using C-compatible definitions. We also retain the same + argument names, for clarity. +] else if lang == "de" [ + Ähnlich wie bei unserem obigen Datentyp definieren wir die Datentypen + der Funktionsargumente mithilfe von C-kompatiblen Definitionen. Der + Übersichtlichkeit halber behalten wir zudem die ursprünglichen + Argumentnamen bei. +] else { todo } + +#if lang == "en" [ + We have one new type here, `*mut CoolStruct`. As C does not have a + concept of Rust's references, which would look like this: + `&mut CoolStruct`, we instead have a raw pointer. As dereferencing this + pointer is `unsafe`, and the pointer may in fact be a `null` pointer, + care must be taken to ensure the guarantees typical of Rust when + interacting with C or C++ code. +] else if lang == "de" [ + Hier begegnet uns ein neuer Typ: `*mut CoolStruct`. Da C das Konzept der + Rust-Referenzen (die etwa so aussehen: `&mut CoolStruct`) nicht kennt, + verwenden wir stattdessen einen sogenannten „Raw Pointer" (Rohzeiger). + Da das Dereferenzieren dieses Zeigers als `unsafe` gilt und es sich + tatsächlich um einen `null`-Zeiger handeln kann, ist bei der Interaktion + mit C- oder C++-Code besondere Sorgfalt geboten, um die für Rust + typischen Garantien zu wahren. +] else { todo } + +== #(if lang == "en" [Automatically generating the interface] + else if lang == "de" [Automatische Generierung der Schnittstelle] + else { todo }) + +#let url_bindgen = "https://github.com/rust-lang/rust-bindgen" +#let ln_cty = link("https://crates.io/crates/cty")[`cty`] +#if lang == "en" [ + Rather than manually generating these interfaces, which may be tedious + and error prone, there is a tool called + #link(url_bindgen)[bindgen] which will + perform these conversions automatically. For instructions of the usage + of #link(url_bindgen)[bindgen], please + refer to the #link(url_bindgen)[bindgen user's manual], + however the typical process consists of the following: + + Gather all C or C++ headers defining interfaces or datatypes you would + like to use with Rust. + + Write a `bindings.h` file, which `#include "..."`'s each of the files + you gathered in step one. + + Feed this `bindings.h` file, along with any compilation flags used to + compile your code into `bindgen`. Tip: use + `Builder.ctypes_prefix("cty")` / `--ctypes-prefix=cty` and + `Builder.use_core()` / `--use-core` to make the generated code + `#![no_std]` compatible. + + `bindgen` will produce the generated Rust code to the output of the + terminal window. This output may be piped to a file in your project, + such as `bindings.rs`. You may use this file in your Rust project to + interact with C/C++ code compiled and linked as an external library. + Tip: don't forget to use the #ln_cty crate if your types in + the generated bindings are prefixed with `cty`. +] else if lang == "de" [ + Anstatt diese Schnittstellen manuell zu erstellen -- was mühsam und + fehleranfällig sein kann --, gibt es ein Werkzeug namens + #link(url_bindgen)[bindgen], das diese + Konvertierungen automatisch durchführt. Hinweise zur Verwendung von + #link(url_bindgen)[bindgen] finden Sie im + #link(url_bindgen)[bindgen-Benutzerhandbuch]\; + der typische Ablauf sieht jedoch folgendermaßen aus: + + Sammeln Sie alle C- oder C++-Header, die Schnittstellen oder + Datentypen definieren, die Sie mit Rust verwenden möchten. + + Erstelle eine Datei namens `bindings.h`, die mittels `#include "..."` + jede der Dateien einbindet, die du in Schritt eins zusammengetragen + hast. + + Übergeben Sie diese `bindings.h`-Datei zusammen mit den für die + Kompilierung Ihres Codes verwendeten Flags an `bindgen`. Tipp: + Verwenden Sie `Builder.ctypes_prefix("cty")` / `--ctypes-prefix=cty` + und `Builder.use_core()` / `--use-core`, um den generierten Code + `#![no_std]`-kompatibel zu machen. + + `bindgen` gibt den generierten Rust-Code direkt im Terminal aus. Diese + Ausgabe lässt sich in eine Datei Ihres Projekts umleiten, + beispielsweise `bindings.rs`. Sie können diese Datei in Ihrem + Rust-Projekt verwenden, um mit C/C++-Code zu interagieren, der als + externe Bibliothek kompiliert und gelinkt wurde. Tipp: Vergessen Sie + nicht, das #ln_cty;-Crate zu + verwenden, falls die Typen in den generierten Bindings das Präfix + `cty` aufweisen. +] else { todo } + += #(if lang == "en" [Building your C/C++ code] + else if lang == "de" [Erstellen Ihres C/C++-Codes] + else { todo }) + +#if lang == "en" [ + As the Rust compiler does not directly know how to compile C or C++ code + (or code from any other language, which presents a C interface), it is + necessary to compile your non-Rust code ahead of time. +] else if lang == "de" [ + Da der Rust-Compiler nicht direkt weiß, wie man C- oder C++-Code (oder + Code einer anderen Sprache mit C-Schnittstelle) kompiliert, muss der + Nicht-Rust-Code vorab kompiliert werden. +] else { todo } + +#if lang == "en" [ + For embedded projects, this most commonly means compiling the C/C++ code + to a static archive (such as `cool-library.a`), which can then be + combined with your Rust code at the final linking step. +] else if lang == "de" [ + Bei Embedded-Projekten bedeutet dies meist, dass der C/C++-Code zu einem + statischen Archiv (z. B. `cool-library.a`) kompiliert wird, welches dann + im abschließenden Link-Schritt mit dem Rust-Code zusammengeführt werden + kann. +] else { todo } + +#if lang == "en" [ + If the library you would like to use is already distributed as a static + archive, it is not necessary to rebuild your code. Just convert the + provided interface header file as described above, and include the + static archive at compile/link time. +] else if lang == "de" [ + Wenn die gewünschte Bibliothek bereits als statisches Archiv vorliegt, + ist eine erneute Kompilierung des Codes nicht erforderlich. Es genügt, + die bereitgestellte Header-Datei für die Schnittstelle wie oben + beschrieben umzuwandeln und das statische Archiv beim Kompilieren bzw. + Linken einzubinden. +] else { todo } + +#if lang == "en" [ + If your code exists as a source project, it will be necessary to compile + your C/C++ code to a static library, either by triggering your existing + build system (such as `make`, `CMake`, etc.), or by porting the + necessary compilation steps to use a tool called the `cc` crate. For + both of these steps, it is necessary to use a `build.rs` script. +] else if lang == "de" [ + Liegt der Code als Quellcode-Projekt vor, muss der C/C++-Code in eine + statische Bibliothek kompiliert werden. Dies kann entweder durch Aufruf + des vorhandenen Build-Systems (z. B. `make`, `CMake` usw.) oder durch + Portierung der erforderlichen Kompilierungsschritte auf das sogenannte + `cc`-Crate erfolgen. Für beide Vorgehensweisen ist die Verwendung eines + `build.rs`-Skripts erforderlich. +] else { todo } + +== #(if lang == "en" [Rust `build.rs` build scripts] + else if lang == "de" [Rust-`build.rs`-Build-Skripte] + else { todo }) + +#if lang == "en" [ + A `build.rs` script is a file written in Rust syntax, that is executed + on your compilation machine, AFTER dependencies of your project have + been built, but BEFORE your project is built. +] else if lang == "de" [ + Ein `build.rs`-Skript ist eine in Rust-Syntax verfasste Datei, die auf + dem Kompilierrechner ausgeführt wird -- und zwar _nachdem_ die + Abhängigkeiten Ihres Projekts erstellt wurden, aber _bevor_ Ihr + Projekt selbst kompiliert wird. +] else { todo } + +#let url_build_scripts = "https://doc.rust-lang.org/cargo/reference/build-scripts.html" +#if lang == "en" [ + The full reference may be found #link(url_build_scripts)[here]. + `build.rs` scripts are useful for generating code + (such as via #link(url_bindgen)[bindgen]), calling + out to external build systems such as `Make`, or directly compiling + C/C++ through use of the `cc` crate. +] else if lang == "de" [ + Die vollständige Referenz finden Sie #link(url_build_scripts)[hier]. + `build.rs`-Skripte eignen sich beispielsweise zur Code-Generierung (etwa + mittels #link(url_bindgen)[bindgen]), + zum Aufruf externer Build-Systeme wie `Make` oder zur direkten + Kompilierung von C/C++-Code unter Verwendung des `cc`-Crates. +] else { todo } + +== #(if lang == "en" [Triggering external build systems] + else if lang == "de" [Auslösen externer Build-Systeme] + else { todo }) + +#let ln_command = link("https://doc.rust-lang.org/std/process/struct.Command.html")[`std::process::Command`] +#if lang == "en" [ + For projects with complex external projects or build systems, + it may be easiest to use #ln_command + to "shell out" to your other build systems by traversing relative paths, + calling a fixed command (such as `make library`), and then copying the + resulting static library to the proper location in the `target` build + directory. +] else if lang == "de" [ + Bei Projekten, die komplexe externe Projekte oder Build-Systeme + einbinden, ist es oft am einfachsten, #ln_command + zu verwenden, um andere Build-Systeme aufzurufen (sogenanntes + „Shelling-out"). Dabei navigieren Sie über relative Pfade, führen einen + festen Befehl aus (wie etwa `make library`) und kopieren anschließend + die erzeugte statische Bibliothek an den entsprechenden Ort im + `target`-Build-Verzeichnis. +] else { todo } + +#if lang == "en" [ + While your crate may be targeting a `no_std` embedded platform, your + `build.rs` executes only on machines compiling your crate. This means + you may use any Rust crates which will run on your compilation host. +] else if lang == "de" [ + Auch wenn Ihre Crate für eine eingebettete Plattform ohne + Standardbibliothek (`no_std`) gedacht ist, wird die `build.rs` + ausschließlich auf dem Rechner ausgeführt, der die Crate kompiliert. Das + bedeutet, dass Sie beliebige Rust-Crates verwenden können, die auf Ihrem + Kompilier-Host lauffähig sind. +] else { todo } + +== #(if lang == "en" [Building C/C++ code with the `cc` crate] + else if lang == "de" [Kompilieren von C/C++-Code mit dem `cc`-Crate] + else { todo }) + +#let url_cc = "https://github.com/alexcrichton/cc-rs" +#if lang == "en" [ + For projects with limited dependencies or complexity, or for projects + where it is difficult to modify the build system to produce a static + library (rather than a final binary or executable), it may be easier to + instead utilize the #link(url_cc)[`cc` crate], which + provides an idiomatic Rust interface to the compiler provided by the + host. +] else if lang == "de" [ + Bei Projekten mit geringen Abhängigkeiten oder überschaubarer + Komplexität -- oder wenn es schwierig ist, das Build-System so + anzupassen, dass eine statische Bibliothek (statt einer fertigen + Binärdatei oder eines ausführbaren Programms) erzeugt wird -- kann es + einfacher sein, stattdessen das + #link(url_cc)[`cc`-Crate] zu verwenden; + dieses bietet eine idiomatische Rust-Schnittstelle zu dem vom Host + bereitgestellten Compiler. +] else { todo } + +#if lang == "en" [ + In the simplest case of compiling a single C file as a dependency to a + static library, an example `build.rs` script using the + #link(url_cc)[`cc` crate] would look like this: +] else if lang == "de" [ + Im einfachsten Fall, bei dem eine einzelne C-Datei als Abhängigkeit für + eine statische Bibliothek kompiliert wird, sähe ein Beispiel für ein + `build.rs`-Skript, das das + #link(url_cc)[`cc`-Crate] verwendet, + folgendermaßen aus: +] else { todo } + +```rust +fn main() { + cc::Build::new() + .file("src/foo.c") + .compile("foo"); +} +``` + +#if lang == "en" [ + The `build.rs` is placed at the root of the package. Then `cargo build` + will compile and execute it before the build of the package. A static + archive named `libfoo.a` is generated and placed in the `target` directory. +] else if lang == "de" [ + Die Datei `build.rs` befindet sich im Wurzelverzeichnis des Pakets. + `cargo build` kompiliert und führt sie dann vor dem eigentlichen + Build-Vorgang des Pakets aus. Dabei wird ein statisches Archiv namens + `libfoo.a` erstellt und im Verzeichnis `target` abgelegt. +] else { todo } diff --git a/typ/interoperability/index.typ b/typ/interoperability/index.typ new file mode 100644 index 00000000..ce400715 --- /dev/null +++ b/typ/interoperability/index.typ @@ -0,0 +1,149 @@ +#import "../config.typ": * + +#h1(if lang == "en" [Interoperability] + else if lang == "de" [Interoperabilität] + else { todo }) +#set heading(offset: whole) + +#let ln_ffi = link("https://doc.rust-lang.org/std/ffi/index.html")[`std::ffi`] +#if lang == "en" [ + Interoperability between Rust and C code is always dependent on + transforming data between the two languages. For this purpose, there is + a dedicated module in the `stdlib` called #ln_ffi. +] else if lang == "de" [ + Die Interoperabilität zwischen Rust- und C-Code hängt stets von der + Umwandlung von Daten zwischen den beiden Sprachen ab. Hierfür gibt es in + der Standardbibliothek (`stdlib`) ein spezielles Modul namens #ln_ffi. +] else { todo } + +#if lang == "en" [ + `std::ffi` provides type definitions for C primitive types, such as + `char`, `int`, and `long`. It also provides some utility for converting + more complex types such as strings, mapping both `&str` and `String` to + C types that are easier and safer to handle. +] else if lang == "de" [ + `std::ffi` stellt Typdefinitionen für primitive C-Datentypen bereit, wie + etwa `char`, `int` und `long`. Zudem bietet es Hilfsmittel zur + Konvertierung komplexerer Typen wie Strings, indem es sowohl `&str` als + auch `String` auf C-Typen abbildet, die sich einfacher und sicherer + handhaben lassen. +] else { todo } + +#let ln_cty = link("https://crates.io/crates/cty")[`cty`] +#let ln_cstr = link("https://crates.io/crates/cstr_core")[`cstr_core`] +#if lang == "en" [ + As of Rust 1.30, functionalities of `std::ffi` are available in either + `core::ffi` or `alloc::ffi` depending on whether or not memory + allocation is involved. The #ln_cty crate and the #ln_cstr + crate also offer similar functionalities. +] else if lang == "de" [ + Seit Rust 1.30 stehen die Funktionalitäten von `std::ffi` wahlweise in + `core::ffi` oder `alloc::ffi` zur Verfügung, je nachdem, ob + Speicherreservierungen erforderlich sind oder nicht. + Auch die Crates #ln_cty und #ln_cstr bieten vergleichbare Funktionalitäten an. +] else { todo } + +#figure( + kind: table, + table( + columns: 3, + align: (auto,auto,auto,), + table.header( + if lang == "en" [Rust type] + else if lang == "de" [Rusttyp] + else { todo }, + if lang == "en" [Intermediate] + else if lang == "de" [Dazwischenliegend] + else { todo }, + if lang == "en" [C type] + else if lang == "de" [C-Typ] + else { todo }, + ), + `String`, `CString`, `char *`, + `&str`, `CStr`, `const char *`, + `()`, `c_void`, `void`, + if lang in ("en", "de") [`u32` or `u64`] + else { todo }, + `c_uint`, `unsigned int`, + if lang == "en" [etc] + else if lang == "de" [usw.] + else { todo }, + […], […], + ) +) + +#if lang == "en" [ + A value of a C primitive type can be used as one of the corresponding + Rust type and vice versa, since the former is simply a type alias of the + latter. For example, the following code compiles on platforms where + `unsigned int` is 32-bit long. +] else if lang == "de" [ + Ein Wert eines primitiven C-Datentyps kann als Wert des entsprechenden + Rust-Datentyps verwendet werden und umgekehrt, da der erstere lediglich + ein Typalias des letzteren ist. Beispielsweise lässt sich der folgende + Code auf Plattformen kompilieren, auf denen `unsigned int` 32 Bit lang + ist. +] else { todo } + +```rust +fn foo(num: u32) { + let c_num: c_uint = num; + let r_num: u32 = c_num; +} +``` + +== #(if lang == "en" [Interoperability with other build systems] + else if lang == "de" [Interoperabilität mit anderen Build-Systemen] + else { todo }) + +#if lang == "en" [ + A common requirement for including Rust in your embedded project is + combining Cargo with your existing build system, such as make or cmake. +] else if lang == "de" [ + Eine häufige Voraussetzung für die Einbindung von Rust in + Embedded-Projekte ist die Kombination von Cargo mit Ihrem bestehenden + Build-System, beispielsweise Make oder CMake. +] else { todo } + +#let url_issue61 = "https://github.com/rust-embedded/book/issues/61" +#if lang == "en" [ + We are collecting examples and use cases for this on our issue tracker + in #link(url_issue61)[issue \#61]. +] else if lang == "de" [ + Wir sammeln Beispiele und Anwendungsfälle hierzu in unserem + Problem-Verfolgungswerkzeug unter #link(url_issue61)[Issue \#61]. +] else { todo } + +== #(if lang == "en" [Interoperability with RTOSs] + else { todo }) + +#if lang == "en" [ + Integrating Rust with an RTOS such as FreeRTOS or ChibiOS is still a + work in progress; especially calling RTOS functions from Rust can be + tricky. +] else if lang == "de" [ + Die Integration von Rust in ein RTOS wie FreeRTOS oder ChibiOS befindet + sich noch in der Entwicklung; insbesondere der Aufruf von + RTOS-Funktionen aus Rust heraus kann sich als schwierig erweisen. +] else { todo } + +#let url_zephyr = "https://docs.zephyrproject.org/latest/develop/languages/rust/index.html" +#if lang == "en" [ + Currently, the following projects publicly support Rust\<-\>RTOS + interoperability: + - #link(url_zephyr)[Zephyr Project] +] else if lang == "de" [ + Derzeit unterstützen die folgenden Projekte öffentlich die + Interoperabilität zwischen Rust und RTOS: + - #link(url_zephyr)[Zephyr-Projekt] +] else { todo } + +#let url_issue62 = "https://github.com/rust-embedded/book/issues/62" +#if lang == "en" [ + We are collecting examples and use cases for this on our issue tracker + in #link(url_issue62)[issue \#62]. +] else if lang == "de" [ + Wir sammeln hierfür Beispiele und Anwendungsfälle in unserem + Problem-Verfolgungswerkzeug unter + #link(url_issue62)[Issue \#62]. +] else { todo } diff --git a/typ/interoperability/rust-with-c.typ b/typ/interoperability/rust-with-c.typ new file mode 100644 index 00000000..034f3a67 --- /dev/null +++ b/typ/interoperability/rust-with-c.typ @@ -0,0 +1,229 @@ +#import "../config.typ": * + +#h1(offset: whole, + if lang == "en" [A little Rust with your C] + else if lang == "de" [Ein bisschen Rust zu Ihrem C] + else { todo }) + +#set heading(offset: whole*2) + +#if lang == "en" [ + Using Rust code inside a C or C++ project mostly consists of two parts. + - Creating a C-friendly API in Rust + - Embedding your Rust project into an external build system +] else if lang == "de" [ + Die Verwendung von Rust-Code in einem C- oder C++-Projekt besteht + größtenteils aus zwei Teilen. + - Erstellung einer C-kompatiblen API in Rust + - Einbindung Ihres Rust-Projekts in ein externes Build-System +] else { todo } + +#if lang == "en" [ + Apart from `cargo` and `meson`, most build systems don't have native + Rust support. So you're most likely best off just using `cargo` for + compiling your crate and any dependencies. +] else if lang == "de" [ + Abgesehen von `cargo` und `meson` bieten die meisten Build-Systeme keine + native Rust-Unterstützung. Daher fährst du höchstwahrscheinlich am + besten damit, einfach `cargo` für die Kompilierung deines Crates und + aller Abhängigkeiten zu verwenden. +] else { todo } + += #(if lang == "en" [Setting up a project] + else if lang == "de" [Ein Projekt einrichten] + else { todo }) + +#if lang == "en" [ + Create a new `cargo` project as usual. +] else if lang == "de" [ + Erstellen Sie wie gewohnt ein neues `cargo`-Projekt. +] else { todo } + +#if lang == "en" [ + There are flags to tell `cargo` to emit a systems library, instead of + its regular rust target. This also allows you to set a different output + name for your library, if you want it to differ from the rest of your crate. +] else if lang == "de" [ + Es gibt Flags, mit denen man `cargo` anweisen kann, eine + Systembibliothek anstelle des üblichen Rust-Ziels zu erstellen. Auf + diese Weise können Sie auch einen abweichenden Namen für die Bibliothek + festlegen, falls dieser sich von dem des restlichen Crates unterscheiden soll. +] else { todo } + +#raw(block: true, lang: "toml", +"[lib] +name = \"your_crate\" +crate-type = [\"cdylib\"] # " + if lang == "en" { + "Creates dynamic lib" + } else if lang == "de" { + "Erstellt eine dynamische Bibliothek" + } else { todos } + " +# crate-type = [\"staticlib\"] # " + if lang == "en" { + "Creates static lib" + } else if lang == "de" { + "Erstellt eine statische Bibliothek" + } else { todos } + " +") + += #(if lang == "en" [Building a `C` API] + else if lang == "de" [Erstellung einer C-API] + else { todo }) + +#if lang == "en" [ + Because C++ has no stable ABI for the Rust compiler to target, we use + `C` for any interoperability between different languages. This is no + exception when using Rust inside of C and C++ code. +] else if lang == "de" [ + Da C++ über keine stabile ABI verfügt, auf die der Rust-Compiler + abzielen könnte, nutzen wir C für die Interoperabilität zwischen + verschiedenen Sprachen. Dies gilt auch für die Verwendung von Rust + innerhalb von C- und C++-Code. +] else { todo } + +== `#[no_mangle]` + +#if lang == "en" [ + The Rust compiler mangles symbol names differently than native code + linkers expect. As such, any function that Rust exports to be used + outside of Rust needs to be told not to be mangled by the compiler. +] else if lang == "de" [ + Der Rust-Compiler verarbeitet Symbolnamen anders als Linker für nativen + Code erwarten. Daher muss jeder Funktion, die Rust zur Verwendung + außerhalb von Rust exportiert, mitgeteilt werden, dass der Compiler sie + nicht verändern soll. +] else { todo } + +== `extern "C"` + +#if lang == "en" [ + By default, any function you write in Rust will use the Rust ABI (which + is also not stabilized). Instead, when building outwards facing FFI APIs + we need to tell the compiler to use the system ABI. +] else if lang == "de" [ + Standardmäßig verwendet jede in Rust geschriebene Funktion die Rust-ABI + (die ebenfalls nicht stabilisiert ist). Beim Erstellen von nach außen + gerichteten FFI-APIs muss der Compiler daher angewiesen werden, die + System-ABI zu verwenden. +] else { todo } + +#let url_external = "https://doc.rust-lang.org/reference/items/external-blocks.html" +#if lang == "en" [ + Depending on your platform, you might want to target a specific ABI + version, which are documented #link(url_external)[here]. +] else if lang == "de" [ + Je nach Plattform möchten Sie möglicherweise eine bestimmte ABI-Version + anvisieren; diese sind #link(url_external)[hier] dokumentiert. +] else { todo } + +#divider() + +#if lang == "en" [ + Putting these parts together, you get a function that looks roughly like this. +] else if lang == "de" [ + Wenn man diese Teile zusammensetzt, erhält man eine Funktion, die + ungefähr so ​​aussieht. +] else { todo } + +```rust +#[no_mangle] +pub extern "C" fn rust_function() { + +} +``` + +#if lang == "en" [ + Just as when using `C` code in your Rust project you now need to + transform data from and to a form that the rest of the application will + understand. +] else if lang == "de" [ + Genau wie bei der Verwendung von C-Code in Ihrem Rust-Projekt müssen Sie + nun Daten in eine Form umwandeln -- und aus dieser zurück --, die der + Rest der Anwendung versteht. +] else { todo } + += #(if lang == "en" [Linking and greater project context] + else if lang == "de" [Verknüpfung und übergeordneter Projektkontext] + else { todo }) + +#if lang == "en" [ + So then, that's one half of the problem solved. How do you use this now? +] else if lang == "de" [ + Damit ist die eine Hälfte des Problems gelöst. Wie verwendet man das nun? +] else { todo } + +#if lang == "en" [ + *This very much depends on your project and/or build system* +] else if lang == "de" [ + *Das hängt stark von Ihrem Projekt bzw. Ihrem Build-System ab.* +] else { todo } + +#if lang == "en" [ + `cargo` will create a `my_lib.so`/`my_lib.dll` or `my_lib.a` file, + depending on your platform and settings. This library can simply be + linked by your build system. +] else if lang == "de" [ + `cargo` erstellt -- je nach Plattform und Einstellungen -- eine Datei + namens `my_lib.so`, `my_lib.dll` oder `my_lib.a`. Diese Bibliothek kann + einfach von Ihrem Build-System eingebunden (gelinkt) werden. +] else { todo } + +#if lang == "en" [ + However, calling a Rust function from C requires a header file to + declare the function signatures. +] else if lang == "de" [ + Um jedoch eine Rust-Funktion aus C heraus aufzurufen, ist eine + Header-Datei erforderlich, in der die Funktionssignaturen deklariert werden. +] else { todo } + +#if lang == "en" [ + Every function in your Rust-ffi API needs to have a corresponding header function. +] else if lang == "de" [ + Für jede Funktion in Ihrer Rust-FFI-API muss eine entsprechende + Deklaration im Header vorhanden sein. +] else { todo } + +```rust +#[no_mangle] +pub extern "C" fn rust_function() {} +``` + +#if lang == "en" [ + would then become +] else if lang == "de" [ + würde dann werden +] else { todo } + +```c +void rust_function(); +``` + +#if lang == "en" [ + etc. +] else if lang == "de" [ + usw. +] else { todo } + +#if lang == "en" [ + There is a tool to automate this process, called + #link("https://github.com/eqrion/cbindgen")[cbindgen] which analyses + your Rust code and then generates headers for your C and C++ projects + from it. +] else if lang == "de" [ + Es gibt ein Werkzeug zur Automatisierung dieses Prozesses namens + #link("https://github.com/eqrion/cbindgen")[cbindgen], das Ihren + Rust-Code analysiert und daraus Header-Dateien für Ihre C- und + C++-Projekte generiert. +] else { todo } + +#if lang == "en" [ + At this point, using the Rust functions from C is as simple as including + the header and calling them! +] else if lang == "de" [ + An diesem Punkt ist die Verwendung der Rust-Funktionen aus C heraus so + einfach, wie den Header einzubinden und die Funktionen aufzurufen! +] else { todo } + +```c +#include "my-rust-project.h" +rust_function(); +``` diff --git a/typ/intro/hardware.typ b/typ/intro/hardware.typ new file mode 100644 index 00000000..5ed8e268 --- /dev/null +++ b/typ/intro/hardware.typ @@ -0,0 +1,159 @@ +#import "../config.typ": * + +#h1(offset: whole, + if lang == "en" [Meet Your Hardware] + else if lang == "de" [Lernen Sie Ihre Hardware kennen] + else if lang == "uk" [Зустрічайте залізо] + else if lang == "zh" [熟悉你的硬件] + else { todo }) +#set heading(offset: whole*2) + +#if lang == "en" [ + Let's get familiar with the hardware we'll be working with. +] else if lang == "de" [ + Machen wir uns mit der Hardware vertraut, mit der wir arbeiten werden. +] else if lang == "uk" [ + Давайте ознайомимося з обладнанням, з яким ми будемо працювати. +] else if lang == "zh" [ + 先来熟悉下我们要用的硬件。 +] else { todo } + +#figure( + image("../assets/f3.jpg") +) + += #(if lang in ("en", "zh") [STM32F3DISCOVERY (the "F3")] + else if lang == "de" [STM32F3DISCOVERY (die "F3")] + else if lang == "uk" [STM32F3DISCOVERY (скорочено "F3")] + else { todo }) + +#if lang == "en" [ + What does this board contain? +] else if lang == "de" [ + Was beinhaltet diese Platine? +] else if lang == "uk" [ + Що міститься на цій платі? +] else if lang == "zh" [ + 这个板子有什么? +] else { todo } + +#let url_f303 = "https://www.st.com/en/microcontrollers/stm32f303vc.html" +#let wiki_accelerometer = "https://en.wikipedia.org/wiki/Accelerometer" +#let url_lsm303 = "https://www.st.com/en/mems-and-sensors/lsm303dlhc.html" +#let wiki_magnetometer = "https://en.wikipedia.org/wiki/Magnetometer" +#let wiki_gyroscope = "https://en.wikipedia.org/wiki/Gyroscope" +#let url_l3gd20 = "https://www.pololu.com/file/0J563/L3GD20.pdf" +#let url_f103 = "https://www.st.com/en/microcontrollers/stm32f103cb.html" +#if lang == "en" [ + - A #link(url_f303)[STM32F303VCT6] microcontroller. This microcontroller has + - A single-core ARM Cortex-M4F processor with hardware support for + single-precision floating point operations and a maximum clock + frequency of 72 MHz. + - 256 KiB of "Flash" memory. (1 KiB = 10#[*24*] bytes) + - 48 KiB of RAM. + - A variety of integrated peripherals such as timers, I2C, SPI and + USART. + - General purpose Input Output (GPIO) and other types of pins + accessible through the two rows of headers along side the board. + - A Mini-USB interface accessible through the USB port labeled "USB + USER". + - An #link(wiki_accelerometer)[accelerometer] as part of the #link(url_lsm303)[LSM303DLHC] chip. + - A #link(wiki_magnetometer)[magnetometer] as part of the #link(url_lsm303)[LSM303DLHC] chip. + - A #link(wiki_gyroscope)[gyroscope] as part of the #link(url_l3gd20)[L3GD20] chip. + - 8 user LEDs arranged in the shape of a compass. + - A second microcontroller: a #link(url_f103)[STM32F103]. + This microcontroller is actually part of an on-board programmer / + debugger and is connected to the Mini-USB port named "USB ST-LINK". +] else if lang == "de" [ + - Ein #link(url_f303)[STM32F303VCT6]-Mikrocontroller. + Dieser Mikrocontroller hat + - Ein Single-Core-ARM-Cortex-M4F-Prozessor mit Hardware-Unterstützung + für Gleitkommaoperationen einfacher Genauigkeit und einer maximalen + Taktfrequenz von 72 MHz. + - 256 KiB "Flash"-Speicher. (1 KiB = 10#[*24*] bytes) + - 48 KiB RAM. + - Eine Vielzahl integrierter Peripherieeinheiten wie Timer, I2C, SPI + und USART. + - GPIO-Pins (General Purpose Input/Output) und andere Pin-Typen, die + über die beiden Stiftleistenreihen an den Seiten der Platine + zugänglich sind. + - Eine Mini-USB-Schnittstelle, die über den mit „USB USER" + gekennzeichneten USB-Anschluss zugänglich ist. + - Ein #link(wiki_accelerometer)[Beschleunigungssensor] als Teil des #link(url_lsm303)[LSM303DLHC]-Chips. #todoupd("de") + - Ein #link(wiki_gyroscope)[Gyroskop] als Teil des + #link(url_l3gd20)[L3GD20]-Chips. + - 8 Benutzer-LEDs, angeordnet in Form eines Kompasses. + - Ein zweiter Mikrocontroller: ein + #link(url_f103)[STM32F103]. + Dieser Mikrocontroller ist eigentlich Teil eines + On-Board-Programmers/Debuggers und mit dem Mini-USB-Anschluss namens + „USB ST-LINK" verbunden. +] else if lang == "uk" [ + - Мікроконтролер #link(url_f303)[STM32F303VCT6]. Цей мікроконтролер має: + - Одноядерний процесор ARM Cortex-M4F з апаратною підтримкою операцій + з плаваючою комою одинарної точності і максимальною тактовою частотою 72 МГц. + - 256 KiB флеш-пам'яті. (1 KiB = 10#[*24*] байт) + - 48 KiB оперативної пам'яті. + - Різноманітні інтегровані периферійні пристрої, такі як таймери, I2C, SPI та USART. + - Піни вводу-виводу загального призначення (GPIO) та інші типи пінів, + доступні через дворядні гребінки уздовж бокових стінок плати. + - Інтерфейс USB, доступний через порт USB з позначкою "USB USER". + - #link(wiki_accelerometer)[Акселерометр] у складі мікросхеми #link(url_lsm303)[LSM303DLHC]. + - #link(wiki_magnetometer)[Магнітометр] у складі мікросхеми #link(url_lsm303)[LSM303DLHC]. + - #link(wiki_gyroscope)[Гіроскоп] у складі мікросхеми #link(url_l3gd20)[L3GD20]. + - 8 користувацьких світлодіодів, розташованих у формі компаса. + - Другий мікроконтролер: #link(url_f103)[STM32F103]. + Цей мікроконтролер фактично є частиною програматора/відладчика, розміщеного на платі, і підключений до USB-порту з назвою "USB ST-LINK". +] else if lang == "zh" [ + - 一个#link(url_f303)[STM32F303VCT6]微控制器。这个微控制器包含 + - 一个单核的ARM Cortex-M4F + 处理器,支持单精度浮点运算,72MHz的最大时钟频率。 + - 256 KiB的"Flash"存储。 + - 48 KiB的RAM + - 多种多样的外设,比如计时器,I2C,SPI和USART + - 通用GPIO和在板子两侧的其它类型的引脚 + - 一个写着"USB USER"的USB接口 + - 一个位于#link(url_lsm303)[LSM303DLHC]芯片上的#link(wiki_accelerometer)[加速度计]。 + - 一个位于#link(url_lsm303)[LSM303DLHC]芯片上的#link(wiki_magnetometer)[磁力计]。 + - 一个位于#link(url_l3gd20)[L3GD20]芯片上的#link(wiki_gyroscope)[陀螺仪]. + - 8个摆得像一个指南针形状的user LEDs。 + - 一个二级微控制器: + #link(url_f103)[STM32F103]。这个微控制器实际上是一个板载编程器/调试器的一部分,与名为"USB + ST-LINK"的USB端口相连。 +] else { todo } + +#let url_f3disco = "https://www.st.com/en/evaluation-tools/stm32f3discovery.html" +#if lang == "en" [ + For a more detailed list of features and further specifications of the + board take a look at the #link(url_f3disco)[STMicroelectronics] website. +] else if lang == "de" [ + Eine detailliertere Liste der Funktionen und weitere technische Daten + des Boards finden Sie auf der Website von #link(url_f3disco)[STMicroelectronics]. +] else if lang == "uk" [ + Більш детальний перелік функцій та специфікацій плати можна знайти + на сайті #link(url_f3disco)[STMicroelectronics]. +] else if lang == "zh" [ + 关于所列举的功能的更多细节和开发板的更多规格请查阅#link(url_f3disco)[STMicroelectronics]网站。 +] else { todo } + + +#let url_f303_ds = "https://www.st.com/resource/en/datasheet/stm32f303vc.pdf" +#if lang == "en" [ + A word of caution: be careful if you want to apply external signals to + the board. The microcontroller STM32F303VCT6 pins take a nominal voltage + of 3.3 volts. For further information consult the + #link(url_f303_ds)[6.2 Absolute maximum ratings section in the manual] +] else if lang == "de" [ + Ein wichtiger Hinweis: Seien Sie vorsichtig, wenn Sie externe Signale an + das Board anlegen möchten. Die Pins des Mikrocontrollers STM32F303VCT6 + sind für eine Nennspannung von 3,3 Volt ausgelegt. Weitere Informationen + finden Sie im Abschnitt „6.2 Absolute maximum ratings" (Absolute + Grenzwerte) im #link(url_f303_ds)[Datenblatt]. +] else if lang == "uk" [ + Застереження: будьте обережні, подаючи на плату зовнішні сигнали. + Виводи мікроконтролера STM32F303VCT6 мають номінальну напругу 3,3 вольта. + Для отримання додаткової інформації зверніться до розділу #link(url_f303_ds)[6.2 Absolute maximum ratings section у Reference Manual] +] else if lang == "zh" [ + 提醒一句: + 如果想要为板子提供外部信号,请小心。微控制器STM32F303VCT6管脚的标称电压是3.3伏。更多信息请查看#link(url_f303_ds)[6.2 Absolute maximum ratings section in the manual]。 +] else { todo } \ No newline at end of file diff --git a/typ/intro/index.typ b/typ/intro/index.typ new file mode 100644 index 00000000..4c20b942 --- /dev/null +++ b/typ/intro/index.typ @@ -0,0 +1,563 @@ +#import "../config.typ": * + +#h1(if lang == "en" [Introduction] + else if lang == "de" [Einleitung] + else if lang == "uk" [Вступ] + else if lang == "zh" [引言] + else { todo }) +#set heading(offset: whole*2) + +#if lang == "en" [ + Welcome to The Embedded Rust Book: An introductory book about using + the Rust Programming Language on "Bare Metal" embedded systems, such as Microcontrollers. +] else if lang == "de" [ + Willkommen bei „Das Embedded-Rust Buch“ – einem Einführungswerk zur Verwendung + der Programmiersprache Rust auf „Bare-Metal“-Embedded-Systemen, wie etwa Mikrocontrollern. +] else if lang == "uk" [ + Ласкаво просимо до The Embedded Rust Book: ознайомчої книги про використання + мови програмування Rust у вбудованих "Bare Metal" системах, таких як мікроконтролери. +] else if lang == "zh" [ + 欢迎阅读嵌入式Rust:一本关于如何在裸机(比如,微处理器)上使用Rust编程语言的入门书籍。 +] else { todo } + += #(if lang == "en" [Who Embedded Rust is For] + else if lang == "de" [Für wen Embedded-Rust geeignet ist] + else if lang == "uk" [Для кого Вбудований Rust] + else if lang == "zh" [嵌入式Rust是为谁准备的] + else { todo }) + +#let url_rustbook = "https://doc.rust-lang.org/book/ch00-00-introduction.html" +#if lang == "en" [ + Embedded Rust is for everyone who wants to do embedded programming while + taking advantage of the higher-level concepts and safety guarantees the + Rust language provides. (See also + #link(url_rustbook)[Who Rust Is For]) +] else if lang == "de" [ + Embedded-Rust ist für alle, die eingebettete Systeme programmieren und + dabei die Vorteile der fortgeschrittenen Konzepte und + Sicherheitsgarantien der Rust-Sprache nutzen möchten. + + (Siehe auch + #link(url_rustbook)[Für wen ist Rust geeignet?]) +] else if lang == "uk" [ + Вбудований Rust для кожного, хто бажає програмувати вбудовані системи використовуючи + переваги концепцій вищого рівня та гарантій безпеки, які надає мова Rust. + (Дивіться також #link(url_rustbook)[Для кого Rust]) +] else if lang == "zh" [ + 嵌入式Rust是为了那些既想要进行嵌入式编程,又想要使用Rust语言所提供的高级概念和安全保障的人们而准备的(参见#link(url_rustbook)[Who Rust Is For]) +] else { todo } + += #(if lang == "en" [Scope] + else if lang == "de" [Umfang] + else if lang == "uk" [Область застосування] + else if lang == "zh" [本书范围] + else { todo }) + +#if lang == "en" [ + The goals of this book are: + - Get developers up to speed with embedded Rust development. + i.e.~How to et up a development environment. + - Share _current_ best practices about using Rust for embedded + development. i.e. How to best use Rust language features to write more + correct embedded software. + - Serve as a cookbook in some cases. e.g.~How do I mix C and Rust in a + single project? +] else if lang == "de" [ + Die Ziele dieses Buches sind: + - Machen Sie Entwickler mit der Embedded-Rust-Entwicklung vertraut -- + zum Beispiel damit, wie man eine Entwicklungsumgebung einrichtet. + - Teilen Sie _aktuelle_ Best Practices für den Einsatz von Rust in + der Embedded-Entwicklung -- zum Beispiel: Wie man Sprachfunktionen von + Rust optimal nutzt, um korrektere Embedded-Software zu schreiben. + - Dient in einigen Fällen als Kochbuch -- zum Beispiel: Wie kombiniere + ich C und Rust in einem einzigen Projekt? +] else if lang == "uk" [ + Цілі цієї книги: + - Надати розробникам швидкий доступ до розробки вбудованих систем на Rust. + Тобто як налаштувати середовище розробки. + - Поділіться найкращими _сучасними_ практиками використання Rust + для розробки вбудованих систем. Тобто як найкраще використовувати можливості мови Rust + для написання більш коректних вбудованих програм. + - Cлугувати кулінарною книгою у деяких випадках. Наприклад як поєднати C та Rust в одному проекті? +] else if lang == "zh" [ + 这本书的目的是: + - 让开发者快速上手Rust嵌入式开发,比如,如何设置一个开发环境。 + - 分享那些关于使用Rust进行嵌入式开发的,现存的,最好的实践经验,比如,如何最大程度上地利用好Rust语言的特性去写更正确的嵌入式软件 + - 某种程度下作为工具书,比如,如何在一个项目里将C和Rust混合在一起使用 +] else { todo } + +#if lang == "en" [ + This book tries to be as general as possible but to make things easier + for both the readers and the writers it uses the ARM Cortex-M + architecture in all its examples. However, the book doesn't assume that + the reader is familiar with this particular architecture and explains + details particular to this architecture where required. +] else if lang == "de" [ + Dieses Buch ist bestrebt, so allgemein wie möglich zu bleiben; um jedoch + sowohl den Lesern als auch den Autoren die Arbeit zu erleichtern, + verwendet es in allen Beispielen die ARM-Cortex-M-Architektur. Dennoch + setzt das Buch keine Vorkenntnisse zu dieser speziellen Architektur + voraus und erläutert die entsprechenden Besonderheiten, wo immer dies + erforderlich ist. +] else if lang == "uk" [ + Ця книга намагається бути якомога більш загальною, але щоб полегшити роботу + як читачам, так і авторам, у ній використовується архітектура ARM Cortex-M у всіх її прикладах. + Однак книга не передбачає, що читач знайомий із цією конкретною архітектурою, + і пояснює деталі, пов’язані з цією архітектурою, де потрібно. +] else if lang == "zh" [ + 虽然尽可能地尝试让这本书可以用于大多数场景,但是为了使读者和作者更容易理解,在所有的示例中,这本书都使用了ARM + Cortex-M架构。然而,这本书并不需要读者熟悉这个架构,书中会在需要时对这个架构的特定细节进行解释。 +] else { todo } + += #(if lang == "en" [Who This Book is For] + else if lang == "de" [Für wen dieses Buch gedacht ist] + else if lang == "uk" [Для кого Ця Книга] + else if lang == "zh" [这本书是为谁准备的] + else { todo }) + +#if lang == "en" [ + This book caters towards people with either some embedded background or + some Rust background, however we believe everybody curious about + embedded Rust programming can get something out of this book. For those + without any prior knowledge we suggest you read the "Assumptions and + Prerequisites" section and catch up on missing knowledge to get more out + of the book and improve your reading experience. You can check out the + "Other Resources" section to find resources on topics you might want to + catch up on. +] else if lang == "de" [ + Dieses Buch richtet sich an Personen mit Vorkenntnissen im Bereich + Embedded-Systeme oder Rust; wir sind jedoch der Meinung, dass jeder, der + sich für die Embedded-Programmierung mit Rust interessiert, von diesem + Buch profitieren kann. Wer keine Vorkenntnisse mitbringt, dem empfehlen + wir, den Abschnitt „Annahmen und Voraussetzungen" zu lesen und sich + entsprechendes Wissen anzueignen, um den größtmöglichen Nutzen aus dem + Buch zu ziehen und das Leseerlebnis zu verbessern. Im Abschnitt „Weitere + Ressourcen" finden Sie Hinweise zu Themen, in die Sie sich bei Bedarf + einarbeiten können. +] else if lang == "uk" [ + Ця книга розрахована на людей, які мають досвід роботи з вбудованими програмами або з Rust, + але ми віримо, що всі, хто цікавиться програмуванням у вбудованому Rust, можуть щось отримати з цієї книги. + Для тих, хто не має попередніх знань, ми пропонуємо прочитати розділ «Припущення та передумови» + та надолужити пропущені знання, щоб отримати більше від книги та покращити свій досвід читання. + Ви можете переглянути розділ «Інші ресурси», щоб знайти ресурси на теми, які, можливо, захочете надолужити. +] else if lang == "zh" [ + 这本书适合那些有一些嵌入式背景或者有Rust背景的人,然而我相信每一个对Rust嵌入式编程好奇的人都能从这本书中获得某些收获。对于那些先前没有任何经验的人,我们建议你读一下"要求和预备知识"部分。从其它资料中获取、补充缺失的知识,这样能提高你的阅读体验。你可以看看"其它资源"部分,以找到你感兴趣的那些主题的资源。 +] else { todo } + +== #(if lang == "en" [Assumptions and Prerequisites] + else if lang == "de" [Annahmen und Voraussetzungen] + else if lang == "uk" [Припущення та передумови] + else if lang == "zh" [要求和预备知识] + else { todo }) + +#let url_ediion = "https://doc.rust-lang.org/edition-guide/" +#if lang == "en" [ + - You are comfortable using the Rust Programming Language, and have + written, run, and debugged Rust applications on a desktop environment. + You should also be familiar with the idioms of the + #link(url_ediion)[2018 edition] as + this book targets Rust 2018. + + - You are comfortable developing and debugging embedded systems in + another language such as C, C++, or Ada, and are familiar with + concepts such as: + - Cross Compilation + - Memory Mapped Peripherals + - Interrupts + - Common interfaces such as I2C, SPI, Serial, etc. +] else if lang == "de" [ + - Sie sind sicher im Umgang mit der Programmiersprache Rust und haben + bereits Rust-Anwendungen in einer Desktop-Umgebung geschrieben, + ausgeführt und deren Fehler behoben. Zudem sollten Sie mit den Idiomen + der #link(url_ediion)[2018 Edition] + vertraut sein, da sich dieses Buch auf Rust 2018 konzentriert. + + - Sie sind sicher in der Entwicklung und Fehlersuche bei + Embedded-Systemen in einer anderen Sprache wie C, C++ oder Ada und mit + Konzepten vertraut wie: + - Cross-Kompilierung + - Im Speicher abgebildete Peripheriegeräte + - Interrupts + - Gängige Schnittstellen wie I2C, SPI, seriell usw. +] else if lang == "uk" [ + - Вам зручно користуватися мовою програмування Rust і ви вже писали, + запускали та налагоджували програми Rust у середовищі робочого столу. + Ви також повинні бути знайомі з ідіомами #link(url_ediion)[2018 Edition], оскільки ця книга спрямована на Rust 2018. + - Вам зручно розробляти та налагоджувати вбудовані системи іншою + мовою, наприклад C, C++ або Ada, і ви знайомі з такими поняттями, як: + - Крос-компіляція + - Периферійні пристрої з відображенням у пам'яті + - Переривання + - Загальні інтерфейси, такі як I2C, SPI, Serial тощо. +] else if lang == "zh" [ + - 你可以轻松地使用Rust编程语言,且在一个桌面环境上写过,运行过,调试过Rust应用。你应该也要熟悉#link(url_ediion)[2018 edition]的术语,因为这本书是面向Rust + 2018的。 + + - 你可以轻松地使用其它语言,比如C,C++或者Ada,开发和调试嵌入式系统,且熟悉如下的概念: + - 交叉编译 + - 存储映射的外设(Memory Mapped Peripherals) + - 中断 + - I2C,SPI,串口等等常见的接口 +] else { todo } + +== #(if lang == "en" [Other Resources] + else if lang == "de" [Weitere Ressourcen] + else if lang == "uk" [Інші ресурси] + else if lang == "zh" [其它资源] + else { todo }) + +#if lang == "en" [ + If you are unfamiliar with anything mentioned above or if you want more + information about a specific topic mentioned in this book you might find + some of these resources helpful. +] else if lang == "de" [ + Falls Ihnen einer der oben genannten Punkte nicht vertraut ist oder Sie + mehr über ein bestimmtes, in diesem Buch erwähntes Thema erfahren + möchten, könnten einige dieser Ressourcen hilfreich für Sie sein. +] else if lang == "uk" [ + Якщо ви не знайомі з чимось із згаданого вище або вам потрібна додаткова інформація про конкретну тему, згадану в цій книзі, деякі з цих ресурсів можуть стати в нагоді. +] else if lang == "zh" [ + 如果你还不熟悉上面提到的东西或者你对这本书中提到的某个特定主题感兴趣,你也许能从这些资源中找到有用的信息。 +] else { todo } + +#figure( + kind: table, + table( + columns: (37.84%, 27.03%, 35.14%), + align: (center,left,left,), + table.header( + if lang == "en" [Topic] + else if lang == "de" [Thema] + else if lang == "zh" [主题] + else { todo }, + if lang == "en" [Resource] + else if lang == "de" [Ressource] + else if lang == "zh" [资源] + else { todo }, + if lang == "en" [Description] + else if lang == "de" [Beschreibung] + else if lang == "zh" [描述] + else { todo } + ), + [Rust], + link("https://doc.rust-lang.org/book/")[Rust Book], + if lang == "en" [ + If you are not yet comfortable with Rust, we highly suggest reading this book. + ] else if lang == "de" [ + Wenn Sie sich mit Rust noch nicht sicher fühlen, + empfehlen wir Ihnen dringend, dieses Buch zu lesen. + ] else if lang == "zh" [ + 如果你还不熟悉Rust,我们强烈建议你读这本书. + ] else { todo }, + + [Rust, Embedded], + link("https://docs.rust-embedded.org/discovery/")[Discovery Book], + if lang == "en" [ + If you have never done any embedded programming, this book might be a better start + ] else if lang == "de" [ + Wenn Sie sich noch nie mit eingebetteter Programmierung beschäftigt haben, + ist dieses Buch möglicherweise der bessere Einstieg + ] else if lang == "zh" [ + 如果你从没做过嵌入式编程,这本书可能是个更好的开端. + ] else { todo }, + + [Rust, Embedded], link("https://docs.rust-embedded.org")[Embedded Rust Bookshelf], + if lang == "en" [ + Here you can find several other resources provided by Rust's Embedded Working Group. + ] else if lang == "de" [ + Hier finden Sie weitere Ressourcen, die von der Embedded Working Group + von Rust bereitgestellt werden. + ] else if lang == "zh" [ + 在这里,你可以找到由Rust的嵌入式工作组提供的许多其它资源. + ] else { todo }, + + [Rust, Embedded], + link("https://docs.rust-embedded.org/embedonomicon/")[Embedonomicon], + if lang == "en" [ + The nitty gritty details when doing embedded programming in Rust. + ] else if lang == "de" [ + Die Feinheiten der Embedded-Programmierung mit Rust. + ] else if lang == "zh" [ + 用Rust进行嵌入式编程的细节. + ] else { todo }, + + [Rust, Embedded], + link("https://docs.rust-embedded.org/faq.html")[embedded FAQ], + if lang == "en" [ + Frequently asked questions about Rust in an embedded context. + ] else if lang == "de" [ + Häufig gestellte Fragen zu Rust im Embedded-Umfeld. + ] else if lang == "zh" [ + Rust在嵌入式上下文中遇到的常见问题. + ] else { todo }, + + [Rust, Embedded], + link("https://google.github.io/comprehensive-rust/bare-metal.html")[Comprehensive Rust 🦀: Bare Metal], + if lang == "en" [ + Teaching material for a 4-day class on bare-metal Rust development + ] else if lang == "de" [ + Unterrichtsmaterial für einen viertägigen Kurs zu Bare-Metal Rust development + ] else if lang == "zh" [ + 用于四天课时的裸机Rust开发课程的教学资料. + ] else { todo }, + + [Interrupts], + link("https://en.wikipedia.org/wiki/Interrupt")[Interrupt], + [-], + + [Memory-mapped IO/Peripherals], + link("https://en.wikipedia.org/wiki/Memory-mapped_I/O")[Memory-mapped I/O], + [-], + + [SPI, UART, RS232, USB, I2C, TTL], + link("https://electronics.stackexchange.com/questions/37814/usart-uart-rs232-usb-spi-i2c-ttl-etc-what-are-all-of-these-and-how-do-th")[Stack Exchange about SPI, UART, and other interfaces], + [-], + ) +) + +== #(if lang == "en" [Translations] + else if lang == "de" [Übersetzungen] + else if lang == "uk" [Переклади] + else if lang == "zh" [翻译] + else { todo }) + +#{ +let jp_ln = "https://tomoyuki-nakabayashi.github.io/book/" +let jp_rep = "https://github.com/tomoyuki-nakabayashi/book" +let cn_ln = "https://xxchang.github.io/book/" +let cn_rep = "https://github.com/XxChang/book" +if lang == "en" [ + This book has been translated by generous volunteers. If you would like + your translation listed here, please open a PR to add it. + - #link(jp_ln)[Japanese] (#link(jp_rep)[repository]) + - #link(cn_ln)[Chinese] (#link(cn_rep)[repository]) +] else if lang == "de" [ + Dieses Buch wurde von großzügigen Freiwilligen übersetzt. Wenn Ihre + Übersetzung hier aufgeführt werden soll, erstellen Sie bitte einen Pull + Request, um sie hinzuzufügen. + - #link(jp_ln)[Japanisch] (#link(jp_rep)[repository]) + - #link(cn_ln)[Chinesisch] (#link(cn_rep)[repository]) +] else if lang == "uk" [ + Цю книгу перекладено волонтерами. + Якщо ви хочете, щоб ваш переклад був тут, будь ласка, + відкрийте запит на злиття, щоб додати його. + - #link(jp_ln)[Японською] (#link(jp_rep)[репозиторій]) + - #link(cn_ln)[Китайською] (#link(cn_rep)[репозиторій]) +] else if lang == "zh" [ + 这本书是已经被一些慷慨的志愿者们翻译了。如果你想要将你的翻译列在这里,请打开一个PR去添加它。 + - #link(jp_ln)[日文] (#link(jp_rep)[repository]) + - #link(cn_ln)[中文] (#link(cn_rep)[repository]) +] else { todo } +} + +== #(if lang == "en" [How to Use This Book] + else if lang == "de" [Wie Sie dieses Buch nutzen] + else if lang == "uk" [Як читати цю книгу] + else if lang == "zh" [如何使用这本书] + else { todo }) + +#if lang == "en" [ + This book generally assumes that you're reading it front-to-back. Later + chapters build on concepts in earlier chapters, and earlier chapters may + not dig into details on a topic, revisiting the topic in a later + chapter. +] else if lang == "de" [ + Dieses Buch ist im Allgemeinen so konzipiert, dass es von Anfang bis + Ende gelesen wird. Spätere Kapitel bauen auf den Konzepten früherer + Kapitel auf; dabei werden bestimmte Themen in den früheren Kapiteln + möglicherweise noch nicht bis ins Detail behandelt, sondern erst in + einem späteren Kapitel erneut aufgegriffen. +] else if lang == "uk" [ + Ця книга зазвичай передбачає, що ви читаєте її послідовно. Подальші + розділи ґрунтуються на концепціях попередніх розділів, і попередні розділи можуть + не заглиблюватися в деталі теми, повертаючись до неї в наступному розділі. +] else if lang == "zh" [ + 这本书通常假设你是按顺序阅读的。之后的章节是建立在先前的章节中提到的概念之上的,先前章节可能不会深入一个主题的细节,因为在随后的章节将会再次重温这个主题。 +] else { todo } + +#let url_f3disco = "http://www.st.com/en/evaluation-tools/stm32f3discovery.html" +#if lang == "en" [ + This book will be using the #link(url_f3disco)[STM32F3DISCOVERY] + development board from STMicroelectronics for the majority of the + examples contained within. This board is based on the ARM Cortex-M + architecture, and while basic functionality is the same across most CPUs + based on this architecture, peripherals and other implementation details + of Microcontrollers are different between different vendors, and often + even different between Microcontroller families from the same vendor. +] else if lang == "de" [ + Für den Großteil der in diesem Buch enthaltenen Beispiele wird das + Entwicklungsboard #link(url_f3disco)[STM32F3DISCOVERY] + von STMicroelectronics verwendet. Dieses Board basiert auf der + ARM-Cortex-M-Architektur. Zwar ist die grundlegende Funktionalität bei + den meisten auf dieser Architektur basierenden CPUs identisch, doch + unterscheiden sich Peripheriekomponenten und andere + Implementierungsdetails der Mikrocontroller je nach Hersteller -- und + oft sogar zwischen verschiedenen Mikrocontroller-Familien desselben + Herstellers. +] else if lang == "uk" [ + Для більшості наведених у цій книзі прикладів використовуватиметься + розробна плата #link(url_f3disco)[STM32F3DISCOVERY] від STMicroelectronics. Ця плата + заснована на архітектурі ARM Cortex-M, і хоча основні функції однакові + для більшості процесорів на основі цієї архітектури, периферійні пристрої та інші + деталі реалізації відрізняються у мікроконтролерах різних постачальників, + і часто навіть різняться між сімействами мікроконтролерів одного й того ж + постачальника. +] else if lang == "zh" [ + 在大多数示例中这本书将使用#link(url_f3disco)[STM32F3DISCOVERY]开发板。这个板子是基于ARM + Cortex-M架构的,且基本功能与大多数基于这个架构的CPUs功能相似。微处理器的外设和其它实现细节在不同的厂家之间是不同的,甚至来自同一个厂家,不同处理器系列之间也是不同的。 +] else { todo } + +#if lang == "en" [ + For this reason, we suggest purchasing the + #link(url_f3disco)[STM32F3DISCOVERY] + development board for the purpose of following the examples in this + book. +] else if lang == "de" [ + Aus diesem Grund empfehlen wir die Anschaffung des Entwicklungsboards + #link(url_f3disco)[STM32F3DISCOVERY], + um die Beispiele in diesem Buch praktisch nachvollziehen zu können. +] else if lang == "uk" [ + З цієї причини ми пропонуємо придбати плату розробки + #link(url_f3disco)[STM32F3DISCOVERY] щоб повторювати приклади з цієї книги. +] else if lang == "zh" [ + 因此我们建议购买#link(url_f3disco)[STM32F3DISCOVERY]开发板来尝试这本书中的例子。 +] else { todo } + +== #(if lang == "en" [Contributing to This Book] + else if lang == "de" [Beiträge zu diesem Buch] + else if lang == "uk" [Внесок у цю книгу] + else if lang == "zh" [贡献] + else { todo }) + +#let url_repo = "https://github.com/rust-embedded/book" +#let url_team = "https://github.com/rust-embedded/wg#the-resources-team" +#if lang == "en" [ + The work on this book is coordinated in #link(url_repo)[this repository] + and is mainly developed by the #link(url_team)[resources team]. +] else if lang == "de" [ + Die Arbeit an diesem Buch wird in #link(url_repo)[diesem Repository] koordiniert + und hauptsächlich vom #link(url_team)[Ressourcen-Team] vorangetrieben. +] else if lang == "uk" [ + Робота над цією книгою координується в #link(url_repo)[цьому сховищі] + і в основному розробляється #link(url_team)[командою ресурсів]. +] else if lang == "zh" [ + 这本书的工作主要在#link(url_repo)[这个仓库]里管理,且主要由#link(url_team)[resouces team]开发。 +] else { todo } + +#let url_issues = "https://github.com/rust-embedded/book/issues/" +#if lang == "en" [ + If you have trouble following the instructions in this book or find that + some section of the book is not clear enough or hard to follow then + that's a bug and it should be reported in #link(url_issues)[the issue tracker] + of this book. +] else if lang == "de" [ + Wenn Sie Schwierigkeiten haben, den Anweisungen in diesem Buch zu + folgen, oder wenn ein Abschnitt des Buches nicht klar genug oder schwer + verständlich ist, handelt es sich um einen Fehler; dieser sollte im + #link(url_issues)[Problem-Verfolgungswerkzeug] + des Buches gemeldet werden. +] else if lang == "uk" [ + Якщо у вас виникли проблеми з дотриманням інструкцій з цієї книги + або ви вважаєте, що якийсь розділ книги недостатньо зрозумілий + або складний для виконання, то це помилка, і про неї слід повідомити + в #link(url_issues)[трекері випуску] цієї книги. +] else if lang == "zh" [ + 如果你按着这本书的操作遇到了什么麻烦,或者这本书的一些部分不够清楚,或者很难进行下去,那这本书就是有个bug,这个bug应该被报道给这本书的#link("https://github.com/rust-embedded/book/issues/")[the issue tracker] + 。 +] else { todo } + +#if lang == "en" [ + Pull requests fixing typos and adding new content are very welcome! +] else if lang == "de" [ + Pull Requests, die Tippfehler korrigieren oder neue Inhalte hinzufügen, + sind herzlich willkommen! +] else if lang == "uk" [ + Запити на виправлення помилок і додавання нового матеріалу дуже вітаються! +] else if lang == "zh" [ + 修改拼写错误和添加新内容的Pull requests非常欢迎! +] else { todo } + += #(if lang == "en" [Re-using this material] + else if lang == "de" [Wiederverwendung dieses Materials] + else if lang == "uk" [Повторне використання цього матеріалу] + else if lang == "zh" [二次使用这个材料] + else { todo }) + +#{ +let mit = "https://opensource.org/licenses/MIT" +let apache = "http://www.apache.org/licenses/LICENSE-2.0" +let legalcode = "https://creativecommons.org/licenses/by-sa/4.0/legalcode" +if lang == "en" [ + This book is distributed under the following licenses: + - The code samples and free-standing Cargo projects contained within + this book are licensed under the terms of both the + #link(mit)[MIT License] and the + #link(apache)[Apache License v2.0]. + - The written prose, pictures and diagrams contained within this book + are licensed under the terms of the Creative Commons + #link(legalcode)[CC-BY-SA v4.0] + license. +] else if lang == "de" [ + Dieses Buch wird unter den folgenden Lizenzen vertrieben: + - Die in diesem Buch enthaltenen Codebeispiele und eigenständigen + Cargo-Projekte stehen unter den Bedingungen sowohl der + #link(apache)[MIT-Lizenz] als auch der + #link(apache)[Apache-Lizenz v2.0]. + - Die in diesem Buch enthaltenen Texte, Bilder und Diagramme stehen + unter der Lizenz Creative Commons + #link(legalcode)[CC-BY-SA v4.0]. +] else if lang == "uk" [ + Ця книга розповсюджується за наступними ліцензіями: + - Зразки коду та окремі проекти Cargo, що містяться в цій книзі, ліцензовано + відповідно до умов #link(mit)[MIT License] та #link(apache)[Apache License v2.0]. + - Текст, малюнки та діаграми, що містяться в цій книзі, ліцензовані відповідно до + умов ліцензії Creative Commons #link(legalcode)[CC-BY-SA v4.0]. +] else if lang == "zh" [ + 这本书根据以下许可证发布: + - 本书中包含的代码示例和独立的Cargo项目均根据#link(mit)[MIT License]和#link(apache)[Apache License v2.0]发放许可的。 + - 本书中包含的文档,图片和表格均根据#link(legalcode)[CC-BY-SA v4.0]发放许可的。 +] else { todo } + +if lang == "en" [ + TL;DR: If you want to use our text or images in your work, you need to: + - Give the appropriate credit (i.e.~mention this book on your slide, and + provide a link to the relevant page) + - Provide a link to the + #link(legalcode)[CC-BY-SA v4.0] + licence + - Indicate if you have changed the material in any way, and make any + changes to our material available under the same licence +] else if lang == "de" [ + Kurz gesagt: Wenn Sie unsere Texte oder Bilder für Ihre Arbeit verwenden + möchten, müssen Sie: + - Geben Sie die entsprechende Quellenangabe an (d.~h. erwähnen Sie + dieses Buch auf Ihrer Folie und geben Sie einen Link zur entsprechenden Seite an). + - Geben Sie einen Link zur + #link(legalcode)[CC-BY-SA v4.0]-Lizenz an. + - Geben Sie an, ob Sie das Material in irgendeiner Weise verändert + haben, und stellen Sie Änderungen an unserem Material unter derselben + Lizenz zur Verfügung. +] else if lang == "uk" [ + TL;DR: Якщо ви маєте намір використовувати наш текст або зображення у своїй роботі, вам потрібно: + - Надати відповідне посилання (тобто згадати цю книгу + на своєму слайді та надати посилання на відповідну сторінку) + - Надати посилання на ліцензію #link(legalcode)[CC-BY-SA v4.0] + - Вказати, чи змінювали ви матеріал будь-яким чином, + і зробити зміни в наші матеріали доступними за тією ж ліцензією +] else if lang == "zh" [ + 总之:如果你想在你的工作中使用我们的文档或者图片,你需要: + - 提供合适的授信 (i.e.~在你的幻灯片中提到本书,提供相关页面的连接) + - 提供#link(legalcode)[CC-BY-SA v4.0]的许可证的链接 + - 指出你是否改变了材料的内容,在同一个许可证下,可以对材料进行任何改变 +] else { todo } +} + +#if lang == "en" [ + Also, please do let us know if you find this book useful! +] else if lang == "de" [ + Teilen Sie uns bitte auch mit, ob Sie dieses Buch nützlich finden! +] else if lang == "uk" [ + Також, будь ласка, повідомте нам, якщо ви знайдете цю книгу корисною! +] else if lang == "zh" [ + 也请告诉我这本书对你是否有帮助! +] else { todo } diff --git a/typ/intro/install.typ b/typ/intro/install.typ new file mode 100644 index 00000000..6a922065 --- /dev/null +++ b/typ/intro/install.typ @@ -0,0 +1,134 @@ +#import "../config.typ": * + +#h1(offset: whole, + if lang == "en" [Installing the tools] + else { todo }) +#set heading(offset: whole*2) + +#if lang == "en" [ + This page contains OS-agnostic installation instructions for a few of + the tools: +] else { todo } + +== Rust Toolchain + +#let ln_rustup = link("https://rustup.rs") +#if lang == "en" [ + Install rustup by following the instructions at #ln_rustup. +] else { todo } + +#if lang == "en" [ + *NOTE* Make sure you have a compiler version equal to or newer than `1.31`. + `rustc -V` should return a date newer than the one shown below. +] else { todo } + +```text +$ rustc -V +rustc 1.31.1 (b6c32da9b 2018-12-18) +``` + +#let url_find = "https://developer.arm.com/ip-products/processors/cortex-m#c-7d3b69ce-5b17-4c9e-8f06-59b605713133" +#if lang == "en" [ + For bandwidth and disk usage concerns the default installation only + supports native compilation. To add cross compilation support for the + ARM Cortex-M architectures choose one of the following compilation + targets. For the STM32F3DISCOVERY board used for the examples in this + book, use the `thumbv7em-none-eabihf` target. + #link(url_find)[Find the best Cortex-M for you.] +] else { todo } + +#if lang == "en" [ + Cortex-M0, M0+, and M1 (ARMv6-M architecture): +] else { todo } + +```console +rustup target add thumbv6m-none-eabi +``` + +#if lang == "en" [ + Cortex-M3 (ARMv7-M architecture): +] else { todo } + +```console +rustup target add thumbv7m-none-eabi +``` + +#if lang == "en" [ + Cortex-M4 and M7 without hardware floating point (ARMv7E-M architecture): +] else { todo } + +```console +rustup target add thumbv7em-none-eabi +``` + +#if lang == "en" [ + Cortex-M4F and M7F with hardware floating point (ARMv7E-M architecture): +] else { todo } + +```console +rustup target add thumbv7em-none-eabihf +``` + +#if lang == "en" [ + Cortex-M23 (ARMv8-M architecture): +] else { todo } + +```console +rustup target add thumbv8m.base-none-eabi +``` + +#if lang == "en" [ + Cortex-M33 and M35P (ARMv8-M architecture): +] else { todo } + +```console +rustup target add thumbv8m.main-none-eabi +``` + +#if lang == "en" [ +Cortex-M33F and M35PF with hardware floating point (ARMv8-M architecture): +] else { todo } + +```console +rustup target add thumbv8m.main-none-eabihf +``` + +== `cargo-binutils` + +```text +cargo install cargo-binutils + +rustup component add llvm-tools +``` + +#let ln_vs = link("https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools&rel=16") +#if lang == "en" [ + WINDOWS: prerequisite C++ Build Tools for Visual Studio 2019 is + installed. #ln_vs +] else { todo } + +== `cargo-generate` + +#if lang == "en" [ + We'll use this later to generate a project from a template. +] else { todo } + +```console +cargo install cargo-generate +``` + +#if lang == "en" [ + Note: on some Linux distros (e.g.~Ubuntu) you may need to install the + packages `libssl-dev` and `pkg-config` prior to installing + cargo-generate. +] else { todo } + +== #(if lang == "en" [OS-Specific Instructions] + else { todo }) + +#if lang == "en" [ + Now follow the instructions specific to the OS you are using: +] else { todo } +- #link("install/linux.html")[Linux] +- #link("install/windows.html")[Windows] +- #link("install/macos.html")[macOS] diff --git a/typ/intro/install/linux.typ b/typ/intro/install/linux.typ new file mode 100644 index 00000000..9e3e77fa --- /dev/null +++ b/typ/intro/install/linux.typ @@ -0,0 +1,192 @@ +#import "../../config.typ": * + +#h1(offset: whole*2, + [Linux]) +#set heading(offset: whole*3) + +#if lang == "en" [ + Here are the installation commands for a few Linux distributions. +] else if lang == "de" [ + Hier sind die Installationsbefehle für einige Linux-Distributionen. +] else { todo } + += #(if lang == "en" [Packages] + else if lang == "de" [Pakete] + else { todo }) + +#if lang == "en" [ + - Ubuntu 18.04 or newer / Debian stretch or newer +] else if lang == "de" [ + - Ubuntu 18.04 oder neuer / Debian Stretch oder neuer +] else { todo } + +#quote(block: true)[ +#if lang == "en" [ + *NOTE* `gdb-multiarch` is the GDB command you'll use to debug + your ARM Cortex-M programs +] else if lang == "de" [ + *HINWEIS* `gdb-multiarch` ist der GDB-Befehl, den Sie zum + Debuggen Ihrer ARM-Cortex-M-Programme verwenden werden. +] else { todo } +] + +```console +sudo apt install gdb-multiarch openocd qemu-system-arm +``` + +#if lang == "en" [ + - Ubuntu 14.04 and 16.04 +] else if lang == "de" [ + - Ubuntu 14.04 und 16.04 +] else { todo } + +#quote(block: true)[ +#if lang == "en" [ + *NOTE* `arm-none-eabi-gdb` is the GDB command you'll use to debug + your ARM Cortex-M programs +] else if lang == "de" [ + *HINWEIS* `arm-none-eabi-gdb` ist der GDB-Befehl, den Sie zum + Debuggen Ihrer ARM-Cortex-M-Programme verwenden werden. +] else { todo } +] + +```console +sudo apt install gdb-arm-none-eabi openocd qemu-system-arm +``` + +#if lang == "en" [ + - Fedora 27 or newer +] else if lang == "de" [ + - Fedora 27 oder neuer +] else { todo } + +```console +sudo dnf install gdb openocd qemu-system-arm +``` + +- Arch Linux + +#quote(block: true)[ +#if lang == "en" [ + *NOTE* `arm-none-eabi-gdb` is the GDB command you'll use to debug + ARM Cortex-M programs +] else if lang == "de" [ + *HINWEIS* `arm-none-eabi-gdb` ist der GDB-Befehl, den Sie zum + Debuggen von ARM-Cortex-M-Programmen verwenden. +] else { todo } +] + +```console +sudo pacman -S arm-none-eabi-gdb qemu-system-arm openocd +``` + += #(if lang == "en" [udev rules] + else if lang == "de" [udev-Regeln] + else { todo }) + +#if lang == "en" [ + This rule lets you use OpenOCD with the Discovery board without root privilege. +] else if lang == "de" [ + Diese Regel ermöglicht die Nutzung von OpenOCD mit dem Discovery Board ohne Root-Rechte. +] else { todo } + +#{ +let f = `/etc/udev/rules.d/70-st-link.rules` +if lang == "en" [ + Create the file #f with the contents shown below. +] else if lang == "de" [ + Erstellen Sie die Datei #f mit dem unten stehenden Inhalt. +] else { todo } +} + +```text +# STM32F3DISCOVERY rev A/B - ST-LINK/V2 +ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3748", TAG+="uaccess" + +# STM32F3DISCOVERY rev C+ - ST-LINK/V2-1 +ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374b", TAG+="uaccess" +``` + +#if lang == "en" [ + Then reload all the udev rules with: +] else if lang == "de" [ + Laden Sie anschließend alle udev-Regeln neu mit: +] else { todo } + +```console +sudo udevadm control --reload-rules +``` + +#if lang == "en" [ + If you had the board plugged to your laptop, unplug it and then plug it again. +] else if lang == "de" [ + Wenn Sie die Platine an Ihren Laptop angeschlossen hatten, ziehen Sie + den Stecker heraus und schließen Sie ihn dann erneut an. +] else { todo } + +#if lang == "en" [ + You can check the permissions by running this command: +] else if lang == "de" [ + Sie können die Berechtigungen überprüfen, indem Sie diesen Befehl ausführen: +] else { todo } + +```console +lsusb +``` + +#if lang == "en" [ + Which should show something like +] else if lang == "de" [ + Was so etwas wie +] else { todo } + +```text +(..) +Bus 001 Device 018: ID 0483:374b STMicroelectronics ST-LINK/V2.1 +(..) +``` +#if lang == "de" [ + anzeigen sollte. +] + +#if lang == "en" [ + Take note of the bus and device numbers. Use those numbers to create a + path like `/dev/bus/usb//`. Then use this path like so: +] else if lang == "de" [ + Notieren Sie sich die Bus- und Gerätenummern. Verwenden Sie diese + Nummern, um einen Pfad wie `/dev/bus/usb//` zu bilden. + Verwenden Sie diesen Pfad anschließend wie folgt: +] else { todo } + +```console +ls -l /dev/bus/usb/001/018 +``` + +```text +crw-------+ 1 root root 189, 17 Sep 13 12:34 /dev/bus/usb/001/018 +``` + +```console +getfacl /dev/bus/usb/001/018 | grep user +``` + +```text +user::rw- +user:you:rw- +``` + +#if lang == "en" [ + The `+` appended to permissions indicates the existence of an extended + permission. The `getfacl` command tells the user `you` can make use of + this device. +] else if lang == "de" [ + Das angehängte „+" bei den Berechtigungen weist auf eine erweiterte + Berechtigung hin. Der Befehl „getfacl" teilt dem Benutzer mit, dass er + dieses Gerät verwenden darf. +] else { todo } + +#if lang == "en" [ + Now, go to the #link()[next section]. +] else if lang == "de" [ + Fahren Sie nun mit dem #link()[nächsten Abschnitt] fort. +] else { todo } diff --git a/typ/intro/install/macos.typ b/typ/intro/install/macos.typ new file mode 100644 index 00000000..c1b6f9d5 --- /dev/null +++ b/typ/intro/install/macos.typ @@ -0,0 +1,64 @@ +#import "../../config.typ": * + +#h1(offset: whole*2, + [macOS]) +#set heading(offset: whole*3) + +#let brew_ln = link("http://brew.sh/")[Homebrew] +#let ports_ln = link("https://www.macports.org/")[MacPorts] + +#if lang == "en" [ + All the tools can be installed using #brew_ln or #ports_ln: +] else if lang == "de" [ + Alle Werkzeuge können mit #brew_ln oder #ports_ln installiert werden: +] else { todo } + += #(if lang == "en" [Install tools with #brew_ln] + else if lang == "de" [Werkzeuge mit #brew_ln installieren] + else { todo }) + +```text +$ # GDB +$ brew install arm-none-eabi-gdb + +$ # OpenOCD +$ brew install openocd + +$ # QEMU +$ brew install qemu +``` + +#quote(block: true)[ +#if lang == "en" [ + *NOTE* If OpenOCD crashes you may need to install the latest + version using: +] else if lang == "de" [ + *HINWEIS* Falls OpenOCD abstürzt, müssen Sie möglicherweise die + neueste Version installieren; verwenden Sie dazu: +] else { todo } +] + +```text +$ brew install --HEAD openocd +``` + += #(if lang == "en" [Install tools with #ports_ln] + else if lang == "de" [Werkzeuge mit #ports_ln installieren] + else { todo }) + +```text +$ # GDB +$ sudo port install arm-none-eabi-gcc + +$ # OpenOCD +$ sudo port install openocd + +$ # QEMU +$ sudo port install qemu +``` + +#if lang == "en" [ + That's all! Go to the #link()[next section]. +] else if lang == "de" [ + Das war's! Gehen Sie zum #link()[nächsten Abschnitt]. +] else { todo } diff --git a/typ/intro/install/verify.typ b/typ/intro/install/verify.typ new file mode 100644 index 00000000..cc42604e --- /dev/null +++ b/typ/intro/install/verify.typ @@ -0,0 +1,152 @@ +#import "../../config.typ": * + +#h1(offset: whole*2, + if lang == "en" [Verify Installation] + else if lang == "de" [Die Installation überprüfen] + else { todo }) + +#set heading(offset: whole*3) + +#if lang == "en" [ + In this section we check that some of the required tools / drivers have + been correctly installed and configured. +] else if lang == "de" [ + In diesem Abschnitt überprüfen wir, ob einige der erforderlichen + Werkzeuge bzw. Treiber korrekt installiert und konfiguriert wurden. +] else { todo } + +#if lang == "en" [ + Connect your laptop / PC to the discovery board using a Mini-USB USB + cable. The discovery board has two USB connectors; use the one labeled + "USB ST-LINK" that sits on the center of the edge of the board. +] else if lang == "de" [ + Verbinden Sie Ihren Laptop oder PC über ein Mini-USB-Kabel mit dem + Discovery-Board. Das Discovery-Board verfügt über zwei USB-Anschlüsse; + verwenden Sie den mit „USB ST-LINK" beschrifteten Anschluss, der sich + mittig an der Platinenkante befindet. +] else { todo } + +#if lang == "en" [ + Also check that the ST-LINK header is populated. See the picture below; + the ST-LINK header is highlighted. +] else if lang == "de" [ + Überprüfen Sie außerdem, ob die ST-LINK-Stiftleiste bestückt ist. Siehe + dazu die Abbildung unten; die ST-LINK-Stiftleiste ist dort + hervorgehoben. +] else { todo } + +#if lang == "en" [ + Now run the following command: +] else if lang == "de" [ + Führen Sie nun den folgenden Befehl aus: +] else { todo } + +```console +openocd -f interface/stlink.cfg -f target/stm32f3x.cfg +``` + +#quote(block: true)[ +#if lang == "en" [ + *NOTE*: Old versions of openocd, including the 0.10.0 release + from 2017, do not contain the new (and preferable) + `interface/stlink.cfg` file; instead you may need to use + `interface/stlink-v2.cfg` or `interface/stlink-v2-1.cfg`. +] else if lang == "de" [ + *HINWEIS*: Ältere Versionen von OpenOCD, einschließlich der + Version 0.10.0 aus dem Jahr 2017, enthalten nicht die neue (und zu + bevorzugende) Datei `interface/stlink.cfg`; stattdessen müssen Sie + möglicherweise `interface/stlink-v2.cfg` oder + `interface/stlink-v2-1.cfg` verwenden. +] else { todo } +] + +#if lang == "en" [ + You should get the following output and the program should block the + console: +] else if lang == "de" [ + Sie sollten die folgende Ausgabe erhalten und das Programm sollte die + Konsole blockieren: +] else { todo } + +```text +Open On-Chip Debugger 0.10.0 +Licensed under GNU GPL v2 +For bug reports, read + http://openocd.org/doc/doxygen/bugs.html +Info : auto-selecting first available session transport "hla_swd". To override use 'transport select '. +adapter speed: 1000 kHz +adapter_nsrst_delay: 100 +Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD +none separate +Info : Unable to match requested speed 1000 kHz, using 950 kHz +Info : Unable to match requested speed 1000 kHz, using 950 kHz +Info : clock speed 950 kHz +Info : STLINK v2 JTAG v27 API v2 SWIM v15 VID 0x0483 PID 0x374B +Info : using stlink api v2 +Info : Target voltage: 2.919881 +Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints +``` + +#if lang == "en" [ + The contents may not match exactly but you should get the last line + about breakpoints and watchpoints. If you got it then terminate the + OpenOCD process and move to the #link()[next section]. +] else if lang == "de" [ + Der Inhalt stimmt möglicherweise nicht exakt überein, aber die letzte + Zeile bezüglich Breakpoints und Watchpoints sollten Sie sehen. Wenn dies + der Fall ist, beenden Sie den OpenOCD-Prozess und fahren Sie mit dem + #link()[nächsten Abschnitt] fort. +] else { todo } + +#if lang == "en" [ + If you didn't get the "breakpoints" line then try one of the following + commands. +] else if lang == "de" [ + Falls du die Zeile „breakpoints" nicht erhalten hast, versuche einen der + folgenden Befehle. +] else { todo } + +```console +openocd -f interface/stlink-v2.cfg -f target/stm32f3x.cfg +``` + +```console +openocd -f interface/stlink-v2-1.cfg -f target/stm32f3x.cfg +``` + +#if lang == "en" [ + If one of those commands works it means you got an old hardware revision + of the discovery board. That won't be a problem but commit that fact to + memory as you'll need to configure things a bit differently later on. + You can move to the #link()[next section]. +] else if lang == "de" [ + Wenn einer dieser Befehle funktioniert, bedeutet das, dass Sie eine + ältere Hardware-Revision des Discovery-Boards besitzen. Das stellt kein + Problem dar, aber merken Sie sich diesen Umstand, da Sie die + Konfiguration später etwas anders vornehmen müssen. Sie können zum + #link()[nächsten Abschnitt] übergehen. +] else { todo } + +#if lang == "en" [ + If none of the commands work as a normal user then try to run them with + root permission (e.g.~`sudo openocd ..`). If the commands do work with + root permission then check that the + #link()[udev rules] have been correctly set. +] else if lang == "de" [ + Wenn keiner der Befehle als normaler Benutzer funktioniert, versuchen + Sie, sie mit Root-Rechten auszuführen (z. B. `sudo openocd ..`). Sollten + die Befehle mit Root-Rechten funktionieren, überprüfen Sie, ob die + #link()[udev-Regeln] korrekt eingerichtet wurden. +] else { todo } + +#let url_issues = "https://github.com/rust-embedded/book/issues" +#if lang == "en" [ + If you have reached this point and OpenOCD is not working please open + #link(url_issues)[an issue] and + we'll help you out! +] else if lang == "de" [ + Wenn Sie an diesem Punkt angelangt sind und OpenOCD nicht funktioniert, + eröffnen Sie bitte + #link(url_issues)[eine Problemmeldung], + und wir helfen Ihnen weiter! +] else { todo } diff --git a/typ/intro/install/windows.typ b/typ/intro/install/windows.typ new file mode 100644 index 00000000..bf8c02dd --- /dev/null +++ b/typ/intro/install/windows.typ @@ -0,0 +1,91 @@ +#import "../../config.typ": * + +#h1(offset: whole*2, + [Windows]) +#set heading(offset: whole*3) + += `arm-none-eabi-gdb` + +#let url_arm_gnu = "https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads" +#if lang == "en" [ + ARM provides `.exe` installers for Windows. Grab one from #link(url_arm_gnu)[here], + and follow the instructions. Just before the installation process + finishes tick/select the "Add path to environment variable" option. Then + verify that the tools are in your `%PATH%`: +] else if lang == "de" [ + ARM stellt `.exe`-Installationsprogramme für Windows bereit. + Laden Sie eines von #link(url_arm_gnu)[hier] + herunter und befolgen Sie die Anweisungen. Aktivieren Sie kurz vor + Abschluss des Installationsvorgangs die Option „Add path to environment + variable". Überprüfen Sie anschließend, ob die Werkzeuge in Ihrem + `%PATH%` enthalten sind: +] else { todo } + +```text +$ arm-none-eabi-gdb -v +GNU gdb (GNU Tools for Arm Embedded Processors 7-2018-q2-update) 8.1.0.20180315-git +(..) +``` + += OpenOCD + +#let url_openocd = "https://xpack.github.io/openocd/" +#let bin_path = `C:\Users\USERNAME\AppData\Roaming\xPacks\@xpack-dev-tools\openocd\0.10.0-13.1\.content\bin\` +#if lang == "en" [ + There's no official binary release of OpenOCD for Windows but if you're + not in the mood to compile it yourself, the xPack project provides a + binary distribution, #link(url_openocd)[here]. + Follow the provided installation instructions. Then update your `%PATH%` + environment variable to include the path where the binaries were + installed. (#bin_path, if you've been using the easy install) +] else if lang == "de" [ + Es gibt kein offizielles Binär-Release von OpenOCD für Windows, aber + wenn Sie es nicht selbst kompilieren möchten, stellt das xPack-Projekt + eine Binärdistribution bereit -- zu finden + #link(url_openocd)[hier]. Befolgen Sie die dort + aufgeführten Installationsanweisungen. Aktualisieren Sie anschließend + Ihre Umgebungsvariable `%PATH%`, indem Sie den Pfad hinzufügen, unter + dem die Binärdateien installiert wurden (z. B. #bin_path, + falls Sie die einfache Installation verwendet haben). +] else { todo } + +#if lang == "en" [ + Verify that OpenOCD is in your `%PATH%` with: +] else if lang == "de" [ + Überprüfen Sie mit folgendem Befehl, ob OpenOCD in Ihrem `%PATH%` enthalten ist: +] else { todo } + +```text +$ openocd -v +Open On-Chip Debugger 0.10.0 +(..) +``` + += QEMU + +#let url_qemu = "https://www.qemu.org/download/#windows" +#if lang == "en" [ + Grab QEMU from #link(url_qemu)[the official website]. +] else if lang == "de" [ + Lade QEMU von #link(url_qemu)[der offiziellen Website] herunter. +] else { todo } + += ST-LINK USB driver + +#let url_stlink = "http://www.st.com/en/embedded-software/stsw-link009.html" +#if lang == "en" [ + You'll also need to install #link(url_stlink)[this USB driver] + or OpenOCD won't work. Follow the installer instructions and make sure + you install the right version (32-bit or 64-bit) of the driver. +] else if lang == "de" [ + Sie müssen außerdem #link(url_stlink)[diesen USB-Treiber] + installieren, da OpenOCD sonst nicht funktioniert. Befolgen Sie die + Anweisungen des Installationsprogramms und stellen Sie sicher, dass Sie + die richtige Version des Treibers (32-Bit oder 64-Bit) installieren. +] else { todo } + +#if lang == "en" [ + That's all! Go to the #link()[next section]. +] else if lang == "de" [ + Das war's! Gehen Sie zum #link()[nächsten Abschnitt]. +] else { todo } diff --git a/typ/intro/no-std.typ b/typ/intro/no-std.typ new file mode 100644 index 00000000..25232a52 --- /dev/null +++ b/typ/intro/no-std.typ @@ -0,0 +1,267 @@ +#import "../config.typ": * + +#h1(offset: whole, + if lang == "en" [A `no_std` Rust Environment] + else if lang == "de" [Eine `no_std`-Rust-Umgebung] + else if lang == "zh" [一个 `no_std` Rust环境] + else { todo }) +#set heading(offset: whole*2) + +#let ln_st72325 = link("https://www.st.com/resource/en/datasheet/st72325j6.pdf")[ST72325xx] +#let ln_rpi = link("https://en.wikipedia.org/wiki/Raspberry_Pi#Specifications")[Modell B 3+] +#if lang == "en" [ + The term Embedded Programming is used for a wide range of different + classes of programming. Ranging from programming 8-Bit MCUs (like the + #ln_st72325 with just a few KB of RAM and ROM, + up to systems like the Raspberry Pi (#ln_rpi) + which has a 32/64-bit 4-core Cortex-A53 \@ 1.4 GHz and 1GB of RAM. + Different restrictions/limitations will apply when writing code + depending on what kind of target and use case you have. +] else if lang == "de" [ + Der Begriff „Embedded Programming" (Programmierung eingebetteter + Systeme) umfasst ein breites Spektrum unterschiedlichster + Programmierbereiche. Das Spektrum reicht von der Programmierung von + 8-Bit-Mikrocontrollern (wie dem + #ln_st72325 mit nur wenigen Kilobyte RAM und ROM + bis hin zu Systemen wie dem Raspberry Pi (#ln_rpi), + der über einen 32/64-Bit-Quad-Core-Prozessor (Cortex-A53 mit 1,4 GHz) + und 1 GB RAM verfügt. Je nach Zielsystem und Anwendungsfall gelten bei + der Programmierung unterschiedliche Einschränkungen und + Rahmenbedingungen. +] else if lang == "zh" [ + 嵌入式编程这个词被广泛用于许多不同的编程场景中。小到RAM和ROM只有KB的8位机(像是#ln_st72325),大到一个具有32/64位4核Cortex-A53和1GB + RAM的系统,比如树莓派(#ln_rpi)。当编写代码时,取决于你的目标环境和用例,将会有不同的限制和局限。 +] else { todo } + +#if lang == "en" [ + There are two general Embedded Programming classifications: +] else if lang == "de" [ + Es gibt zwei grundlegende Kategorien der Programmierung eingebetteter Systeme: +] else if lang == "zh" [ + 通常嵌入式编程有两类: +] else { todo } + += #(if lang == "en" [Hosted Environments] + else if lang == "de" [Hosted Environments] + else if lang == "zh" [主机环境下] + else { todo }) + +#let ln_posix = "https://en.wikipedia.org/wiki/POSIX" +#if lang == "en" [ + These kinds of environments are close to a normal PC environment. What + this means is that you are provided with a System Interface + #link(ln_posix)[E.G. POSIX] that provides + you with primitives to interact with various systems, such as file + systems, networking, memory management, threads, etc. Standard libraries + in turn usually depend on these primitives to implement their + functionality. You may also have some sort of sysroot and restrictions + on RAM/ROM-usage, and perhaps some special HW or I/Os. Overall it feels + like coding on a special-purpose PC environment. +] else if lang == "de" [ + Derartige Umgebungen ähneln einer herkömmlichen PC-Umgebung. Das + bedeutet, dass Ihnen eine Systemschnittstelle (z. B. + #link(ln_posix)[POSIX]) zur Verfügung + steht, die Grundfunktionen (Primitive) für die Interaktion mit + verschiedenen Systemkomponenten bietet -- etwa Dateisystemen, + Netzwerkfunktionen, Speicherverwaltung, Threads usw. + Standardbibliotheken wiederum basieren in der Regel auf diesen + Grundfunktionen, um ihre Funktionalität zu implementieren. Zudem können + eine spezifische System-Root-Umgebung (sysroot) sowie Einschränkungen + bei der RAM- oder ROM-Nutzung vorliegen; auch spezielle Hardware oder + Ein-/Ausgabeschnittstellen (I/Os) können vorhanden sein. Insgesamt + vermittelt die Programmierung den Eindruck, in einer auf einen + speziellen Einsatzzweck ausgerichteten PC-Umgebung zu arbeiten. +] else if lang == "zh" [ + 这类环境与一个常见的PC环境类似。意味着向你提供了一个系统接口#link(ln_posix)[比如 POSIX],使你能和不同的系统进行交互,比如文件系统,网络,内存管理,进程,等等。标准库相应地依赖这些接口去实现了它们的功能。可能有某种sysroot并限制了对RAM/ROM的使用,可能还有一些特别的硬件或者I/O。总之感觉像是在专用的PC环境上编程一样。 +] else { todo } + += #(if lang == "en" [Bare Metal Environments] + else if lang == "de" [Rein physische Umgebung] + else if lang == "zh" [裸机环境下] + else { todo }) + +#let ln_core = link("https://doc.rust-lang.org/core/")[libcore] +#if lang == "en" [ + In a bare metal environment no code has been loaded before your program. + Without the software provided by an OS we can not load the standard + library. Instead the program, along with the crates it uses, can only + use the hardware (bare metal) to run. To prevent rust from loading the + standard library use `no_std`. The platform-agnostic parts of the + standard library are available through #ln_core. libcore also excludes + things which are not always desirable in an embedded environment. One of + these things is a memory allocator for dynamic memory allocation. If you + require this or any other functionalities there are often crates which + provide these. +] else if lang == "de" [ + In einer rein physischen Umgebung wird vor Ihrem Programm kein Code + geladen. Ohne die vom Betriebssystem bereitgestellte Software kann die + Standardbibliothek nicht geladen werden. Stattdessen kann das Programm + zusammen mit den verwendeten Crates ausschließlich die Hardware (Bare + Metal) nutzen. Um zu verhindern, dass Rust die Standardbibliothek lädt, + verwenden Sie `no_std`. Die plattformunabhängigen Teile der + Standardbibliothek sind über #ln_core verfügbar. libcore + schließt außerdem Funktionen aus, die in eingebetteten Umgebungen nicht + immer erwünscht sind. Eine dieser Funktionen ist ein Speicherallokator + für die dynamische Speicherverwaltung. Falls Sie diese oder andere + Funktionalitäten benötigen, stehen Ihnen häufig entsprechende Crates zur Verfügung. +] else if lang == "zh" [ + 在一个裸机环境中,程序被加载前,环境中不存在代码。没有系统提供的软件,我们不能加载标准库。相反地,程序和它使用的crates只能使用硬件(裸机)去运行。使用`no-std`可以防止rust读取标准库。标准库中与平台无关的部分在#link("https://doc.rust-lang.org/core/")[libcore]中。libcore剔除了那些在一个嵌入式环境中非必要的东西。比如用于动态分配的内存分配器。如果你需要这些或者其它的某些功能,通常会有提供这些功能的crates。 +] else { todo } + +== #(if lang == "en" [The libstd Runtime] + else if lang == "de" [Die libstd-Laufzeitumgebung] + else if lang == "zh" [libstd运行时] + else { todo }) + +#let ln_std = link("https://doc.rust-lang.org/std/")[libstd] +#if lang == "en" [ + As mentioned before using + #ln_std requires some sort of + system integration, but this is not only because + #ln_std is just providing a + common way of accessing OS abstractions, it also provides a runtime. + This runtime, among other things, takes care of setting up stack + overflow protection, processing command line arguments, and spawning the + main thread before a program's main function is invoked. This runtime + also won't be available in a `no_std` environment. +] else if lang == "de" [ + Wie bereits erwähnt, erfordert die Verwendung von + #ln_std eine gewisse + Systemintegration; dies liegt jedoch nicht nur daran, dass + #ln_std eine einheitliche + Schnittstelle zu Betriebssystem-Abstraktionen bereitstellt, sondern auch + daran, dass es eine Laufzeitumgebung (Runtime) mitliefert. Diese + Laufzeitumgebung kümmert sich unter anderem um die Einrichtung eines + Stack-Overflow-Schutzes, die Verarbeitung von Kommandozeilenargumenten + sowie das Starten des Haupt-Threads, bevor die `main`-Funktion des + Programms aufgerufen wird. In einer `no_std`-Umgebung steht diese + Laufzeitumgebung nicht zur Verfügung. +] else if lang == "zh" [ + 就像之前提到的,使用#link("https://doc.rust-lang.org/std/")[libstd]需要一些系统集成,这不仅仅是因为#link("https://doc.rust-lang.org/std/")[libstd]使用了一个公共的方法访问操作系统,它也提供了一个运行时环境。这个运行时环境,负责设置堆栈溢出保护,处理命令行参数,并在一个程序的主函数被激活前启动一个主线程。在一个`no_std`环境中,这个运行时环境也是不可用的。 +] else { todo } + += #(if lang == "en" [Summary] + else if lang == "de" [Zusammenfassung] + else if lang == "zh" [总结] + else { todo }) + +#let ln_core = link("https://doc.rust-lang.org/core/")[libcore] +#if lang == "en" [ + `#![no_std]` is a crate-level attribute that indicates that the crate + will link to the core-crate instead of the std-crate. The + #ln_core crate in turn is a + platform-agnostic subset of the std crate which makes no assumptions + about the system the program will run on. As such, it provides APIs for + language primitives like floats, strings and slices, as well as APIs + that expose processor features like atomic operations and SIMD + instructions. However it lacks APIs for anything that involves platform + integration. Because of these properties no_std and + #ln_core code can be used for + any kind of bootstrapping (stage 0) code like bootloaders, firmware or kernels. +] else if lang == "de" [ + `#![no_std]` ist ein Attribut auf Crate-Ebene, das angibt, dass das + Crate gegen das `core`-Crate anstatt gegen das `std`-Crate gelinkt wird. + Das #ln_core;-Crate wiederum ist + eine plattformunabhängige Teilmenge des `std`-Crates, die keinerlei + Annahmen über das System trifft, auf dem das Programm ausgeführt wird. + Dementsprechend stellt es APIs für Sprachprimitive wie Gleitkommazahlen, + Strings und Slices bereit sowie APIs, die Prozessorfunktionen wie + atomare Operationen und SIMD-Befehle zugänglich machen. Es fehlen jedoch + APIs für Funktionen, die eine Plattformintegration erfordern. Aufgrund + dieser Eigenschaften eignen sich `no_std`- und + #ln_core;-Code für jegliche Art + von Bootstrapping-Code (Stufe 0), wie etwa Bootloader, Firmware oder Kernel. +] else if lang == "zh" [ + `#![no_std]`是一个crate层级的属性,它说明crate将连接至core-crate而不是std-crate。#ln_core crate是std + crate的一个的子集,其与平台无关,它对程序将要运行的系统没有做要求。比如,它提供了像是floats,strings和切片的APIs,暴露了像是与原子操作和SIMD指令相关的处理器功能的APIs。然而,它缺少涉及到平台集成的那些APIs。由于这些特性,no_std和#ln_core;代码可以用于任何引导程序(stage + 0)像是bootloaders,固件或者内核。 +] else { todo } + +== #(if lang == "en" [Overview] + else if lang == "de" [Überblick] + else if lang == "zh" [概述] + else { todo }) + +#figure( + kind: table, + table( + columns: (1fr, 11.11%, 6.94%), + align: center, + table.header( + if lang == "en" [feature] + else if lang == "de" [Eigenschaft] + else if lang == "zh" [特性] + else { todo }, + [no_std], + [std], + ), + if lang == "en" [heap (dynamic memory)] + else if lang == "de" [heap (dynamischer Speicher)] + else if lang == "zh" [堆 (dynamic memory)] + else { todo }, + [\*], [✓], + if lang == "en" [collections (Vec, BTreeMap, etc)] + else if lang == "de" [collections (Vec, BTreeMap, usw.)] + else if lang == "zh" [容器 (Vec, BTreeMap, etc)] + else { todo }, + [\*\*], [✓], + if lang == "en" [stack overflow protection] + else if lang == "de" [Schutz vor Stack-Überlauf] + else if lang == "zh" [栈溢出保护] + else { todo }, + [✘], [✓], + if lang == "en" [runs init code before main] + else if lang == "de" [führt Initialisierungscode vor der main-Funktion aus] + else if lang == "zh" [在进入main之前运行的初始化代码] + else { todo }, + [✘], [✓], + if lang in ("en", "zh") [libstd available] + else if lang == "de" [libstd verfügbar] + else { todo }, + [✘], [✓], + if lang in ("en", "zh") [libcore available] + else if lang == "de" [libcore verfügbar] + else { todo }, + [✓], [✓], + if lang == "en" [writing firmware, kernel, or bootloader code] + else if lang == "de" [schreibt firmware, kernel, oder bootloader code] + else if lang == "zh" [编写固件,内核,或者引导程序] + else { todo }, + [✓], [✘], + ) +) + +#let ln_cortex = link("https://github.com/rust-embedded/alloc-cortex-m")[alloc-cortex-m] +#if lang == "en" [ + \* Only if you use the `alloc` crate and use a suitable allocator like #ln_cortex. +] else if lang == "de" [ + \* Nur wenn Sie das `alloc`-Crate und einen geeigneten Allocator wie #ln_cortex verwenden. +] else if lang == "zh" [ + \* 只有在你使用了 `alloc` crate + 并设置了一个适合的分配器后,比如#ln_cortex;后可用. +] else { todo } + +#if lang == "en" [ + \*\* Only if you use the `collections` crate and configure a global default allocator. +] else if lang == "de" [ + \*\* Nur wenn Sie das `collections`-Crate verwenden und einen globalen Standard-Allocator konfigurieren. +] else if lang == "zh" [ + \*\* 只有在你使用了 `collections` crate + 并配置了一个全局默认的分配器后可用. +] else { todo } + +#if lang == "en" [ + \*\* HashMap and HashSet are not available due to a lack of a secure random number generator. +] else if lang == "de" [ + \*\* HashMap und HashSet stehen aufgrund des Fehlens eines sicheren + Zufallszahlengenerators nicht zur Verfügung. +] else if lang == "zh" [ + \*\* 由于缺少安全的随机数产生器,所以无法使用HashMap和HashSet. +] else { todo } + += #(if lang == "en" [See Also] + else if lang == "de" [Siehe auch] + else if lang == "zh" [参见] + else { todo }) + +- #link("https://github.com/rust-lang/rfcs/blob/master/text/1184-stabilize-no_std.html")[RFC-1184] diff --git a/typ/intro/tooling.typ b/typ/intro/tooling.typ new file mode 100644 index 00000000..906dd862 --- /dev/null +++ b/typ/intro/tooling.typ @@ -0,0 +1,501 @@ +#import "../config.typ": * + +#h1(offset: whole, + if lang == "en" [Tooling] + else if lang == "de" [Werkzeuge] + else if lang == "zh" [工具] + else { todo }) +#set heading(offset: whole*2) + +#if lang == "en" [ + Dealing with microcontrollers involves using several different tools as + we'll be dealing with an architecture different than your laptop's and + we'll have to run and debug programs on a _remote_ device. +] else if lang == "de" [ + Der Umgang mit Mikrocontrollern erfordert den Einsatz verschiedener + Werkzeuge, da wir es mit einer Architektur zu tun haben, die sich von + der Ihres Laptops unterscheidet, und Programme auf einem + _entfernten_ Gerät ausführen sowie debuggen müssen. +] else if lang == "zh" [ + 与微控制器打交道需要使用几种不同的工具,因为我们要处理的架构与笔记本电脑不同,我们必须在 _远程_ +] else { todo } + +#{ +let ln_binutils = link("https://github.com/rust-embedded/cargo-binutils")[`cargo-binutils`] +let ln_qemu = link("https://www.qemu.org/")[`qemu-system-arm`] +let ln_generate = link("https://github.com/ashleygwilliams/cargo-generate")[`cargo-generate`] +if lang == "en" [ + We'll use all the tools listed below. Any recent version should work + when a minimum version is not specified, but we have listed the versions + we have tested. + - Rust 1.31, 1.31-beta, or a newer toolchain PLUS ARM Cortex-M + compilation support. + - #ln_binutils \~0.1.4 + - #ln_qemu. Tested versions: 3.0.0 + - OpenOCD \>=0.8. Tested versions: v0.9.0 and v0.10.0 + - GDB with ARM support. Version 7.12 or newer highly recommended. + Tested versions: 7.10, 7.11, 7.12 and 8.1 + - #ln_generate or `git`. + These tools are optional but will make it easier to follow along with the book. +] else if lang == "de" [ + Wir werden alle unten aufgeführten Werkzeuge verwenden. Sofern keine + Mindestversion angegeben ist, sollte jede aktuelle Version + funktionieren; wir haben jedoch die Versionen aufgelistet, die wir + getestet haben. + - Rust 1.31, 1.31-beta oder neuer, SOWIE Unterstützung für die + Kompilierung für ARM Cortex-M. + - #ln_binutils \~0.1.4 + - #ln_qemu. Getestete Version: 3.0.0 + - OpenOCD \>=0.8. Getestete Versionen: v0.9.0 und v0.10.0 + - GDB mit ARM-Unterstützung. Version 7.12 oder neuer wird dringend + empfohlen. Getestete Versionen: 7.10, 7.11, 7.12 und 8.1. + - #ln_generate oder `git`. + Diese Werkzeuge sind optional, erleichtern es jedoch, dem Buch zu folgen. +] else if lang == "zh" [ + 设备上运行和调试程序。我们将使用下面列举出来的工具。当没有指定一个最小版本时,最新的版本应该也可以用,但是我们还是列出了我们已经测过的那些版本。 + - Rust 1.31, 1.31-beta, 或者一个更新的,支持ARM Cortex-M编译的工具链。 + - #ln_binutils \~0.1.4 + - #ln_qemu. 测试的版本: 3.0.0 + - OpenOCD \>=0.8. 测试的版本: v0.9.0 and v0.10.0 + - 有ARM支持的GDB。强烈建议7.12或者更新的版本。测试版本: 7.10, 7.11 和 8.1 + - #ln_generate 或者 `git`。 + 这些工具都是可选的,但是跟着这本书来使用它们,会更容易。 +] else { todo } +} + +#if lang == "en" [ + The text below explains why we are using these tools. Installation + instructions can be found on the next page. +] else if lang == "de" [ + Der folgende Text erläutert, warum wir diese Werkzeuge verwenden. + Installationsanweisungen finden Sie auf der nächsten Seite. +] else if lang == "zh" [ + 下面的文档将解释我们为什么使用这些工具。安装指令可以在下一页找到。 +] else { todo } + += #(if lang == "en" [`cargo-generate` OR `git`] + else if lang == "de" [`cargo-generate` ODER `git`] + else if lang == "zh" [`cargo-generate` 或者 `git`] + else { todo }) + +#if lang == "en" [ + Bare metal programs are non-standard (`no_std`) Rust programs that + require some adjustments to the linking process in order to get the + memory layout of the program right. This requires some additional files + (like linker scripts) and settings (like linker flags). We have packaged + those for you in a template such that you only need to fill in the + missing information (such as the project name and the characteristics of + your target hardware). +] else if lang == "de" [ + „Bare-Metal"-Programme sind Rust-Programme ohne Standardbibliothek + (`no_std`), die Anpassungen am Linker-Vorgang erfordern, um das korrekte + Speicherlayout des Programms zu gewährleisten. Dies macht zusätzliche + Dateien (wie Linker-Skripte) und Einstellungen (wie Linker-Flags) + erforderlich. Wir haben diese Elemente in einer Vorlage zusammengefasst, + sodass Sie lediglich die noch fehlenden Angaben ergänzen müssen (etwa + den Projektnamen und die Spezifikationen Ihrer Zielhardware). +] else if lang == "zh" [ + 裸机编程是非标准Rust编程,为了得到正确的程序的内存布局,需要对链接过程进行一些调整,这要求添加一些额外的文件(比如linker + scripts)和配置(比如linker + flags)。我们已经为你把这些打包进了一个模板里了,你只需要补充缺失的信息(比如项目名和目标硬件的特性)。 +] else { todo } + +#if lang == "en" [ + Our template is compatible with `cargo-generate`: a Cargo subcommand for + creating new Cargo projects from templates. You can also download the + template using `git`, `curl`, `wget`, or your web browser. +] else if lang == "de" [ + Unsere Vorlage ist mit `cargo-generate` kompatibel -- einem + Cargo-Unterbefehl zum Erstellen neuer Cargo-Projekte auf Basis von + Vorlagen. Alternativ können Sie die Vorlage auch mithilfe von `git`, + `curl`, `wget` oder Ihrem Webbrowser herunterladen. +] else if lang == "zh" [ + 我们的模板兼容`cargo-generate`:一个用来从模板生成新的Cargo项目的Cargo子命令。你也能使用`git`,`curl`,`wget`,或者你的网页浏览器下载模板。 +] else { todo } + += `cargo-binutils` + +#if lang == "en" [ + `cargo-binutils` is a collection of Cargo subcommands that make it easy + to use the LLVM tools that are shipped with the Rust toolchain. These + tools include the LLVM versions of `objdump`, `nm` and `size` and are + used for inspecting binaries. +] else if lang == "de" [ + `cargo-binutils` ist eine Sammlung von Cargo-Unterbefehlen, die die + Verwendung der mit der Rust-Toolchain ausgelieferten LLVM-Werkzeuge + vereinfachen. Zu diesen Werkzeugen gehören die LLVM-Varianten von + `objdump`, `nm` und `size`, die zur Untersuchung von Binärdateien dienen. +] else if lang == "zh" [ + `cargo-binutils`是一个Cargo命令的子集,它让我们能轻松使用Rust工具链带来的LLVM工具。这些工具包括LLVM版本的`objdump`,`nm`和`size`,用来查看二进制文件。 + 在GNU +] else { todo } + +#{ +let cmd = `rustup component add llvm-tools` +if lang == "en" [ + The advantage of using these tools over GNU binutils is that (a) + installing the LLVM tools is the same one-command installation + (#cmd) regardless of your OS and (b) tools + like `objdump` support all the architectures that `rustc` supports -- + from ARM to x86_64 -- because they both share the same LLVM backend. +] else if lang == "de" [ + Der Vorteil dieser Werkzeuge gegenüber den GNU-Binutils liegt darin, + dass (a) die Installation der LLVM-Werkzeuge unabhängig vom + Betriebssystem stets mit demselben einzelnen Befehl + (#cmd) erfolgt und (b) Werkzeuge wie + `objdump` alle von `rustc` unterstützten Architekturen abdecken -- von + ARM bis x86_64 --, da sie auf demselben LLVM-Backend basieren. +] else if lang == "zh" [ + binutils之上使用这些工具的好处是,(a)无论你的操作系统是什么,安装这些LLVM工具都可以用同一条命令(#cmd)。(b)像是`objdump`这样的工具,支持所有`rustc`支持的架构--从ARM到x86\_64--因为它们都有一样的LLVM后端。 +] else { todo } +} + += `qemu-system-arm` + +#if lang == "en" [ + QEMU is an emulator. In this case we use the variant that can fully + emulate ARM systems. We use QEMU to run embedded programs on the host. + Thanks to this you can follow some parts of this book even if you don't + have any hardware with you! +] else if lang == "de" [ + QEMU ist ein Emulator. In diesem Fall verwenden wir die Variante, die + ARM-Systeme vollständig emulieren kann. Wir nutzen QEMU, um + Embedded-Programme auf dem Host-System auszuführen. Auf diese Weise + können Sie einige Teile dieses Buches auch dann nachvollziehen, wenn Sie + keine Hardware zur Hand haben! +] else if lang == "zh" [ + QEMU是一个仿真器。在这个例子里,我们使用能完全仿真ARM系统的改良版QEMU。我们使用QEMU在主机上运行嵌入式程序。多亏了它,你可以在没有任何硬件的情况下,尝试这本书的部分示例。 +] else { todo } + +#h1(offset: whole, + if lang == "en" [Tooling for Embedded Rust Debugging] + else if lang == "de" [Werkzeuge für das Debugging von Embedded Rust] + else if lang == "zh" [用于调试嵌入式Rust的工具] + else { todo }) + += #(if lang == "en" [Overview] + else if lang == "de" [Überblick] + else if lang == "zh" [概述] + else { todo }) + +#if lang == "en" [ + Debugging embedded systems in Rust requires specialized tools including + software to manage the debugging process, debuggers to inspect and + control program execution, and hardware probes to facilitate interaction + between the host and the embedded device. This document outlines + essential software tools like Probe-rs and OpenOCD, which simplify and + support the debugging process, alongside prominent debuggers such as GDB + and the Probe-rs Visual Studio Code extension. Additionally, it covers + key hardware probes such as Rusty-probe, ST-Link, J-Link, and MCU-Link, + which are integral for effective debugging and programming of embedded devices. +] else if lang == "de" [ + Das Debugging eingebetteter Systeme in Rust erfordert spezialisierte + Werkzeuge, darunter Software zur Steuerung des Debugging-Prozesses, + Debugger zur Überwachung und Kontrolle der Programmausführung sowie + Hardware-Probes, die die Interaktion zwischen dem Host-System und dem + eingebetteten Gerät ermöglichen. Dieses Dokument stellt wesentliche + Software-Werkzeuge wie Probe-rs und OpenOCD vor, die den + Debugging-Prozess vereinfachen und unterstützen, sowie bekannte Debugger + wie GDB und die Probe-rs-Erweiterung für Visual Studio Code. Zudem + werden wichtige Hardware-Probes wie Rusty-probe, ST-Link, J-Link und + MCU-Link behandelt, die für das effiziente Debugging und Programmieren + eingebetteter Geräte unerlässlich sind. +] else if lang == "zh" [ + 在Rust中调试嵌入式系统需要用到专业的工具,这包括用于管理调试进程的软件,用于观察和控制程序执行的调试器,和用于便捷主机和嵌入式设备之间进行交互的硬件探测器.这个文档会介绍像是Probe-rs和OpenOCD这样的基础软件,以及像是GDB和Probe-rs + Visual Studio + Code扩展这样常见的调试器.另外,该文档会覆盖像是Rusty-probe,ST-Link,J-Link,和MCU-Link这样的硬件探测器,它们整合在一起可以高效地对嵌入式设备进行调试和编程. +] else { todo } + += #(if lang == "en" [Software that drives debugging tools] + else if lang == "de" [Software, die Debugging-Werkzeuge steuert] + else if lang == "zh" [驱动调试工具的软件] + else { todo }) + +== Probe-rs + +#if lang == "en" [ + Probe-rs is a modern, Rust-focused software designed to work with + debuggers in embedded systems. Unlike OpenOCD, Probe-rs is built with + simplicity in mind and aims to reduce the configuration burden often + found in other debugging solutions. It supports various probes and + targets, providing a high-level interface for interacting with embedded + hardware. Probe-rs integrates directly with Rust tooling, and integrates + with Visual Studio Code through its extension, allowing developers to + streamline their debugging workflow. +] else if lang == "de" [ + Probe-rs ist eine moderne, auf Rust ausgerichtete Software für das + Debugging in Embedded-Systemen. Im Gegensatz zu OpenOCD wurde Probe-rs + mit dem Fokus auf Einfachheit entwickelt und zielt darauf ab, den bei + anderen Debugging-Lösungen oft hohen Konfigurationsaufwand zu + reduzieren. Es unterstützt diverse Debug-Probes sowie Zielsysteme und + bietet eine komfortable Schnittstelle für die Interaktion mit + Embedded-Hardware. Probe-rs lässt sich direkt in die Rust-Toolchain + sowie -- dank einer entsprechenden Erweiterung -- in Visual Studio Code + integrieren, wodurch Entwickler ihren Debugging-Workflow optimieren können. +] else if lang == "zh" [ + Probe-rs是一个现代化的,以Rust开发的软件,被设计用来配合嵌入式系统中的调试器一起工作.不像OpenOCD,Probe-rs设计的时候就考虑到了简单性,目标是减少在其它调试解决方案中常见的配置重担. + 它支持不同的探测器和目标架构,提供一个用于与嵌入式硬件交互的高层接口.Probe-rs直接集成了Rust工具链,并且通过扩展集成进了Visual + Studio Code中,允许开发者精简它们的调试工作流程. +] else { todo } + +== OpenOCD (Open On-Chip Debugger) + +#if lang == "en" [ + OpenOCD is an open-source software tool used for debugging, testing, and + programming embedded systems. It provides an interface between the host + system and embedded hardware, supporting various transport layers like + JTAG and SWD (Serial Wire Debug). OpenOCD integrates with GDB, which is + a debugger. OpenOCD is widely supported, with extensive documentation + and a large community, but may require complex configuration, especially + for custom embedded setups. +] else if lang == "de" [ + OpenOCD ist ein Open-Source-Softwaretool für das Debugging, Testen und + Programmieren eingebetteter Systeme. Es stellt eine Schnittstelle + zwischen dem Host-System und der eingebetteten Hardware bereit und + unterstützt verschiedene Übertragungsprotokolle wie JTAG und SWD (Serial + Wire Debug). OpenOCD lässt sich in GDB, einen Debugger, integrieren. Das + Werkzeug ist weit verbreitet, verfügt über eine umfangreiche + Dokumentation sowie eine große Community, kann jedoch -- insbesondere + bei kundenspezifischen Embedded-Konfigurationen -- eine komplexe + Einrichtung erfordern. +] else if lang == "zh" [ + OpenOCD是一个用于调试,测试,和编程嵌入式系统的开源软件工具.它提供了一个主机系统和嵌入式硬件之间的接口,支持不同的传输层,比如JTAG和SWD(Serial + Wire + Debug).OpenOCD集成了GDB,其是一个调试器.OpenOCD受到了广泛的支持,拥有大量的文档和一个庞大的社区,但是配置可能会很复杂,特别是对于自定义的嵌入式设置. +] else { todo } + += #(if lang in ("en", "zh") [Debuggers] + else if lang == "de" [Debugger] + else { todo }) + +#if lang == "en" [ + A debugger allows developers to inspect and control the execution of a + program in order to identify and correct errors or bugs. It provides + functionalities such as setting breakpoints, stepping through code line + by line, and examining the values of variables and memory states. + Debuggers are essential for thorough software development and + maintenance, enabling developers to ensure that their code behaves as + intended under various conditions. +] else if lang == "de" [ + Ein Debugger ermöglicht es Entwicklern, die Ausführung eines Programms + zu untersuchen und zu steuern, um Fehler oder Bugs zu identifizieren und + zu beheben. Er bietet Funktionen wie das Setzen von Haltepunkten + (Breakpoints), das schrittweise Durchlaufen des Codes sowie die + Überprüfung von Variablenwerten und Speicherzuständen. Debugger sind für + eine gründliche Softwareentwicklung und -wartung unerlässlich, da sie + Entwicklern helfen sicherzustellen, dass sich ihr Code unter + verschiedenen Bedingungen wie vorgesehen verhält. +] else if lang == "zh" [ + 调试器允许开发者观察和控制一个程序的执行,以辨别和纠正错误或者bugs.它提供像是设置断点,一行一行地步进代码,和研究变量的值以及内存的状态等功能.调试器本质上是为了通过软件开发和维护,使得开发者可以确保他们的代码的行为在不同环境下就像他们预期的那样运行. +] else { todo } + +#if lang == "en" [ + Debuggers know how to: + - Interact with the memory mapped registers. + - Set Breakpoints/Watchpoints. + - Read and write to the memory mapped registers. + - Detect when the MCU has been halted for a debug event. + - Continue MCU execution after a debug event has been encountered. + - Erase and write to the microcontroller's FLASH. +] else if lang == "de" [ + Debugger bieten folgende Möglichkeiten: + - Auf die im Speicher abgebildeten Register zugreifen. + - Haltepunkte/Überwachungspunkte setzen. + - In/aus im Speicher abgebildeten Registern lesen und schreiben. + - Erkennen, wenn die MCU aufgrund eines Debug-Ereignisses angehalten wurde. + - Die MCU-Ausführung nach dem Auftreten eines Debug-Ereignisses fortsetzen. + - Den Flash-Speicher des Mikrocontrollers löschen und beschreiben. +] else if lang == "zh" [ + 调试器可以知道如何: + - 与映射到存储上的寄存器交互. + - 设置断点. + - 读取和写入映射到存储上的寄存器. + - 检测什么时候MCU因为一个调试时间被挂了起来. + - 在遇到一个调试事件后继续MCU的执行. + - 擦出和写入微控制器的FLASH. +] else { todo } + +== Probe-rs Visual Studio Code Extension + +#if lang == "en" [ + Probe-rs has a Visual Studio Code extension, providing a seamless + debugging experience without extensive setup. Through this connection, + developers can use Rust-specific features like pretty printing and + detailed error messages, ensuring that their debugging process aligns + with the Rust ecosystem. +] else if lang == "de" [ + Probe-rs bietet eine Visual Studio Code-Erweiterung, die ein nahtloses + Debugging-Erlebnis ohne aufwendige Einrichtung ermöglicht. Dank dieser + Integration können Entwickler Rust-spezifische Funktionen wie + Pretty-Printing und detaillierte Fehlermeldungen nutzen und so + sicherstellen, dass ihr Debugging-Prozess optimal auf das Rust-Ökosystem + abgestimmt ist. +] else if lang == "zh" [ + Probe-rs有一个Visual Studio + Code的扩展,提供了不需要额外设置的无缝的调试体验.通过它的帮助,开发者可以使用Rust特定的特性,像是漂亮的打印和详细的错误信息,确保它们的调试过程可以与Rust的生态对齐. +] else { todo } + +== TRACE32 + +#if lang == "en" [ + TRACE32 is a professional debugging and tracing solution developed by + Lauterbach for embedded systems. It supports a wide range of processor + architectures, including ARM and RISC-V, and connects to target hardware + via JTAG, SWD, and various trace interfaces. TRACE32 provides advanced + debugging capabilities such as multicore debugging, complex breakpoints, + and real-time trace analysis. It works with standard ELF/DWARF debug + information, making it compatible with Rust binaries built using + conventional toolchains. +] else if lang == "de" [ + TRACE32 ist eine von Lauterbach für Embedded-Systeme entwickelte + professionelle Debugging- und Tracing-Lösung. Sie unterstützt eine + Vielzahl von Prozessorarchitekturen, darunter ARM und RISC-V, und stellt + die Verbindung zur Zielhardware über JTAG, SWD sowie diverse + Trace-Schnittstellen her. TRACE32 bietet leistungsstarke + Debugging-Funktionen wie Multicore-Debugging, komplexe Breakpoints und + Echtzeit-Trace-Analyse. Da das System auf + Standard-ELF/DWARF-Debuginformationen basiert, ist es mit + Rust-Binärdateien kompatibel, die mit herkömmlichen Werkzeugen erstellt wurden. +] else { todo } + +== GDB (GNU Debugger) + +#if lang == "en" [ + GDB is a versatile debugging tool that allows developers to examine the + state of programs while they run or after they crash. For embedded Rust, + GDB connects to the target system via OpenOCD or other debugging servers + to interact with the embedded code. GDB is highly configurable and + supports features like remote debugging, variable inspection, and + conditional breakpoints. It can be used on a variety of platforms, and + has extensive support for Rust-specific debugging needs, such as pretty + printing and integration with IDEs. +] else if lang == "de" [ + GDB ist ein vielseitiges Debugging-Werkzeug, das es Entwicklern + ermöglicht, den Zustand von Programmen während der Laufzeit oder nach + einem Absturz zu untersuchen. Im Bereich Embedded Rust verbindet sich + GDB über OpenOCD oder andere Debugging-Server mit dem Zielsystem, um mit + dem Embedded-Code zu interagieren. GDB ist hochgradig konfigurierbar und + unterstützt Funktionen wie Remote-Debugging, Variableninspektion und + bedingte Haltepunkte. Es ist auf einer Vielzahl von Plattformen + einsetzbar und bietet umfassende Unterstützung für Rust-spezifische + Debugging-Anforderungen, wie etwa Pretty-Printing und die Integration in IDEs. +] else if lang == "zh" [ + GDB是一个多用途的调试工具,其允许开发者研究程序的状态,无论其正在运行中还是程序崩溃后.对于嵌入式Rust,GDB通过OpenOCD或者其它的调试服务器链接到目标系统上去和嵌入式代码交互.GDB是高度可配置的,并且支持像是远程调试,变量检测,和条件断点.它可以被用于多个平台,并对Rust特定的调试需求有广泛的支持,比如好看的打印和与IDEs集成. +] else { todo } + += #(if lang in ("en", "de") [Probes] + else if lang == "zh" [探测器] + else { todo }) + +#if lang == "en" [ + A hardware probe is a device used in the development and debugging of + embedded systems to facilitate communication between a host computer and + the target embedded device. It typically supports protocols like JTAG or + SWD, enabling it to program, debug, and analyze the microcontroller or + microprocessor on the embedded system. Hardware probes are crucial for + developers to set breakpoints, step through code, and inspect memory and + processor registers, effectively allowing them to diagnose and fix + issues in real-time. +] else if lang == "de" [ + Eine Hardware-Sonde ist ein Gerät, das bei der Entwicklung und dem + Debugging eingebetteter Systeme eingesetzt wird, um die Kommunikation + zwischen einem Host-Computer und dem eingebetteten Zielsystem zu + ermöglichen. Sie unterstützt typischerweise Protokolle wie JTAG oder SWD + und ermöglicht so das Programmieren, Debuggen und Analysieren des + Mikrocontrollers oder Mikroprozessors auf dem eingebetteten System. + Hardware-Sonden sind für Entwickler unverzichtbar, um Haltepunkte + (Breakpoints) zu setzen, den Code schrittweise auszuführen sowie + Speicher und Prozessorregister zu untersuchen; dies erlaubt es ihnen, + Probleme effizient in Echtzeit zu diagnostizieren und zu beheben. +] else if lang == "zh" [ + 硬件探头是一个被用于嵌入式系统的开发和调试的设备,其可以使得主机和目标嵌入式设备间的通信变得简单.它通常支持像是JTAG或者SWD这样的协议,可以编程,调试和分析嵌入式系统上的微控制器或者微处理器.硬件探头对于要设置断点,步进代码,和观察内存与处理器的寄存器的开发者来说很重要,可以让开发者们高效地实时地分析和修复问题. +] else { todo } + +== Rusty-probe + +#if lang == "en" [ + Rusty-probe is an open-sourced USB-based hardware debugging probe + designed to work with probe-rs. The combination of Rusty-Probe and + probe-rs provides an easy-to-use, cost-effective solution for developers + working with embedded Rust applications. +] else if lang == "de" [ + Rusty-probe ist ein Open-Source-Hardware-Debugging-Interface auf + USB-Basis, das für den Einsatz mit probe-rs entwickelt wurde. Die + Kombination aus Rusty-Probe und probe-rs bietet Entwicklern, die mit + Embedded-Rust-Anwendungen arbeiten, eine benutzerfreundliche und + kostengünstige Lösung. +] else if lang == "zh" [ + Rusty-probe是一个开源的基于USB的硬件调试探测器,被设计用来辅助probe-rs一起工作.Rusy-Probe和probe-rs的结合为嵌入式Rust应用的开发者提供了一个易用的,成本高效的解决方案. +] else { todo } + +== ST-Link + +#if lang == "en" [ + The ST-Link is a popular debugging and programming probe developed by + STMicroelectronics primarily for their STM32 and STM8 microcontroller + series. It supports both debugging and programming via JTAG or SWD + (Serial Wire Debug) interfaces. ST-Link is widely used due to its direct + support from STMicroelectronics' extensive range of development boards + and its integration into major IDEs, making it a convenient choice for + developers working with STM microcontrollers. +] else if lang == "de" [ + Der ST-Link ist ein weit verbreiteter Debugging- und Programmieradapter, + der von STMicroelectronics primär für die Mikrocontroller-Serien STM32 + und STM8 entwickelt wurde. Er unterstützt sowohl das Debugging als auch + die Programmierung über JTAG- oder SWD-Schnittstellen (Serial Wire + Debug). Dank der direkten Unterstützung durch das umfangreiche Angebot + an Entwicklungsboards von STMicroelectronics sowie der Integration in + gängige Entwicklungsumgebungen (IDEs) ist der ST-Link weit verbreitet + und stellt eine komfortable Wahl für Entwickler dar, die mit + STM-Mikrocontrollern arbeiten. +] else if lang == "zh" [ + ST-Link是一个由STMicroelectronics开发的常见的调试和编程探测器,其主要用于它们的STM32和STM8微控制器系列.它支持通过JTAG或者SWD接口进行调试和编程.因为STMicroelectronics的大量的开发板对其直接支持并且它集成进了主流的IDEs中,所以使得它成为使用STM微控制器的开发者的首选. +] else { todo } + +== J-Link + +#if lang == "en" [ + J-Link, developed by SEGGER Microcontroller, is a robust and versatile + debugger supporting a wide range of CPU cores and devices beyond just + ARM, such as RISC-V. Known for its high performance and reliability, + J-Link supports various communication interfaces, including JTAG, SWD, + and fine-pitch JTAG interfaces. It is favored for its advanced features + like unlimited breakpoints in flash memory and its compatibility with a + multitude of development environments. +] else if lang == "de" [ + J-Link, entwickelt von SEGGER Microcontroller, ist ein robuster und + vielseitiger Debugger, der neben ARM auch eine breite Palette weiterer + CPU-Kerne und Bausteine -- wie etwa RISC-V -- unterstützt. J-Link ist + für seine hohe Leistungsfähigkeit und Zuverlässigkeit bekannt und + unterstützt diverse Kommunikationsschnittstellen, darunter JTAG, SWD + sowie Fine-Pitch-JTAG-Schnittstellen. Geschätzt wird das System zudem + für seine erweiterten Funktionen, wie etwa unbegrenzte Breakpoints im + Flash-Speicher, sowie für seine Kompatibilität mit einer Vielzahl von + Entwicklungsumgebungen. +] else if lang == "zh" [ + J-Link是由SEGGER微控制器开发的,它是一个鲁棒和功能丰富的调试器,其支持大量的CPU内核和设备,不仅仅是ARM,比如RISC-V.因其高性能和可读性而闻名,J-Link支持不同的通信接口,包括JTAG,SWD,和fine-pitch + JTAG接口.它因其高级的特性而受到欢迎,比如在flash存储中的无限的断点和它与多种开发环境的兼容性. +] else { todo } + +== MCU-Link + +#if lang == "en" [ + MCU-Link is a debugging probe that also functions as a programmer, + provided by NXP Semiconductors. It supports a variety of ARM Cortex + microcontrollers and interfaces seamlessly with development tools like + MCUXpresso IDE. MCU-Link is particularly notable for its versatility and + affordability, making it an accessible option for hobbyists, educators, + and professional developers alike. +] else if lang == "de" [ + MCU-Link ist ein von NXP Semiconductors bereitgestellter + Debugging-Adapter, der auch als Programmiergerät fungiert. Er + unterstützt eine Vielzahl von ARM-Cortex-Mikrocontrollern und lässt sich + nahtlos in Entwicklungsumgebungen wie die MCUXpresso IDE integrieren. + MCU-Link zeichnet sich besonders durch seine Vielseitigkeit und + Kosteneffizienz aus und ist damit eine attraktive Lösung für + Hobbyanwender, Lehrkräfte und professionelle Entwickler gleichermaßen. +] else if lang == "zh" [ + MCU-Link是一个调试探测器,也可以作为编程器使用,由NXP + Semiconductors提供.它支持不同的ARM Cortex微控制器且可以与像是MCUXpresso + IDE这样的开发工具进行无缝地交互.MCU-Link因其丰富的功能和易使用而闻名,使它成为像是爱好者,教育者,和专业的开发者们的可行的选项. +] else { todo } diff --git a/typ/mdbook/assets/ayu-highlight-3fdfc3ac.css b/typ/mdbook/assets/ayu-highlight-3fdfc3ac.css new file mode 100644 index 00000000..dba94b09 --- /dev/null +++ b/typ/mdbook/assets/ayu-highlight-3fdfc3ac.css @@ -0,0 +1,77 @@ +/* +Based off of the Ayu theme +Original by Dempfi (https://github.com/dempfi/ayu) +*/ + +.hljs { + display: block; + overflow-x: auto; + background: #191f26; + color: #e6e1cf; +} + +.hljs-comment, +.hljs-quote { + color: #5c6773; +} + +.hljs-variable, +.hljs-template-variable, +.hljs-attribute, +.hljs-attr, +.hljs-regexp, +.hljs-link, +.hljs-selector-id, +.hljs-selector-class { + color: #ff7733; +} + +.hljs-number, +.hljs-meta, +.hljs-builtin-name, +.hljs-literal, +.hljs-type, +.hljs-params { + color: #ffee99; +} + +.hljs-string, +.hljs-bullet { + color: #b8cc52; +} + +.hljs-title, +.hljs-built_in, +.hljs-section { + color: #ffb454; +} + +.hljs-keyword, +.hljs-selector-tag, +.hljs-symbol { + color: #ff7733; +} + +.hljs-name { + color: #36a3d9; +} + +.hljs-tag { + color: #00568d; +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-strong { + font-weight: bold; +} + +.hljs-addition { + color: #91b362; +} + +.hljs-deletion { + color: #d96c75; +} diff --git a/typ/mdbook/assets/book-609e4cb8.js b/typ/mdbook/assets/book-609e4cb8.js new file mode 100644 index 00000000..c62124cb --- /dev/null +++ b/typ/mdbook/assets/book-609e4cb8.js @@ -0,0 +1,870 @@ +'use strict'; + +/* global default_theme, default_dark_theme, default_light_theme, hljs, ClipboardJS */ + +// Fix back button cache problem +window.onunload = function() { }; + +// Global variable, shared between modules +function playground_text(playground, hidden = true) { + const code_block = playground.querySelector('code'); + + if (window.ace && code_block.classList.contains('editable')) { + const editor = window.ace.edit(code_block); + return editor.getValue(); + } else if (hidden) { + return code_block.textContent; + } else { + return code_block.innerText; + } +} + +/** + * Helper for global keypress handlers so they don't trigger when certain elements are active. + * @returns {boolean} True if the keypress handler should be skipped. + */ +function mdbook_something_else_has_focus(e) { + // Check composedPath in case the event happened from something generated + // from the shadowDOM. + const target = e.composedPath()[0] || e.target; + // If this is the `checkbox-img` input which has the focus, we want to handle it here. + if (target.classList.contains('checkbox-img')) { + return false; + } + return /^(?:input|select|textarea)$/i.test(target.nodeName); +} + +(function codeSnippets() { + function fetch_with_timeout(url, options, timeout = 6000) { + return Promise.race([ + fetch(url, options), + new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeout)), + ]); + } + + const playgrounds = Array.from(document.querySelectorAll('.playground')); + if (playgrounds.length > 0) { + fetch_with_timeout('https://play.rust-lang.org/meta/crates', { + headers: { + 'Content-Type': 'application/json', + }, + method: 'POST', + mode: 'cors', + }) + .then(response => response.json()) + .then(response => { + // get list of crates available in the rust playground + const playground_crates = response.crates.map(item => item['id']); + playgrounds.forEach(block => handle_crate_list_update(block, playground_crates)); + }); + } + + function handle_crate_list_update(playground_block, playground_crates) { + // update the play buttons after receiving the response + update_play_button(playground_block, playground_crates); + + // and install on change listener to dynamically update ACE editors + if (window.ace) { + const code_block = playground_block.querySelector('code'); + if (code_block.classList.contains('editable')) { + const editor = window.ace.edit(code_block); + editor.addEventListener('change', () => { + update_play_button(playground_block, playground_crates); + }); + // add Ctrl-Enter command to execute rust code + editor.commands.addCommand({ + name: 'run', + bindKey: { + win: 'Ctrl-Enter', + mac: 'Ctrl-Enter', + }, + exec: _editor => run_rust_code(playground_block), + }); + } + } + } + + // updates the visibility of play button based on `no_run` class and + // used crates vs ones available on https://play.rust-lang.org + function update_play_button(pre_block, playground_crates) { + const play_button = pre_block.querySelector('.play-button'); + + // skip if code is `no_run` + if (pre_block.querySelector('code').classList.contains('no_run')) { + play_button.classList.add('hidden'); + return; + } + + // get list of `extern crate`'s from snippet + const txt = playground_text(pre_block); + const re = /extern\s+crate\s+([a-zA-Z_0-9]+)\s*;/g; + const snippet_crates = []; + let item; + while (item = re.exec(txt)) { + snippet_crates.push(item[1]); + } + + // check if all used crates are available on play.rust-lang.org + const all_available = snippet_crates.every(function(elem) { + return playground_crates.indexOf(elem) > -1; + }); + + if (all_available) { + play_button.classList.remove('hidden'); + play_button.hidden = false; + } else { + play_button.classList.add('hidden'); + } + } + + function run_rust_code(code_block) { + let result_block = code_block.querySelector('.result'); + if (!result_block) { + result_block = document.createElement('code'); + result_block.className = 'result hljs language-bash'; + + code_block.append(result_block); + } + + const text = playground_text(code_block); + const classes = code_block.querySelector('code').classList; + let edition = '2015'; + classes.forEach(className => { + if (className.startsWith('edition')) { + edition = className.slice(7); + } + }); + const params = { + version: 'stable', + optimize: '0', + code: text, + edition: edition, + }; + + if (text.indexOf('#![feature') !== -1) { + params.version = 'nightly'; + } + + result_block.innerText = 'Running...'; + + fetch_with_timeout('https://play.rust-lang.org/evaluate.json', { + headers: { + 'Content-Type': 'application/json', + }, + method: 'POST', + mode: 'cors', + body: JSON.stringify(params), + }) + .then(response => response.json()) + .then(response => { + if (response.result.trim() === '') { + result_block.innerText = 'No output'; + result_block.classList.add('result-no-output'); + } else { + result_block.innerText = response.result; + result_block.classList.remove('result-no-output'); + } + }) + .catch(error => result_block.innerText = 'Playground Communication: ' + error.message); + } + + // Syntax highlighting Configuration + hljs.configure({ + tabReplace: ' ', // 4 spaces + languages: [], // Languages used for auto-detection + }); + + const code_nodes = Array + .from(document.querySelectorAll('code')) + // Don't highlight `inline code` blocks in headers. + .filter(function(node) { + return !node.parentElement.classList.contains('header'); + }); + + if (window.ace) { + // language-rust class needs to be removed for editable + // blocks or highlightjs will capture events + code_nodes + .filter(function(node) { + return node.classList.contains('editable'); + }) + .forEach(function(block) { + block.classList.remove('language-rust'); + }); + + code_nodes + .filter(function(node) { + return !node.classList.contains('editable'); + }) + .forEach(function(block) { + hljs.highlightBlock(block); + }); + } else { + code_nodes.forEach(function(block) { + hljs.highlightBlock(block); + }); + } + + // Adding the hljs class gives code blocks the color css + // even if highlighting doesn't apply + code_nodes.forEach(function(block) { + block.classList.add('hljs'); + }); + + Array.from(document.querySelectorAll('code.hljs')).forEach(function(block) { + + const lines = Array.from(block.querySelectorAll('.boring')); + // If no lines were hidden, return + if (!lines.length) { + return; + } + block.classList.add('hide-boring'); + + const buttons = document.createElement('div'); + buttons.className = 'buttons'; + buttons.innerHTML = ''; + buttons.firstChild.innerHTML = document.getElementById('fa-eye').innerHTML; + + // add expand button + const pre_block = block.parentNode; + pre_block.insertBefore(buttons, pre_block.firstChild); + + buttons.firstChild.addEventListener('click', function(e) { + if (this.title === 'Show hidden lines') { + this.innerHTML = document.getElementById('fa-eye-slash').innerHTML; + this.title = 'Hide lines'; + this.setAttribute('aria-label', e.target.title); + + block.classList.remove('hide-boring'); + } else if (this.title === 'Hide lines') { + this.innerHTML = document.getElementById('fa-eye').innerHTML; + this.title = 'Show hidden lines'; + this.setAttribute('aria-label', e.target.title); + + block.classList.add('hide-boring'); + } + }); + }); + + if (window.playground_copyable) { + Array.from(document.querySelectorAll('pre code')).forEach(function(block) { + const pre_block = block.parentNode; + if (!pre_block.classList.contains('playground')) { + let buttons = pre_block.querySelector('.buttons'); + if (!buttons) { + buttons = document.createElement('div'); + buttons.className = 'buttons'; + pre_block.insertBefore(buttons, pre_block.firstChild); + } + + const clipButton = document.createElement('button'); + clipButton.className = 'clip-button'; + clipButton.title = 'Copy to clipboard'; + clipButton.setAttribute('aria-label', clipButton.title); + clipButton.innerHTML = ''; + + buttons.insertBefore(clipButton, buttons.firstChild); + } + }); + } + + // Process playground code blocks + Array.from(document.querySelectorAll('.playground')).forEach(function(pre_block) { + // Add play button + let buttons = pre_block.querySelector('.buttons'); + if (!buttons) { + buttons = document.createElement('div'); + buttons.className = 'buttons'; + pre_block.insertBefore(buttons, pre_block.firstChild); + } + + const runCodeButton = document.createElement('button'); + runCodeButton.className = 'play-button'; + runCodeButton.hidden = true; + runCodeButton.title = 'Run this code'; + runCodeButton.setAttribute('aria-label', runCodeButton.title); + runCodeButton.innerHTML = document.getElementById('fa-play').innerHTML; + + buttons.insertBefore(runCodeButton, buttons.firstChild); + runCodeButton.addEventListener('click', () => { + run_rust_code(pre_block); + }); + + if (window.playground_copyable) { + const copyCodeClipboardButton = document.createElement('button'); + copyCodeClipboardButton.className = 'clip-button'; + copyCodeClipboardButton.innerHTML = ''; + copyCodeClipboardButton.title = 'Copy to clipboard'; + copyCodeClipboardButton.setAttribute('aria-label', copyCodeClipboardButton.title); + + buttons.insertBefore(copyCodeClipboardButton, buttons.firstChild); + } + + const code_block = pre_block.querySelector('code'); + if (window.ace && code_block.classList.contains('editable')) { + const undoChangesButton = document.createElement('button'); + undoChangesButton.className = 'reset-button'; + undoChangesButton.title = 'Undo changes'; + undoChangesButton.setAttribute('aria-label', undoChangesButton.title); + undoChangesButton.innerHTML += + document.getElementById('fa-clock-rotate-left').innerHTML; + + buttons.insertBefore(undoChangesButton, buttons.firstChild); + + undoChangesButton.addEventListener('click', function() { + const editor = window.ace.edit(code_block); + editor.setValue(editor.originalCode); + editor.clearSelection(); + }); + } + }); +})(); + +(function themes() { + const html = document.querySelector('html'); + const themeToggleButton = document.getElementById('mdbook-theme-toggle'); + const themePopup = document.getElementById('mdbook-theme-list'); + const themeColorMetaTag = document.querySelector('meta[name="theme-color"]'); + const themeIds = []; + themePopup.querySelectorAll('button.theme').forEach(function(el) { + themeIds.push(el.id); + }); + const stylesheets = { + ayuHighlight: document.querySelector('#mdbook-ayu-highlight-css'), + tomorrowNight: document.querySelector('#mdbook-tomorrow-night-css'), + highlight: document.querySelector('#mdbook-highlight-css'), + }; + + function showThemes() { + themePopup.style.display = 'block'; + themeToggleButton.setAttribute('aria-expanded', true); + themePopup.querySelector('button#mdbook-theme-' + get_theme()).focus(); + } + + function updateThemeSelected() { + themePopup.querySelectorAll('.theme-selected').forEach(function(el) { + el.classList.remove('theme-selected'); + }); + const selected = get_saved_theme() ?? 'default_theme'; + let element = themePopup.querySelector('button#mdbook-theme-' + selected); + if (element === null) { + // Fall back in case there is no "Default" item. + element = themePopup.querySelector('button#mdbook-theme-' + get_theme()); + } + element.classList.add('theme-selected'); + } + + function hideThemes() { + themePopup.style.display = 'none'; + themeToggleButton.setAttribute('aria-expanded', false); + themeToggleButton.focus(); + } + + function get_saved_theme() { + let theme = null; + try { + theme = localStorage.getItem('mdbook-theme'); + } catch { + // ignore error. + } + return theme; + } + + function delete_saved_theme() { + localStorage.removeItem('mdbook-theme'); + } + + function get_theme() { + const theme = get_saved_theme(); + if (theme === null || theme === undefined || !themeIds.includes('mdbook-theme-' + theme)) { + if (typeof default_dark_theme === 'undefined') { + // A customized index.hbs might not define this, so fall back to + // old behavior of determining the default on page load. + return default_theme; + } + return window.matchMedia('(prefers-color-scheme: dark)').matches + ? default_dark_theme + : default_light_theme; + } else { + return theme; + } + } + + let previousTheme = default_theme; + function set_theme(theme, store = true) { + let ace_theme; + + if (theme === 'coal' || theme === 'navy') { + stylesheets.ayuHighlight.disabled = true; + stylesheets.tomorrowNight.disabled = false; + stylesheets.highlight.disabled = true; + + ace_theme = 'ace/theme/tomorrow_night'; + } else if (theme === 'ayu') { + stylesheets.ayuHighlight.disabled = false; + stylesheets.tomorrowNight.disabled = true; + stylesheets.highlight.disabled = true; + ace_theme = 'ace/theme/tomorrow_night'; + } else { + stylesheets.ayuHighlight.disabled = true; + stylesheets.tomorrowNight.disabled = true; + stylesheets.highlight.disabled = false; + ace_theme = 'ace/theme/dawn'; + } + + setTimeout(function() { + themeColorMetaTag.content = getComputedStyle(document.documentElement).backgroundColor; + }, 1); + + if (window.ace && window.editors) { + window.editors.forEach(function(editor) { + editor.setTheme(ace_theme); + }); + } + + if (store) { + try { + localStorage.setItem('mdbook-theme', theme); + } catch { + // ignore error. + } + } + + html.classList.remove(previousTheme); + html.classList.add(theme); + previousTheme = theme; + updateThemeSelected(); + } + + const query = window.matchMedia('(prefers-color-scheme: dark)'); + query.onchange = function() { + set_theme(get_theme(), false); + }; + + // Set theme. + set_theme(get_theme(), false); + + themeToggleButton.addEventListener('click', function() { + if (themePopup.style.display === 'block') { + hideThemes(); + } else { + showThemes(); + } + }); + + themePopup.addEventListener('click', function(e) { + let theme; + if (e.target.className === 'theme') { + theme = e.target.id; + } else if (e.target.parentElement.className === 'theme') { + theme = e.target.parentElement.id; + } else { + return; + } + theme = theme.replace(/^mdbook-theme-/, ''); + + if (theme === 'default_theme' || theme === null) { + delete_saved_theme(); + set_theme(get_theme(), false); + } else { + set_theme(theme); + } + }); + + themePopup.addEventListener('focusout', function(e) { + // e.relatedTarget is null in Safari and Firefox on macOS (see workaround below) + if (!!e.relatedTarget && + !themeToggleButton.contains(e.relatedTarget) && + !themePopup.contains(e.relatedTarget) + ) { + hideThemes(); + } + }); + + // Should not be needed, but it works around an issue on macOS & iOS: + // https://github.com/rust-lang/mdBook/issues/628 + document.addEventListener('click', function(e) { + if (themePopup.style.display === 'block' && + !themeToggleButton.contains(e.target) && + !themePopup.contains(e.target) + ) { + hideThemes(); + } + }); + + document.addEventListener('keydown', function(e) { + if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { + return; + } + if (!themePopup.contains(e.target)) { + return; + } + + let li; + switch (e.key) { + case 'Escape': + e.preventDefault(); + hideThemes(); + break; + case 'ArrowUp': + e.preventDefault(); + li = document.activeElement.parentElement; + if (li && li.previousElementSibling) { + li.previousElementSibling.querySelector('button').focus(); + } + break; + case 'ArrowDown': + e.preventDefault(); + li = document.activeElement.parentElement; + if (li && li.nextElementSibling) { + li.nextElementSibling.querySelector('button').focus(); + } + break; + case 'Home': + e.preventDefault(); + themePopup.querySelector('li:first-child button').focus(); + break; + case 'End': + e.preventDefault(); + themePopup.querySelector('li:last-child button').focus(); + break; + } + }); +})(); + +(function sidebar() { + const sidebar = document.getElementById('mdbook-sidebar'); + const sidebarLinks = document.querySelectorAll('#mdbook-sidebar a'); + const sidebarToggleButton = document.getElementById('mdbook-sidebar-toggle'); + const sidebarResizeHandle = document.getElementById('mdbook-sidebar-resize-handle'); + const sidebarCheckbox = document.getElementById('mdbook-sidebar-toggle-anchor'); + let firstContact = null; + + + /* Because we cannot change the `display` using only CSS after/before the transition, we + need JS to do it. We change the display to prevent the browsers search to find text inside + the collapsed sidebar. */ + if (!document.documentElement.classList.contains('sidebar-visible')) { + sidebar.style.display = 'none'; + } + sidebar.addEventListener('transitionend', () => { + /* We only change the display to "none" if we're collapsing the sidebar. */ + if (!sidebarCheckbox.checked) { + sidebar.style.display = 'none'; + } + }); + sidebarToggleButton.addEventListener('click', () => { + /* To allow the sidebar expansion animation, we first need to put back the display. */ + if (!sidebarCheckbox.checked) { + sidebar.style.display = ''; + // Workaround for Safari skipping the animation when changing + // `display` and a transform in the same event loop. This forces a + // reflow after updating the display. + sidebar.offsetHeight; + } + }); + + function showSidebar() { + document.documentElement.classList.add('sidebar-visible'); + Array.from(sidebarLinks).forEach(function(link) { + link.setAttribute('tabIndex', 0); + }); + sidebarToggleButton.setAttribute('aria-expanded', true); + sidebar.setAttribute('aria-hidden', false); + try { + localStorage.setItem('mdbook-sidebar', 'visible'); + } catch { + // Ignore error. + } + } + + function hideSidebar() { + document.documentElement.classList.remove('sidebar-visible'); + Array.from(sidebarLinks).forEach(function(link) { + link.setAttribute('tabIndex', -1); + }); + sidebarToggleButton.setAttribute('aria-expanded', false); + sidebar.setAttribute('aria-hidden', true); + try { + localStorage.setItem('mdbook-sidebar', 'hidden'); + } catch { + // Ignore error. + } + } + + // Toggle sidebar + sidebarCheckbox.addEventListener('change', function sidebarToggle() { + if (sidebarCheckbox.checked) { + const current_width = parseInt( + document.documentElement.style.getPropertyValue('--sidebar-target-width'), 10); + if (current_width < 150) { + document.documentElement.style.setProperty('--sidebar-target-width', '150px'); + } + showSidebar(); + } else { + hideSidebar(); + } + }); + + sidebarResizeHandle.addEventListener('mousedown', initResize, false); + + function initResize() { + window.addEventListener('mousemove', resize, false); + window.addEventListener('mouseup', stopResize, false); + document.documentElement.classList.add('sidebar-resizing'); + } + function resize(e) { + let pos = e.clientX - sidebar.offsetLeft; + if (pos < 20) { + hideSidebar(); + } else { + if (!document.documentElement.classList.contains('sidebar-visible')) { + showSidebar(); + } + pos = Math.min(pos, window.innerWidth - 100); + document.documentElement.style.setProperty('--sidebar-target-width', pos + 'px'); + } + } + //on mouseup remove windows functions mousemove & mouseup + function stopResize() { + document.documentElement.classList.remove('sidebar-resizing'); + window.removeEventListener('mousemove', resize, false); + window.removeEventListener('mouseup', stopResize, false); + } + + document.addEventListener('touchstart', function(e) { + firstContact = { + x: e.touches[0].clientX, + time: Date.now(), + }; + }, { passive: true }); + + document.addEventListener('touchmove', function(e) { + if (!firstContact) { + return; + } + + const curX = e.touches[0].clientX; + const xDiff = curX - firstContact.x, + tDiff = Date.now() - firstContact.time; + + if (tDiff < 250 && Math.abs(xDiff) >= 150) { + if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300)) { + showSidebar(); + } else if (xDiff < 0 && curX < 300) { + hideSidebar(); + } + + firstContact = null; + } + }, { passive: true }); +})(); + +(function chapterNavigation() { + function zoomOutImages() { + for (const elem of Array.from(document.querySelectorAll('input.checkbox-img'))) { + elem.checked = false; + } + } + + document.addEventListener('keydown', function(e) { + if (e.altKey || + e.ctrlKey || + e.metaKey || + window.search && window.search.hasFocus() || + mdbook_something_else_has_focus(e) + ) { + return; + } + + const html = document.querySelector('html'); + + function next() { + const nextButton = document.querySelector('.nav-chapters.next'); + if (nextButton) { + window.location.href = nextButton.href; + } + } + function prev() { + const previousButton = document.querySelector('.nav-chapters.previous'); + if (previousButton) { + window.location.href = previousButton.href; + } + } + function showHelp() { + const container = document.getElementById('mdbook-help-container'); + const overlay = document.getElementById('mdbook-help-popup'); + container.style.display = 'flex'; + + // Clicking outside the popup will dismiss it. + const mouseHandler = event => { + if (overlay.contains(event.target)) { + return; + } + if (event.button !== 0) { + return; + } + event.preventDefault(); + event.stopPropagation(); + document.removeEventListener('mousedown', mouseHandler); + hideHelp(); + }; + + // Pressing esc will dismiss the popup. + const escapeKeyHandler = event => { + if (event.key === 'Escape') { + event.preventDefault(); + event.stopPropagation(); + document.removeEventListener('keydown', escapeKeyHandler, true); + hideHelp(); + } + }; + document.addEventListener('keydown', escapeKeyHandler, true); + document.getElementById('mdbook-help-container') + .addEventListener('mousedown', mouseHandler); + } + function hideHelp() { + document.getElementById('mdbook-help-container').style.display = 'none'; + } + + // Usually needs the Shift key to be pressed + switch (e.key) { + case '?': + e.preventDefault(); + showHelp(); + break; + case 'Escape': + zoomOutImages(); + break; + } + + // Rest of the keys are only active when the Shift key is not pressed + if (e.shiftKey) { + return; + } + + switch (e.key) { + case 'ArrowRight': + e.preventDefault(); + if (html.dir === 'rtl') { + prev(); + } else { + next(); + } + break; + case 'ArrowLeft': + e.preventDefault(); + if (html.dir === 'rtl') { + next(); + } else { + prev(); + } + break; + } + }); +})(); + +(function clipboard() { + const clipButtons = document.querySelectorAll('.clip-button'); + + function hideTooltip(elem) { + elem.firstChild.innerText = ''; + elem.className = 'clip-button'; + } + + function showTooltip(elem, msg) { + elem.firstChild.innerText = msg; + elem.className = 'clip-button tooltipped'; + } + + const clipboardSnippets = new ClipboardJS('.clip-button', { + text: function(trigger) { + hideTooltip(trigger); + const playground = trigger.closest('pre'); + return playground_text(playground, false); + }, + }); + + Array.from(clipButtons).forEach(function(clipButton) { + clipButton.addEventListener('mouseout', function(e) { + hideTooltip(e.currentTarget); + }); + }); + + clipboardSnippets.on('success', function(e) { + e.clearSelection(); + showTooltip(e.trigger, 'Copied!'); + }); + + clipboardSnippets.on('error', function(e) { + showTooltip(e.trigger, 'Clipboard error!'); + }); +})(); + +(function scrollToTop() { + const menuTitle = document.querySelector('.menu-title'); + + menuTitle.addEventListener('click', function() { + document.scrollingElement.scrollTo({ top: 0, behavior: 'smooth' }); + }); +})(); + +(function controllMenu() { + const menu = document.getElementById('mdbook-menu-bar'); + + (function controllPosition() { + let scrollTop = document.scrollingElement.scrollTop; + let prevScrollTop = scrollTop; + const minMenuY = -menu.clientHeight - 50; + // When the script loads, the page can be at any scroll (e.g. if you refresh it). + menu.style.top = scrollTop + 'px'; + // Same as parseInt(menu.style.top.slice(0, -2), but faster + let topCache = menu.style.top.slice(0, -2); + menu.classList.remove('sticky'); + let stickyCache = false; // Same as menu.classList.contains('sticky'), but faster + document.addEventListener('scroll', function() { + scrollTop = Math.max(document.scrollingElement.scrollTop, 0); + // `null` means that it doesn't need to be updated + let nextSticky = null; + let nextTop = null; + const scrollDown = scrollTop > prevScrollTop; + const menuPosAbsoluteY = topCache - scrollTop; + if (scrollDown) { + nextSticky = false; + if (menuPosAbsoluteY > 0) { + nextTop = prevScrollTop; + } + } else { + if (menuPosAbsoluteY > 0) { + nextSticky = true; + } else if (menuPosAbsoluteY < minMenuY) { + nextTop = prevScrollTop + minMenuY; + } + } + if (nextSticky === true && stickyCache === false) { + menu.classList.add('sticky'); + stickyCache = true; + } else if (nextSticky === false && stickyCache === true) { + menu.classList.remove('sticky'); + stickyCache = false; + } + if (nextTop !== null) { + menu.style.top = nextTop + 'px'; + topCache = nextTop; + } + prevScrollTop = scrollTop; + }, { passive: true }); + })(); + (function controllBorder() { + function updateBorder() { + if (menu.offsetTop === 0) { + menu.classList.remove('bordered'); + } else { + menu.classList.add('bordered'); + } + } + updateBorder(); + document.addEventListener('scroll', updateBorder, { passive: true }); + })(); +})(); diff --git a/typ/mdbook/assets/clipboard-1626706a.min.js b/typ/mdbook/assets/clipboard-1626706a.min.js new file mode 100644 index 00000000..02c549e3 --- /dev/null +++ b/typ/mdbook/assets/clipboard-1626706a.min.js @@ -0,0 +1,7 @@ +/*! + * clipboard.js v2.0.4 + * https://zenorocha.github.io/clipboard.js + * + * Licensed MIT © Zeno Rocha + */ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return function(n){var o={};function r(t){if(o[t])return o[t].exports;var e=o[t]={i:t,l:!1,exports:{}};return n[t].call(e.exports,e,e.exports,r),e.l=!0,e.exports}return r.m=n,r.c=o,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=0)}([function(t,e,n){"use strict";var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},i=function(){function o(t,e){for(var n=0;n .hljs { + color: var(--links); +} + +/* + mdbook-body-container is necessary because mobile browsers don't seem to like + overflow-x on the body tag when there is a tag. +*/ +#mdbook-body-container { + /* + This is used when the sidebar pushes the body content off the side of + the screen on small screens. Without it, dragging on mobile Safari + will want to reposition the viewport in a weird way. + */ + overflow-x: clip; +} + +/* Menu Bar */ + +#mdbook-menu-bar, +#mdbook-menu-bar-hover-placeholder { + z-index: 101; + margin: auto calc(0px - var(--page-padding)); +} +#mdbook-menu-bar { + position: relative; + display: flex; + flex-wrap: wrap; + background-color: var(--bg); + border-block-end-color: var(--bg); + border-block-end-width: 1px; + border-block-end-style: solid; +} +#mdbook-menu-bar.sticky, +#mdbook-menu-bar-hover-placeholder:hover + #mdbook-menu-bar, +#mdbook-menu-bar:hover, +html.sidebar-visible #mdbook-menu-bar { + position: -webkit-sticky; + position: sticky; + top: 0 !important; +} +#mdbook-menu-bar-hover-placeholder { + position: sticky; + position: -webkit-sticky; + top: 0; + height: var(--menu-bar-height); +} +#mdbook-menu-bar.bordered { + border-block-end-color: var(--table-border-color); +} +#mdbook-menu-bar .fa-svg, #mdbook-menu-bar .icon-button { + position: relative; + padding: 0 8px; + z-index: 10; + line-height: var(--menu-bar-height); + cursor: pointer; + transition: color 0.5s; +} +@media only screen and (max-width: 420px) { + #mdbook-menu-bar .fa-svg, #mdbook-menu-bar .icon-button { + padding: 0 5px; + } +} + +.icon-button { + border: none; + background: none; + padding: 0; + color: inherit; +} +.icon-button .fa-svg { + margin: 0; +} + +.right-buttons { + margin: 0 15px; +} +.right-buttons a { + text-decoration: none; +} + +.left-buttons { + display: flex; + margin: 0 5px; +} +html:not(.js) .left-buttons button { + display: none; +} + +.menu-title { + display: inline-block; + font-weight: 200; + font-size: 2.4rem; + line-height: var(--menu-bar-height); + text-align: center; + margin: 0; + flex: 1; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.menu-title { + cursor: pointer; +} + +.menu-bar, +.menu-bar:visited, +.nav-chapters, +.nav-chapters:visited, +.mobile-nav-chapters, +.mobile-nav-chapters:visited, +.menu-bar .icon-button, +.menu-bar a .fa-svg { + color: var(--icons); +} + +.menu-bar .fa-svg:hover, +.menu-bar .icon-button:hover, +.nav-chapters:hover, +.mobile-nav-chapters .fa-svg:hover { + color: var(--icons-hover); +} + +/* Nav Icons */ + +.nav-chapters { + font-size: 2.5em; + text-align: center; + text-decoration: none; + + position: fixed; + top: 0; + bottom: 0; + margin: 0; + max-width: 150px; + min-width: 90px; + + display: flex; + justify-content: center; + align-content: center; + flex-direction: column; + + transition: color 0.5s, background-color 0.5s; +} + +.nav-chapters:hover { + text-decoration: none; + background-color: var(--theme-hover); + transition: background-color 0.15s, color 0.15s; +} + +.nav-chapters.previous { + /* Ensure that the previous arrow does not overlap the resize indicator. */ + margin-inline-start: var(--sidebar-resize-indicator-width); +} + +.nav-wrapper { + margin-block-start: 50px; + display: none; +} + +.mobile-nav-chapters { + font-size: 2.5em; + text-align: center; + text-decoration: none; + width: 90px; + border-radius: 5px; + background-color: var(--sidebar-bg); +} + +/* Only Firefox supports flow-relative values */ +.previous { float: left; } +[dir=rtl] .previous { float: right; } + +/* Only Firefox supports flow-relative values */ +.next { + float: right; + right: var(--page-padding); +} +[dir=rtl] .next { + float: left; + right: unset; + left: var(--page-padding); +} + +@media only screen and (max-width: 1080px) { + .nav-wide-wrapper { display: none; } + .nav-wrapper { display: block; } +} + +/* sidebar-visible */ +@media only screen and (max-width: 1380px) { + #mdbook-sidebar-toggle-anchor:checked ~ .page-wrapper .nav-wide-wrapper { display: none; } + #mdbook-sidebar-toggle-anchor:checked ~ .page-wrapper .nav-wrapper { display: block; } +} + +/* Inline code */ + +:not(pre) > .hljs { + display: inline; + padding: 0.1em 0.3em; + border-radius: 3px; +} + +:not(pre):not(a) > .hljs { + color: var(--inline-code-color); + overflow-x: initial; +} + +a:hover > .hljs { + text-decoration: underline; +} + +pre { + position: relative; +} +pre > .buttons { + position: absolute; + z-index: 100; + right: 0px; + top: 2px; + margin: 0px; + padding: 2px 0px; + + color: var(--sidebar-fg); + cursor: pointer; + visibility: hidden; + opacity: 0; + transition: visibility 0.1s linear, opacity 0.1s linear; +} +pre:hover > .buttons { + visibility: visible; + opacity: 1 +} +pre > .buttons :hover { + color: var(--sidebar-active); + border-color: var(--icons-hover); + background-color: var(--theme-hover); +} +pre > .buttons button { + cursor: inherit; + margin: 0px 5px; + padding: 2px 3px 0px 4px; + font-size: 23px; + + border-style: solid; + border-width: 1px; + border-radius: 4px; + border-color: var(--icons); + background-color: var(--theme-popup-bg); + transition: 100ms; + transition-property: color,border-color,background-color; + color: var(--icons); +} + +pre > .buttons button.clip-button { + padding: 2px 4px 0px 6px; +} +pre > .buttons button.clip-button::before { + /* clipboard image from octicons (https://github.com/primer/octicons/tree/v2.0.0) MIT license + */ + content: url('data:image/svg+xml,\ +\ +\ +'); + filter: var(--copy-button-filter); +} +pre > .buttons button.clip-button:hover::before { + filter: var(--copy-button-filter-hover); +} + +@media (pointer: coarse) { + pre > .buttons button { + /* On mobile, make it easier to tap buttons. */ + padding: 0.3rem 1rem; + } + + .sidebar-resize-indicator { + /* Hide resize indicator on devices with limited accuracy */ + display: none; + } +} +pre > code { + display: block; + padding: 1rem; +} + +/* FIXME: ACE editors overlap their buttons because ACE does absolute + positioning within the code block which breaks padding. The only solution I + can think of is to move the padding to the outer pre tag (or insert a div + wrapper), but that would require fixing a whole bunch of CSS rules. +*/ +.hljs.ace_editor { + padding: 0rem 0rem; +} + +pre > .result { + margin-block-start: 10px; +} + +/* Search */ + +#mdbook-searchresults a { + text-decoration: none; +} + +mark { + border-radius: 2px; + padding-block-start: 0; + padding-block-end: 1px; + padding-inline-start: 3px; + padding-inline-end: 3px; + margin-block-start: 0; + margin-block-end: -1px; + margin-inline-start: -3px; + margin-inline-end: -3px; + background-color: var(--search-mark-bg); + transition: background-color 300ms linear; + cursor: pointer; +} + +mark.fade-out { + background-color: rgba(0,0,0,0) !important; + cursor: auto; +} + +.searchbar-outer { + margin-inline-start: auto; + margin-inline-end: auto; + max-width: var(--content-max-width); +} + +#mdbook-searchbar-outer.searching #mdbook-searchbar { + padding-right: 30px; +} +#mdbook-searchbar-outer .spinner-wrapper { + display: none; +} +#mdbook-searchbar-outer.searching .spinner-wrapper { + display: block; +} + +.search-wrapper { + position: relative; +} + +.spinner-wrapper { + --spinner-margin: 2px; + position: absolute; + margin-block-start: calc(var(--searchbar-margin-block-start) + var(--spinner-margin)); + right: var(--spinner-margin); + top: 0; + bottom: var(--spinner-margin); + padding: 6px; + background-color: var(--bg); +} + +#fa-spin { + animation: rotating 2s linear infinite; + display: inline-block; +} + +@keyframes rotating { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +#mdbook-searchbar { + width: 100%; + margin-block-start: var(--searchbar-margin-block-start); + margin-block-end: 0; + margin-inline-start: auto; + margin-inline-end: auto; + padding: 10px 16px; + transition: box-shadow 300ms ease-in-out; + border: 1px solid var(--searchbar-border-color); + border-radius: 3px; + background-color: var(--searchbar-bg); + color: var(--searchbar-fg); +} +#mdbook-searchbar:focus, +#mdbook-searchbar.active { + box-shadow: 0 0 3px var(--searchbar-shadow-color); +} + +.searchresults-header { + font-weight: bold; + font-size: 1em; + padding-block-start: 18px; + padding-block-end: 0; + padding-inline-start: 5px; + padding-inline-end: 0; + color: var(--searchresults-header-fg); +} + +.searchresults-outer { + margin-inline-start: auto; + margin-inline-end: auto; + max-width: var(--content-max-width); + border-block-end: 1px dashed var(--searchresults-border-color); +} + +ul#mdbook-searchresults { + list-style: none; + padding-inline-start: 20px; +} +ul#mdbook-searchresults li { + margin: 10px 0px; + padding: 2px; + border-radius: 2px; +} +ul#mdbook-searchresults li.focus { + background-color: var(--searchresults-li-bg); +} +ul#mdbook-searchresults span.teaser { + display: block; + clear: both; + margin-block-start: 5px; + margin-block-end: 0; + margin-inline-start: 20px; + margin-inline-end: 0; + font-size: 0.8em; +} +ul#mdbook-searchresults span.teaser em { + font-weight: bold; + font-style: normal; +} + +/* Sidebar */ + +.sidebar { + position: fixed; + left: 0; + top: 0; + bottom: 0; + width: var(--sidebar-width); + font-size: 0.875em; + box-sizing: border-box; + -webkit-overflow-scrolling: touch; + overscroll-behavior-y: contain; + background-color: var(--sidebar-bg); + color: var(--sidebar-fg); +} +.sidebar-iframe-inner { + --padding: 10px; + + background-color: var(--sidebar-bg); + padding: var(--padding); + margin: 0; + font-size: 1.4rem; + color: var(--sidebar-fg); + min-height: calc(100vh - var(--padding) * 2); +} +.sidebar-iframe-outer { + border: none; + height: 100%; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; +} +[dir=rtl] .sidebar { left: unset; right: 0; } +.sidebar-resizing { + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; +} +html:not(.sidebar-resizing) .sidebar { + transition: transform 0.3s; /* Animation: slide away */ +} +.sidebar code { + line-height: 2em; +} +.sidebar .sidebar-scrollbox { + overflow-y: auto; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + padding: 10px 10px; +} +.sidebar .sidebar-resize-handle { + position: absolute; + cursor: col-resize; + width: 0; + right: calc(var(--sidebar-resize-indicator-width) * -1); + top: 0; + bottom: 0; + display: flex; + align-items: center; +} + +.sidebar-resize-handle .sidebar-resize-indicator { + width: 100%; + height: 16px; + color: var(--icons); + margin-inline-start: var(--sidebar-resize-indicator-space); + display: flex; + align-items: center; + justify-content: flex-start; +} +.sidebar-resize-handle .sidebar-resize-indicator::before { + content: ""; + width: 2px; + height: 12px; + border-left: dotted 2px currentColor; +} +.sidebar-resize-handle .sidebar-resize-indicator::after { + content: ""; + width: 2px; + height: 16px; + border-left: dotted 2px currentColor; +} + +[dir=rtl] .sidebar .sidebar-resize-handle { + left: calc(var(--sidebar-resize-indicator-width) * -1); + right: unset; +} +.js .sidebar .sidebar-resize-handle { + cursor: col-resize; + width: calc(var(--sidebar-resize-indicator-width) - var(--sidebar-resize-indicator-space)); +} + +html:not(.js) .sidebar-resize-handle { + display: none; +} + +/* sidebar-hidden */ +#mdbook-sidebar-toggle-anchor:not(:checked) ~ .sidebar { + transform: translateX(calc(0px - var(--sidebar-width) - var(--sidebar-resize-indicator-width))); +} +[dir=rtl] #mdbook-sidebar-toggle-anchor:not(:checked) ~ .sidebar { + transform: translateX(calc(var(--sidebar-width) + var(--sidebar-resize-indicator-width))); +} +.sidebar::-webkit-scrollbar { + background: var(--sidebar-bg); +} +.sidebar::-webkit-scrollbar-thumb { + background: var(--scrollbar); +} + +/* sidebar-visible */ +#mdbook-sidebar-toggle-anchor:checked ~ .page-wrapper { + transform: translateX(calc(var(--sidebar-width) + var(--sidebar-resize-indicator-width))); +} +[dir=rtl] #mdbook-sidebar-toggle-anchor:checked ~ .page-wrapper { + transform: translateX(calc(0px - var(--sidebar-width) - var(--sidebar-resize-indicator-width))); +} +@media only screen and (min-width: 620px) { + #mdbook-sidebar-toggle-anchor:checked ~ .page-wrapper { + transform: none; + margin-inline-start: calc(var(--sidebar-width) + var(--sidebar-resize-indicator-width)); + } + [dir=rtl] #mdbook-sidebar-toggle-anchor:checked ~ .page-wrapper { + transform: none; + } +} + +.chapter { + list-style: none outside none; + padding-inline-start: 0; + line-height: 2.2em; +} + +.chapter li { + color: var(--sidebar-non-existant); +} + +/* This is a span wrapping the chapter link and the fold chevron. */ +.chapter-link-wrapper { + /* Used to position the chevron to the right, allowing the text to wrap before it. */ + display: flex; +} + +.chapter li a { + /* Remove underlines. */ + text-decoration: none; + color: var(--sidebar-fg); +} + +.chapter li a:hover { + color: var(--sidebar-active); +} + +.chapter li a.active { + color: var(--sidebar-active); +} + +/* This is the toggle chevron. */ +.chapter-fold-toggle { + cursor: pointer; + /* Positions the chevron to the side. */ + margin-inline-start: auto; + padding: 0 10px; + user-select: none; + opacity: 0.68; +} + +.chapter-fold-toggle div { + transition: transform 0.5s; +} + +/* collapse the section */ +.chapter li:not(.expanded) > ol { + display: none; +} + +.chapter li.chapter-item { + line-height: 1.5em; + margin-block-start: 0.6em; +} + +.chapter li.header-item { + /* Make sure there is plenty of spacing between sections so they don't blend in + with each other. */ + margin-block-end: 0.5em; + /* Avoid too much spacing between lines when section heading word wraps so that + it can be distinguished between successive headings. */ + line-height: 1.5em; +} + +/* When expanded, rotate the chevron to point down. */ +.chapter li.expanded > span > .chapter-fold-toggle div { + transform: rotate(90deg); +} + +.chapter a.current-header { + color: var(--sidebar-active); +} + +.on-this-page { + margin-left: 22px; + border-inline-start: 4px solid var(--sidebar-header-border-color); + padding-left: 8px; +} + +.on-this-page > ol { + padding-left: 0; +} + +/* Horizontal line in chapter list. */ +.spacer { + width: 100%; + height: 3px; + margin: 5px 0px; +} +.chapter .spacer { + background-color: var(--sidebar-spacer); +} + +/* On touch devices, add more vertical spacing to make it easier to tap links. */ +@media (-moz-touch-enabled: 1), (pointer: coarse) { + .chapter li a { padding: 5px 0; } + .spacer { margin: 10px 0; } +} + +.section { + list-style: none outside none; + padding-inline-start: 20px; + line-height: 1.9em; +} + +/* Theme Menu Popup */ + +.theme-popup { + position: absolute; + left: 10px; + top: var(--menu-bar-height); + z-index: 1000; + border-radius: 4px; + font-size: 0.7em; + color: var(--fg); + background: var(--theme-popup-bg); + border: 1px solid var(--theme-popup-border); + margin: 0; + padding: 0; + list-style: none; + display: none; + /* Don't let the children's background extend past the rounded corners. */ + overflow: hidden; +} +[dir=rtl] .theme-popup { left: unset; right: 10px; } +.theme-popup .default { + color: var(--icons); +} +.theme-popup .theme { + width: 100%; + border: 0; + margin: 0; + padding: 2px 20px; + line-height: 25px; + white-space: nowrap; + text-align: start; + cursor: pointer; + color: inherit; + background: inherit; + font-size: inherit; +} +.theme-popup .theme:hover { + background-color: var(--theme-hover); +} + +.theme-selected::before { + display: inline-block; + content: "✓"; + margin-inline-start: -14px; + width: 14px; +} + +/* The container for the help popup that covers the whole window. */ +#mdbook-help-container { + /* Position and size for the whole window. */ + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + /* This uses flex layout (which is set in book.js), and centers the popup + in the window.*/ + display: none; + align-items: center; + justify-content: center; + z-index: 1000; + /* Dim out the book while the popup is visible. */ + background: var(--overlay-bg); +} + +/* The popup help box. */ +#mdbook-help-popup { + box-shadow: 0 4px 24px rgba(0,0,0,0.15); + min-width: 300px; + max-width: 500px; + width: 100%; + box-sizing: border-box; + display: flex; + flex-direction: column; + align-items: center; + background-color: var(--bg); + color: var(--fg); + border-width: 1px; + border-color: var(--theme-popup-border); + border-style: solid; + border-radius: 8px; + padding: 10px; +} + +.mdbook-help-title { + text-align: center; + /* mdbook's margin for h2 is way too large. */ + margin: 10px; +} diff --git a/typ/mdbook/assets/css/general-e96d0476.css b/typ/mdbook/assets/css/general-e96d0476.css new file mode 100644 index 00000000..06a7f427 --- /dev/null +++ b/typ/mdbook/assets/css/general-e96d0476.css @@ -0,0 +1,464 @@ +/* Base styles and content styles */ + +:root { + /* Browser default font-size is 16px, this way 1 rem = 10px */ + font-size: 62.5%; + color-scheme: var(--color-scheme); +} + +html { + font-family: "Open Sans", sans-serif; + color: var(--fg); + background-color: var(--bg); + text-size-adjust: none; + -webkit-text-size-adjust: none; +} + +body { + margin: 0; + font-size: 1.6rem; + overflow-x: hidden; +} + +code { + font-family: var(--mono-font) !important; + font-size: var(--code-font-size); + direction: ltr !important; +} + +/* make long words/inline code not x overflow */ +main { + overflow-wrap: break-word; +} + +/* make wide tables scroll if they overflow */ +.table-wrapper { + overflow-x: auto; +} + +/* Don't change font size in headers. */ +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + font-size: unset; +} + +.left { float: left; } +.right { float: right; } +.boring { opacity: 0.6; } +.hide-boring .boring { display: none; } +.hidden { display: none !important; } + +h2, h3 { margin-block-start: 2.5em; } +h4, h5 { margin-block-start: 2em; } + +.header + .header h3, +.header + .header h4, +.header + .header h5 { + margin-block-start: 1em; +} + +h1:target::before, +h2:target::before, +h3:target::before, +h4:target::before, +h5:target::before, +h6:target::before, +dt:target::before { + display: inline-block; + content: "»"; + margin-inline-start: -30px; + width: 30px; +} + +/* This is broken on Safari as of version 14, but is fixed + in Safari Technology Preview 117 which I think will be Safari 14.2. + https://bugs.webkit.org/show_bug.cgi?id=218076 +*/ +:target { + /* Safari does not support logical properties */ + scroll-margin-top: calc(var(--menu-bar-height) + 0.5em); +} + +.page { + outline: 0; + padding: 0 var(--page-padding); + margin-block-start: calc(0px - var(--menu-bar-height)); /* Compensate for the #mdbook-menu-bar-hover-placeholder */ +} +.page-wrapper { + box-sizing: border-box; + background-color: var(--bg); +} +html:not(.js) .page-wrapper, +.js:not(.sidebar-resizing) .page-wrapper { + transition: margin-left 0.3s ease, transform 0.3s ease; /* Animation: slide away */ +} +[dir=rtl]:not(.js) .page-wrapper, +[dir=rtl].js:not(.sidebar-resizing) .page-wrapper { + transition: margin-right 0.3s ease, transform 0.3s ease; /* Animation: slide away */ +} + +.content { + overflow-y: auto; + padding: 0 5px 50px 5px; +} +.content main { + margin-inline-start: auto; + margin-inline-end: auto; + max-width: var(--content-max-width); +} +.content p { line-height: 1.45em; } +.content ol { line-height: 1.45em; } +.content ul { line-height: 1.45em; } +.content a { text-decoration: none; } +.content a:hover { text-decoration: underline; } +.content img, .content video { max-width: 100%; } +/* When an image is inside an anchor, ensure the focus ring covers the entire + image instead of the anchor's line box. */ +.content a:has(img) { display: inline-block; } +.content .header:link, +.content .header:visited { + color: var(--fg); +} +.content .header:link, +.content .header:visited:hover { + text-decoration: none; +} + +table { + margin: 0 auto; + border-collapse: collapse; +} +table td { + padding: 3px 20px; + border: 1px var(--table-border-color) solid; +} +table thead { + background: var(--table-header-bg); +} +table thead td { + font-weight: 700; + border: none; +} +table thead th { + padding: 3px 20px; +} +table thead tr { + border: 1px var(--table-header-bg) solid; +} +/* Alternate background colors for rows */ +table tbody tr:nth-child(2n) { + background: var(--table-alternate-bg); +} + + +blockquote { + margin: 20px 0; + padding: 0 20px; + color: var(--fg); + background-color: var(--quote-bg); + border-block-start: .1em solid var(--quote-border); + border-block-end: .1em solid var(--quote-border); +} + +/* TODO: Remove .warning in a future version of mdbook, it is replaced by +blockquote tags. */ +.warning { + margin: 20px; + padding: 0 20px; + border-inline-start: 2px solid var(--warning-border); +} + +.warning:before { + position: absolute; + width: 3rem; + height: 3rem; + margin-inline-start: calc(-1.5rem - 21px); + content: "ⓘ"; + text-align: center; + background-color: var(--bg); + color: var(--warning-border); + font-weight: bold; + font-size: 2rem; +} + +blockquote .warning:before { + background-color: var(--quote-bg); +} + +kbd { + background-color: var(--table-border-color); + border-radius: 4px; + border: solid 1px var(--theme-popup-border); + box-shadow: inset 0 -1px 0 var(--theme-hover); + display: inline-block; + font-size: var(--code-font-size); + font-family: var(--mono-font); + line-height: 10px; + padding: 4px 5px; + vertical-align: middle; +} + +sup { + /* Set the line-height for superscript and footnote references so that there + isn't an awkward space appearing above lines that contain the footnote. + + See https://github.com/rust-lang/mdBook/pull/2443#discussion_r1813773583 + for an explanation. + */ + line-height: 0; +} + +.footnote-definition { + font-size: 0.9em; +} +/* The default spacing for a list is a little too large. */ +.footnote-definition ul, +.footnote-definition ol { + padding-left: 20px; +} +.footnote-definition > li { + /* Required to position the ::before target */ + position: relative; +} +.footnote-definition > li:target { + scroll-margin-top: 50vh; +} +.footnote-reference:target { + scroll-margin-top: 50vh; +} +/* Draws a border around the footnote (including the marker) when it is selected. + TODO: If there are multiple linkbacks, highlight which one you just came + from so you know which one to click. +*/ +.footnote-definition > li:target::before { + border: 2px solid var(--footnote-highlight); + border-radius: 6px; + position: absolute; + top: -8px; + right: -8px; + bottom: -8px; + left: -32px; + pointer-events: none; + content: ""; +} +/* Pulses the footnote reference so you can quickly see where you left off reading. + This could use some improvement. +*/ +@media not (prefers-reduced-motion) { + .footnote-reference:target { + animation: fn-highlight 0.8s; + border-radius: 2px; + } + + @keyframes fn-highlight { + from { + background-color: var(--footnote-highlight); + } + } +} + +.tooltiptext { + position: absolute; + visibility: hidden; + color: #fff; + background-color: #333; + transform: translateX(-50%); /* Center by moving tooltip 50% of its width left */ + left: -8px; /* Half of the width of the icon */ + top: -35px; + font-size: 0.8em; + text-align: center; + border-radius: 6px; + padding: 5px 8px; + margin: 5px; + z-index: 1000; +} +.tooltipped .tooltiptext { + visibility: visible; +} + +.chapter li.part-title { + color: var(--sidebar-fg); + margin: 5px 0px; + font-weight: bold; +} + +.result-no-output { + font-style: italic; +} + +.fa-svg svg { + width: 1em; + height: 1em; + fill: currentColor; + margin-bottom: -0.1em; +} + +dt { + font-weight: bold; + margin-top: 0.5em; + margin-bottom: 0.1em; +} + +/* This uses a CSS counter to add numbers to definitions, but only if there is + more than one definition. */ +dl, dt { + counter-reset: dd-counter; +} + +/* When there is more than one definition, increment the counter. The first +selector selects the first definition, and the second one selects definitions +2 and beyond.*/ +dd:has(+ dd), dd + dd { + counter-increment: dd-counter; + /* Use flex display to help with positioning the numbers when there is a p + tag inside the definition. */ + display: flex; + align-items: flex-start; +} + +/* Shows the counter for definitions. The first selector selects the first +definition, and the second one selections definitions 2 and beyond.*/ +dd:has(+ dd)::before, dd + dd::before { + content: counter(dd-counter) ". "; + font-weight: 600; + display: inline-block; + margin-right: 0.5em; +} + +dd > p { + /* For loose definitions that have a p tag inside, don't add a bunch of + space before the definition. */ + margin-top: 0; +} + +/* Remove some excess space from the bottom. */ +.blockquote-tag p:last-child { + margin-bottom: 2px; +} + +.blockquote-tag { + /* Add some padding to make the vertical bar a little taller than the text.*/ + padding: 2px 0px 2px 20px; + /* Add a solid color bar on the left side. */ + border-inline-start-style: solid; + border-inline-start-width: 4px; + /* Disable the background color from normal blockquotes . */ + background-color: inherit; + /* Disable border blocks from blockquotes. */ + border-block-start: none; + border-block-end: none; +} + +.blockquote-tag-title svg { + fill: currentColor; + /* Add space between the icon and the title. */ + margin-right: 8px; +} + +.blockquote-tag-note { + border-inline-start-color: var(--blockquote-note-color); +} + +.blockquote-tag-tip { + border-inline-start-color: var(--blockquote-tip-color); +} + +.blockquote-tag-important { + border-inline-start-color: var(--blockquote-important-color); +} + +.blockquote-tag-warning { + border-inline-start-color: var(--blockquote-warning-color); +} + +.blockquote-tag-caution { + border-inline-start-color: var(--blockquote-caution-color); +} + +.blockquote-tag-note > .blockquote-tag-title { + color: var(--blockquote-note-color); +} + +.blockquote-tag-tip > .blockquote-tag-title { + color: var(--blockquote-tip-color); +} + +.blockquote-tag-important > .blockquote-tag-title { + color: var(--blockquote-important-color); +} + +.blockquote-tag-warning > .blockquote-tag-title { + color: var(--blockquote-warning-color); +} + +.blockquote-tag-caution > .blockquote-tag-title { + color: var(--blockquote-caution-color); +} + +.blockquote-tag-title { + /* Slightly increase the weight for more emphasis. */ + font-weight: 600; + /* Vertically center the icon with the text. */ + display: flex; + align-items: center; + /* Remove default large margins for a more compact display. */ + margin: 2px 0 8px 0; +} + +.blockquote-tag-title .fa-svg { + fill: currentColor; + /* Add some space between the icon and the text. */ + margin-right: 8px; +} + +/* All this code is to handle the "zoomable" images feature. */ + +/* This one makes the checkbox invisible while keeping it selectable with the keyboard (with the + "tab" key). */ +.content .checkbox-img { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} +/* When the input is focused, we make it appear to the user by adding an outline to the img + instead */ +.content .checkbox-img:focus + img { + outline: auto; +} +/* The "zoomed-in" image is not displayed until the checkbox is "ticked". */ +.content .checkbox-img:not(:checked) ~ .img-wrapper { + display: none; +} +.content .checkbox-label { + display: block; + max-width: 100%; +} +.content .checkbox-label img { + cursor: zoom-in; +} +.content .checkbox-img:checked ~ .img-wrapper { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background-color: rgba(0, 0, 0, 0.4); + z-index: 1001; + display: flex; + align-items: center; + justify-content: center; + cursor: zoom-out; +} +.content .checkbox-img:checked ~ .img-wrapper > img { + --img-padding: 5px; + padding: var(--img-padding); + background: #999; + cursor: zoom-out; + max-width: calc(100% - (var(--img-padding) * 2)); + max-height: calc(100% - (var(--img-padding) * 2)); +} diff --git a/typ/mdbook/assets/css/print-9e4910d8.css b/typ/mdbook/assets/css/print-9e4910d8.css new file mode 100644 index 00000000..2004384d --- /dev/null +++ b/typ/mdbook/assets/css/print-9e4910d8.css @@ -0,0 +1,50 @@ + +#mdbook-sidebar, +#mdbook-menu-bar, +.nav-chapters, +.mobile-nav-chapters { + display: none; +} + +#mdbook-page-wrapper.page-wrapper { + transform: none !important; + margin-inline-start: 0px; + overflow-y: initial; +} + +#mdbook-content { + max-width: none; + margin: 0; + padding: 0; +} + +.page { + overflow-y: initial; +} + +code { + direction: ltr !important; +} + +pre > .buttons { + z-index: 2; +} + +a, a:visited, a:active, a:hover { + color: #4183c4; + text-decoration: none; +} + +h1, h2, h3, h4, h5, h6 { + page-break-inside: avoid; + page-break-after: avoid; +} + +pre, code { + page-break-inside: avoid; + white-space: pre-wrap; +} + +.fa { + display: none !important; +} diff --git a/typ/mdbook/assets/css/variables-8adf115d.css b/typ/mdbook/assets/css/variables-8adf115d.css new file mode 100644 index 00000000..af5023bc --- /dev/null +++ b/typ/mdbook/assets/css/variables-8adf115d.css @@ -0,0 +1,383 @@ + +/* Globals */ + +:root { + --sidebar-target-width: 300px; + --sidebar-width: min(var(--sidebar-target-width), 80vw); + --sidebar-resize-indicator-width: 8px; + --sidebar-resize-indicator-space: 2px; + --page-padding: 15px; + --content-max-width: 750px; + --menu-bar-height: 50px; + --mono-font: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace; + --code-font-size: 0.875em; /* please adjust the ace font size accordingly in editor.js */ + --searchbar-margin-block-start: 5px; +} + +/* Themes */ + +.ayu { + --bg: hsl(210, 25%, 8%); + --fg: #c5c5c5; + + --sidebar-bg: #14191f; + --sidebar-fg: #c8c9db; + --sidebar-non-existant: #5c6773; + --sidebar-active: #ffb454; + --sidebar-spacer: #2d334f; + + --scrollbar: var(--sidebar-fg); + + --icons: #737480; + --icons-hover: #b7b9cc; + + --links: #0096cf; + + --inline-code-color: #ffb454; + + --theme-popup-bg: #14191f; + --theme-popup-border: #5c6773; + --theme-hover: #191f26; + + --quote-bg: hsl(226, 15%, 17%); + --quote-border: hsl(226, 15%, 22%); + + --warning-border: #ff8e00; + + --table-border-color: hsl(210, 25%, 13%); + --table-header-bg: hsl(210, 25%, 28%); + --table-alternate-bg: hsl(210, 25%, 11%); + + --searchbar-border-color: #848484; + --searchbar-bg: #424242; + --searchbar-fg: #fff; + --searchbar-shadow-color: #d4c89f; + --searchresults-header-fg: #666; + --searchresults-border-color: #888; + --searchresults-li-bg: #252932; + --search-mark-bg: #e3b171; + + --color-scheme: dark; + + /* Same as `--icons` */ + --copy-button-filter: invert(45%) sepia(6%) saturate(621%) hue-rotate(198deg) brightness(99%) contrast(85%); + /* Same as `--sidebar-active` */ + --copy-button-filter-hover: invert(68%) sepia(55%) saturate(531%) hue-rotate(341deg) brightness(104%) contrast(101%); + + --footnote-highlight: #2668a6; + + --overlay-bg: rgba(33, 40, 48, 0.4); + + --blockquote-note-color: #74b9ff; + --blockquote-tip-color: #09ca09; + --blockquote-important-color: #d3abff; + --blockquote-warning-color: #f0b72f; + --blockquote-caution-color: #f21424; + + --sidebar-header-border-color: #c18639; +} + +.coal { + --bg: hsl(200, 7%, 8%); + --fg: #98a3ad; + + --sidebar-bg: #292c2f; + --sidebar-fg: #a1adb8; + --sidebar-non-existant: #505254; + --sidebar-active: #3473ad; + --sidebar-spacer: #393939; + + --scrollbar: var(--sidebar-fg); + + --icons: #43484d; + --icons-hover: #b3c0cc; + + --links: #2b79a2; + + --inline-code-color: #c5c8c6; + + --theme-popup-bg: #141617; + --theme-popup-border: #43484d; + --theme-hover: #1f2124; + + --quote-bg: hsl(234, 21%, 18%); + --quote-border: hsl(234, 21%, 23%); + + --warning-border: #ff8e00; + + --table-border-color: hsl(200, 7%, 13%); + --table-header-bg: hsl(200, 7%, 28%); + --table-alternate-bg: hsl(200, 7%, 11%); + + --searchbar-border-color: #aaa; + --searchbar-bg: #b7b7b7; + --searchbar-fg: #000; + --searchbar-shadow-color: #aaa; + --searchresults-header-fg: #666; + --searchresults-border-color: #98a3ad; + --searchresults-li-bg: #2b2b2f; + --search-mark-bg: #355c7d; + + --color-scheme: dark; + + /* Same as `--icons` */ + --copy-button-filter: invert(26%) sepia(8%) saturate(575%) hue-rotate(169deg) brightness(87%) contrast(82%); + /* Same as `--sidebar-active` */ + --copy-button-filter-hover: invert(36%) sepia(70%) saturate(503%) hue-rotate(167deg) brightness(98%) contrast(89%); + + --footnote-highlight: #4079ae; + + --overlay-bg: rgba(33, 40, 48, 0.4); + + --blockquote-note-color: #4493f8; + --blockquote-tip-color: #08ae08; + --blockquote-important-color: #ab7df8; + --blockquote-warning-color: #d29922; + --blockquote-caution-color: #d91b29; + + --sidebar-header-border-color: #3473ad; +} + +.light, html:not(.js) { + --bg: hsl(0, 0%, 100%); + --fg: hsl(0, 0%, 0%); + + --sidebar-bg: #fafafa; + --sidebar-fg: hsl(0, 0%, 0%); + --sidebar-non-existant: #aaaaaa; + --sidebar-active: #1f1fff; + --sidebar-spacer: #f4f4f4; + + --scrollbar: #8F8F8F; + + --icons: #747474; + --icons-hover: #000000; + + --links: #20609f; + + --inline-code-color: #301900; + + --theme-popup-bg: #fafafa; + --theme-popup-border: #cccccc; + --theme-hover: #e6e6e6; + + --quote-bg: hsl(197, 37%, 96%); + --quote-border: hsl(197, 37%, 91%); + + --warning-border: #ff8e00; + + --table-border-color: hsl(0, 0%, 95%); + --table-header-bg: hsl(0, 0%, 80%); + --table-alternate-bg: hsl(0, 0%, 97%); + + --searchbar-border-color: #aaa; + --searchbar-bg: #fafafa; + --searchbar-fg: #000; + --searchbar-shadow-color: #aaa; + --searchresults-header-fg: #666; + --searchresults-border-color: #888; + --searchresults-li-bg: #e4f2fe; + --search-mark-bg: #a2cff5; + + --color-scheme: light; + + /* Same as `--icons` */ + --copy-button-filter: invert(45.49%); + /* Same as `--sidebar-active` */ + --copy-button-filter-hover: invert(14%) sepia(93%) saturate(4250%) hue-rotate(243deg) brightness(99%) contrast(130%); + + --footnote-highlight: #7e7eff; + + --overlay-bg: rgba(200, 200, 205, 0.4); + + --blockquote-note-color: #0969da; + --blockquote-tip-color: #008000; + --blockquote-important-color: #8250df; + --blockquote-warning-color: #9a6700; + --blockquote-caution-color: #b52731; + + --sidebar-header-border-color: #6e6edb; +} + +.navy { + --bg: hsl(226, 23%, 11%); + --fg: #bcbdd0; + + --sidebar-bg: #282d3f; + --sidebar-fg: #c8c9db; + --sidebar-non-existant: #505274; + --sidebar-active: #2b79a2; + --sidebar-spacer: #2d334f; + + --scrollbar: var(--sidebar-fg); + + --icons: #737480; + --icons-hover: #b7b9cc; + + --links: #2b79a2; + + --inline-code-color: #c5c8c6; + + --theme-popup-bg: #161923; + --theme-popup-border: #737480; + --theme-hover: #282e40; + + --quote-bg: hsl(226, 15%, 17%); + --quote-border: hsl(226, 15%, 22%); + + --warning-border: #ff8e00; + + --table-border-color: hsl(226, 23%, 16%); + --table-header-bg: hsl(226, 23%, 31%); + --table-alternate-bg: hsl(226, 23%, 14%); + + --searchbar-border-color: #aaa; + --searchbar-bg: #aeaec6; + --searchbar-fg: #000; + --searchbar-shadow-color: #aaa; + --searchresults-header-fg: #5f5f71; + --searchresults-border-color: #5c5c68; + --searchresults-li-bg: #242430; + --search-mark-bg: #a2cff5; + + --color-scheme: dark; + + /* Same as `--icons` */ + --copy-button-filter: invert(51%) sepia(10%) saturate(393%) hue-rotate(198deg) brightness(86%) contrast(87%); + /* Same as `--sidebar-active` */ + --copy-button-filter-hover: invert(46%) sepia(20%) saturate(1537%) hue-rotate(156deg) brightness(85%) contrast(90%); + + --footnote-highlight: #4079ae; + + --overlay-bg: rgba(33, 40, 48, 0.4); + + --blockquote-note-color: #4493f8; + --blockquote-tip-color: #09ca09; + --blockquote-important-color: #ab7df8; + --blockquote-warning-color: #d29922; + --blockquote-caution-color: #f21424; + + --sidebar-header-border-color: #2f6ab5; +} + +.rust { + --bg: hsl(60, 9%, 87%); + --fg: #262625; + + --sidebar-bg: #3b2e2a; + --sidebar-fg: #c8c9db; + --sidebar-non-existant: #505254; + --sidebar-active: #e69f67; + --sidebar-spacer: #45373a; + + --scrollbar: var(--sidebar-fg); + + --icons: #737480; + --icons-hover: #262625; + + --links: #2b79a2; + + --inline-code-color: #6e6b5e; + + --theme-popup-bg: #e1e1db; + --theme-popup-border: #b38f6b; + --theme-hover: #99908a; + + --quote-bg: hsl(60, 5%, 75%); + --quote-border: hsl(60, 5%, 70%); + + --warning-border: #ff8e00; + + --table-border-color: hsl(60, 9%, 82%); + --table-header-bg: #b3a497; + --table-alternate-bg: hsl(60, 9%, 84%); + + --searchbar-border-color: #aaa; + --searchbar-bg: #fafafa; + --searchbar-fg: #000; + --searchbar-shadow-color: #aaa; + --searchresults-header-fg: #666; + --searchresults-border-color: #888; + --searchresults-li-bg: #dec2a2; + --search-mark-bg: #e69f67; + + /* Same as `--icons` */ + --copy-button-filter: invert(51%) sepia(10%) saturate(393%) hue-rotate(198deg) brightness(86%) contrast(87%); + /* Same as `--sidebar-active` */ + --copy-button-filter-hover: invert(77%) sepia(16%) saturate(1798%) hue-rotate(328deg) brightness(98%) contrast(83%); + + --footnote-highlight: #d3a17a; + + --overlay-bg: rgba(150, 150, 150, 0.25); + + --blockquote-note-color: #023b95; + --blockquote-tip-color: #007700; + --blockquote-important-color: #8250df; + --blockquote-warning-color: #603700; + --blockquote-caution-color: #aa1721; + + --sidebar-header-border-color: #8c391f; +} + +@media (prefers-color-scheme: dark) { + html:not(.js) { + --bg: hsl(200, 7%, 8%); + --fg: #98a3ad; + + --sidebar-bg: #292c2f; + --sidebar-fg: #a1adb8; + --sidebar-non-existant: #505254; + --sidebar-active: #3473ad; + --sidebar-spacer: #393939; + + --scrollbar: var(--sidebar-fg); + + --icons: #43484d; + --icons-hover: #b3c0cc; + + --links: #2b79a2; + + --inline-code-color: #c5c8c6; + + --theme-popup-bg: #141617; + --theme-popup-border: #43484d; + --theme-hover: #1f2124; + + --quote-bg: hsl(234, 21%, 18%); + --quote-border: hsl(234, 21%, 23%); + + --warning-border: #ff8e00; + + --table-border-color: hsl(200, 7%, 13%); + --table-header-bg: hsl(200, 7%, 28%); + --table-alternate-bg: hsl(200, 7%, 11%); + + --searchbar-border-color: #aaa; + --searchbar-bg: #b7b7b7; + --searchbar-fg: #000; + --searchbar-shadow-color: #aaa; + --searchresults-header-fg: #666; + --searchresults-border-color: #98a3ad; + --searchresults-li-bg: #2b2b2f; + --search-mark-bg: #355c7d; + + --color-scheme: dark; + + /* Same as `--icons` */ + --copy-button-filter: invert(26%) sepia(8%) saturate(575%) hue-rotate(169deg) brightness(87%) contrast(82%); + /* Same as `--sidebar-active` */ + --copy-button-filter-hover: invert(36%) sepia(70%) saturate(503%) hue-rotate(167deg) brightness(98%) contrast(89%); + + --footnote-highlight: #4079ae; + + --overlay-bg: rgba(33, 40, 48, 0.4); + + --blockquote-note-color: #4493f8; + --blockquote-tip-color: #08ae08; + --blockquote-important-color: #ab7df8; + --blockquote-warning-color: #d29922; + --blockquote-caution-color: #d91b29; + + --sidebar-header-border-color: #3473ad; + } +} diff --git a/typ/mdbook/assets/elasticlunr-ef4e11c1.min.js b/typ/mdbook/assets/elasticlunr-ef4e11c1.min.js new file mode 100644 index 00000000..94b20dd2 --- /dev/null +++ b/typ/mdbook/assets/elasticlunr-ef4e11c1.min.js @@ -0,0 +1,10 @@ +/** + * elasticlunr - http://weixsong.github.io + * Lightweight full-text search engine in Javascript for browser search and offline search. - 0.9.5 + * + * Copyright (C) 2017 Oliver Nightingale + * Copyright (C) 2017 Wei Song + * MIT Licensed + * @license + */ +!function(){function e(e){if(null===e||"object"!=typeof e)return e;var t=e.constructor();for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t}var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.9.5",lunr=t,t.utils={},t.utils.warn=function(e){return function(t){e.console&&console.warn&&console.warn(t)}}(this),t.utils.toString=function(e){return void 0===e||null===e?"":e.toString()},t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var e=Array.prototype.slice.call(arguments),t=e.pop(),n=e;if("function"!=typeof t)throw new TypeError("last argument must be a function");n.forEach(function(e){this.hasHandler(e)||(this.events[e]=[]),this.events[e].push(t)},this)},t.EventEmitter.prototype.removeListener=function(e,t){if(this.hasHandler(e)){var n=this.events[e].indexOf(t);-1!==n&&(this.events[e].splice(n,1),0==this.events[e].length&&delete this.events[e])}},t.EventEmitter.prototype.emit=function(e){if(this.hasHandler(e)){var t=Array.prototype.slice.call(arguments,1);this.events[e].forEach(function(e){e.apply(void 0,t)},this)}},t.EventEmitter.prototype.hasHandler=function(e){return e in this.events},t.tokenizer=function(e){if(!arguments.length||null===e||void 0===e)return[];if(Array.isArray(e)){var n=e.filter(function(e){return null===e||void 0===e?!1:!0});n=n.map(function(e){return t.utils.toString(e).toLowerCase()});var i=[];return n.forEach(function(e){var n=e.split(t.tokenizer.seperator);i=i.concat(n)},this),i}return e.toString().trim().toLowerCase().split(t.tokenizer.seperator)},t.tokenizer.defaultSeperator=/[\s\-]+/,t.tokenizer.seperator=t.tokenizer.defaultSeperator,t.tokenizer.setSeperator=function(e){null!==e&&void 0!==e&&"object"==typeof e&&(t.tokenizer.seperator=e)},t.tokenizer.resetSeperator=function(){t.tokenizer.seperator=t.tokenizer.defaultSeperator},t.tokenizer.getSeperator=function(){return t.tokenizer.seperator},t.Pipeline=function(){this._queue=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in t.Pipeline.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[n]=e},t.Pipeline.getRegisteredFunction=function(e){return e in t.Pipeline.registeredFunctions!=!0?null:t.Pipeline.registeredFunctions[e]},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.getRegisteredFunction(e);if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._queue.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i+1,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i,0,n)},t.Pipeline.prototype.remove=function(e){var t=this._queue.indexOf(e);-1!==t&&this._queue.splice(t,1)},t.Pipeline.prototype.run=function(e){for(var t=[],n=e.length,i=this._queue.length,o=0;n>o;o++){for(var r=e[o],s=0;i>s&&(r=this._queue[s](r,o,e),void 0!==r&&null!==r);s++);void 0!==r&&null!==r&&t.push(r)}return t},t.Pipeline.prototype.reset=function(){this._queue=[]},t.Pipeline.prototype.get=function(){return this._queue},t.Pipeline.prototype.toJSON=function(){return this._queue.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.DocumentStore,this.index={},this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var e=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,e)},t.Index.prototype.off=function(e,t){return this.eventEmitter.removeListener(e,t)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;n._fields=e.fields,n._ref=e.ref,n.documentStore=t.DocumentStore.load(e.documentStore),n.pipeline=t.Pipeline.load(e.pipeline),n.index={};for(var i in e.index)n.index[i]=t.InvertedIndex.load(e.index[i]);return n},t.Index.prototype.addField=function(e){return this._fields.push(e),this.index[e]=new t.InvertedIndex,this},t.Index.prototype.setRef=function(e){return this._ref=e,this},t.Index.prototype.saveDocument=function(e){return this.documentStore=new t.DocumentStore(e),this},t.Index.prototype.addDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.addDoc(i,e),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));this.documentStore.addFieldLength(i,n,o.length);var r={};o.forEach(function(e){e in r?r[e]+=1:r[e]=1},this);for(var s in r){var u=r[s];u=Math.sqrt(u),this.index[n].addToken(s,{ref:i,tf:u})}},this),n&&this.eventEmitter.emit("add",e,this)}},t.Index.prototype.removeDocByRef=function(e){if(e&&this.documentStore.isDocStored()!==!1&&this.documentStore.hasDoc(e)){var t=this.documentStore.getDoc(e);this.removeDoc(t,!1)}},t.Index.prototype.removeDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.hasDoc(i)&&(this.documentStore.removeDoc(i),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));o.forEach(function(e){this.index[n].removeToken(e,i)},this)},this),n&&this.eventEmitter.emit("remove",e,this))}},t.Index.prototype.updateDoc=function(e,t){var t=void 0===t?!0:t;this.removeDocByRef(e[this._ref],!1),this.addDoc(e,!1),t&&this.eventEmitter.emit("update",e,this)},t.Index.prototype.idf=function(e,t){var n="@"+t+"/"+e;if(Object.prototype.hasOwnProperty.call(this._idfCache,n))return this._idfCache[n];var i=this.index[t].getDocFreq(e),o=1+Math.log(this.documentStore.length/(i+1));return this._idfCache[n]=o,o},t.Index.prototype.getFields=function(){return this._fields.slice()},t.Index.prototype.search=function(e,n){if(!e)return[];e="string"==typeof e?{any:e}:JSON.parse(JSON.stringify(e));var i=null;null!=n&&(i=JSON.stringify(n));for(var o=new t.Configuration(i,this.getFields()).get(),r={},s=Object.keys(e),u=0;u0&&t.push(e);for(var i in n)"docs"!==i&&"df"!==i&&this.expandToken(e+i,t,n[i]);return t},t.InvertedIndex.prototype.toJSON=function(){return{root:this.root}},t.Configuration=function(e,n){var e=e||"";if(void 0==n||null==n)throw new Error("fields should not be null");this.config={};var i;try{i=JSON.parse(e),this.buildUserConfig(i,n)}catch(o){t.utils.warn("user configuration parse failed, will use default configuration"),this.buildDefaultConfig(n)}},t.Configuration.prototype.buildDefaultConfig=function(e){this.reset(),e.forEach(function(e){this.config[e]={boost:1,bool:"OR",expand:!1}},this)},t.Configuration.prototype.buildUserConfig=function(e,n){var i="OR",o=!1;if(this.reset(),"bool"in e&&(i=e.bool||i),"expand"in e&&(o=e.expand||o),"fields"in e)for(var r in e.fields)if(n.indexOf(r)>-1){var s=e.fields[r],u=o;void 0!=s.expand&&(u=s.expand),this.config[r]={boost:s.boost||0===s.boost?s.boost:1,bool:s.bool||i,expand:u}}else t.utils.warn("field name in user configuration not found in index instance fields");else this.addAllFields2UserConfig(i,o,n)},t.Configuration.prototype.addAllFields2UserConfig=function(e,t,n){n.forEach(function(n){this.config[n]={boost:1,bool:e,expand:t}},this)},t.Configuration.prototype.get=function(){return this.config},t.Configuration.prototype.reset=function(){this.config={}},lunr.SortedSet=function(){this.length=0,this.elements=[]},lunr.SortedSet.load=function(e){var t=new this;return t.elements=e,t.length=e.length,t},lunr.SortedSet.prototype.add=function(){var e,t;for(e=0;e1;){if(r===e)return o;e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o]}return r===e?o:-1},lunr.SortedSet.prototype.locationFor=function(e){for(var t=0,n=this.elements.length,i=n-t,o=t+Math.floor(i/2),r=this.elements[o];i>1;)e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o];return r>e?o:e>r?o+1:void 0},lunr.SortedSet.prototype.intersect=function(e){for(var t=new lunr.SortedSet,n=0,i=0,o=this.length,r=e.length,s=this.elements,u=e.elements;;){if(n>o-1||i>r-1)break;s[n]!==u[i]?s[n]u[i]&&i++:(t.add(s[n]),n++,i++)}return t},lunr.SortedSet.prototype.clone=function(){var e=new lunr.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},lunr.SortedSet.prototype.union=function(e){var t,n,i;this.length>=e.length?(t=this,n=e):(t=e,n=this),i=t.clone();for(var o=0,r=n.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 A;{X5v literal 0 HcmV?d00001 diff --git a/typ/mdbook/assets/favicon-de23e50b.svg b/typ/mdbook/assets/favicon-de23e50b.svg new file mode 100644 index 00000000..90e0ea58 --- /dev/null +++ b/typ/mdbook/assets/favicon-de23e50b.svg @@ -0,0 +1,22 @@ + + + + + diff --git a/typ/mdbook/assets/fonts/OPEN-SANS-LICENSE.txt b/typ/mdbook/assets/fonts/OPEN-SANS-LICENSE.txt new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/typ/mdbook/assets/fonts/OPEN-SANS-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/typ/mdbook/assets/fonts/SOURCE-CODE-PRO-LICENSE.txt b/typ/mdbook/assets/fonts/SOURCE-CODE-PRO-LICENSE.txt new file mode 100644 index 00000000..366206f5 --- /dev/null +++ b/typ/mdbook/assets/fonts/SOURCE-CODE-PRO-LICENSE.txt @@ -0,0 +1,93 @@ +Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/typ/mdbook/assets/fonts/fonts-9644e21d.css b/typ/mdbook/assets/fonts/fonts-9644e21d.css new file mode 100644 index 00000000..ce32fdc8 --- /dev/null +++ b/typ/mdbook/assets/fonts/fonts-9644e21d.css @@ -0,0 +1,100 @@ +/* Open Sans is licensed under the Apache License, Version 2.0. See http://www.apache.org/licenses/LICENSE-2.0 */ +/* Source Code Pro is under the Open Font License. See https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL */ + +/* open-sans-300 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 300; + src: local('Open Sans Light'), local('OpenSans-Light'), + url('../fonts/open-sans-v17-all-charsets-300-7736aa35.woff2') format('woff2'); +} + +/* open-sans-300italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 300; + src: local('Open Sans Light Italic'), local('OpenSans-LightItalic'), + url('../fonts/open-sans-v17-all-charsets-300italic-2c7b95c0.woff2') format('woff2'); +} + +/* open-sans-regular - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 400; + src: local('Open Sans Regular'), local('OpenSans-Regular'), + url('../fonts/open-sans-v17-all-charsets-regular-2e3b1d34.woff2') format('woff2'); +} + +/* open-sans-italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 400; + src: local('Open Sans Italic'), local('OpenSans-Italic'), + url('../fonts/open-sans-v17-all-charsets-italic-6c9463f7.woff2') format('woff2'); +} + +/* open-sans-600 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 600; + src: local('Open Sans SemiBold'), local('OpenSans-SemiBold'), + url('../fonts/open-sans-v17-all-charsets-600-486c6759.woff2') format('woff2'); +} + +/* open-sans-600italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 600; + src: local('Open Sans SemiBold Italic'), local('OpenSans-SemiBoldItalic'), + url('../fonts/open-sans-v17-all-charsets-600italic-1a3e8659.woff2') format('woff2'); +} + +/* open-sans-700 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 700; + src: local('Open Sans Bold'), local('OpenSans-Bold'), + url('../fonts/open-sans-v17-all-charsets-700-c22fe8c7.woff2') format('woff2'); +} + +/* open-sans-700italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 700; + src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), + url('../fonts/open-sans-v17-all-charsets-700italic-238ae959.woff2') format('woff2'); +} + +/* open-sans-800 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 800; + src: local('Open Sans ExtraBold'), local('OpenSans-ExtraBold'), + url('../fonts/open-sans-v17-all-charsets-800-3d2c812a.woff2') format('woff2'); +} + +/* open-sans-800italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 800; + src: local('Open Sans ExtraBold Italic'), local('OpenSans-ExtraBoldItalic'), + url('../fonts/open-sans-v17-all-charsets-800italic-ba1521ec.woff2') format('woff2'); +} + +/* source-code-pro-500 - latin_vietnamese_latin-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Source Code Pro'; + font-style: normal; + font-weight: 500; + src: url('../fonts/source-code-pro-v11-all-charsets-500-2bdd9410.woff2') format('woff2'); +} diff --git a/typ/mdbook/assets/fonts/open-sans-v17-all-charsets-300-7736aa35.woff2 b/typ/mdbook/assets/fonts/open-sans-v17-all-charsets-300-7736aa35.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..9f51be370fa913ce6de2922b580c262c4822b337 GIT binary patch literal 44352 zcmV(;K-<4}Pew8T0RR910Ifg(5dZ)H0f7hr0Ib{q0RR9100000000000000000000 z0000QE*lUWfmQ}!0EY+&fhq})G!YOAg!yQMtSAeP3IG8%0we>R0t6rhgFFYi4Ge-h zTM$x_K;*{m=TyO<(oF`$dr#+gT@uRz<@OP72f1way{Ld4djxa`IG0*=K;{4c|Nq>i zF~-^s+Z{rPn$}0zLzV=G*ddk#MfRZCOSo7Svxg)@O1QTM(GtC{-8am>TkfXJ&}5JO zXokZnNWAaG&x#r1r&DFc3k@sfl$|@RDaX?kWW{EN@TZnHp}gCDd}u9PW@H>gZ142z+QEhtr+J1&#|>KAj6Y2`)3tZkBe+ebjmTS6=W*;+$iHPBB3Un)Qo?6q*dE(P3T9y565vHowfyevYxI9K2P(y z$<_L{@8?J5hBF;TxvX%vUMY{MW;AQOFw;MRE?UNs>(i)Pw#F30SO&%}gA&nV#5ynh z%lr4w|41^EwI78NR7X_&MdMPeXq|gj4oJ6}tzd{4Fvwwq1Zjy_sfS1l0YYgJLOuSa zG~qZt#{MG5VL1;uY?c0qywYwhjn*_`$CiZFZCFDFz-?kG;zz}gkK?(Z;(4Ip0|Y@p zL`0q-DhfWlFW)O~_Uv1l(z*7n%=kiif-51?{(S1>dTz;-%RXz{zW_NG4POWVdSfQ{H?gb&VPWmSuI@as@lXQ zCML+EVq_sv+K31qR)E!rHdbOZcI*#x`rpX1-FQZd2?w2J5+HD^d^xN_9WLUu;R1_>$Sl2l1j{i_PqRoxBKXmEfAv5RRCG~gHz zoRPqp)jl)`>4DtY_ee=+oD0vBlrtv1+}R|)Aj>WYFNl--dsEGJL13vB!x?g=A&fDlOONKn#&pyZ<<^Gt#Y5O9+B{||V8CFK!GIY^G7 ziird-fZ=XPfYkAzOvx!^1&5rvR8_9Zlqy%TZCti4x_G(NsHD}UoUD}Q&&1ywU=hnR zK_eK;d!w1V-89?ZEBgl|7bFMOFTBI&;bU#pwZ9KM5d*ckoOlTrnn@#e=0bxf_LKF$ zd-0Mi4MXYT*CGx?Tq%ZXXdY`yIWWVJf|?K(5DNrL%GP_mZs@nw-vBb}Rw0W;wB!)N z5TjzUSNZXIA7943^nXK*r#Dw9|Mtf&ksu-=h_`Yp9V;Dkh;8<4psUZVb-$?#qp%T! ziAdB1b$5t5hjOzR8iD$*)2$e71R6>Zt8%DEY$Sg2KWN&b4Cqr8h>8`6#((@8#L}XktLaTS zryU~vMx03nNxOB%uQY)H>VL7pz)zP2nhY%8WH@)-0RsdY-07d0c5ee-ah{%=QSM%u z*8so2)#K3GU!m!|?O$6+?&yD~(Fj>ZOxK05BApxGi zJr7YET6WG{j1#TYt(MkW%SPIA4dOUU&zsN+<{}nS6r*LQrr)v8tl?2R`* z6w<>(odO>Dg>%+<`=H~%Re+D2bI267Gb@WPo}vT*tKEȮck{9tEK*~BMoW% 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 literal 0 HcmV?d00001 diff --git a/typ/mdbook/assets/fonts/open-sans-v17-all-charsets-300italic-2c7b95c0.woff2 b/typ/mdbook/assets/fonts/open-sans-v17-all-charsets-300italic-2c7b95c0.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..2f545448418cba6668a2e7d49e9616e8c69dfd15 GIT binary patch literal 40656 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 bQBM5)6s%p?oy_T)fR3IrepgG2|qH4K6n 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

33OR1_N*!%ylDDa|3D1;gP8brBkv%FrMeyX$5% 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�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 literal 0 HcmV?d00001 diff --git a/typ/mdbook/assets/fonts/open-sans-v17-all-charsets-italic-6c9463f7.woff2 b/typ/mdbook/assets/fonts/open-sans-v17-all-charsets-italic-6c9463f7.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..398b68a0853fbf6368758deb1da49d347e3e7d89 GIT binary patch literal 41076 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

  • ~`;p}3;==ZtNy# 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 literal 0 HcmV?d00001 diff --git a/typ/mdbook/assets/fonts/open-sans-v17-all-charsets-regular-2e3b1d34.woff2 b/typ/mdbook/assets/fonts/open-sans-v17-all-charsets-regular-2e3b1d34.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..8383e94c65478622baf43553262e0e31b8725e2b GIT binary patch literal 43236 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⪚aqC6F-jh=?`}zBG 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/g,">").replace(/"/g,""").replace(/'/g,"'")}function r(e,...n){var t={};for(const n in e)t[n]=e[n];return n.forEach((function(e){for(const n in e)t[n]=e[n]})),t}function a(e){return e.nodeName.toLowerCase()}var i=Object.freeze({__proto__:null,escapeHTML:t,inherit:r,nodeStream:function(e){var n=[];return function e(t,r){for(var i=t.firstChild;i;i=i.nextSibling)3===i.nodeType?r+=i.nodeValue.length:1===i.nodeType&&(n.push({event:"start",offset:r,node:i}),r=e(i,r),a(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:r,node:i}));return r}(e,0),n},mergeStreams:function(e,n,r){var i=0,s="",o=[];function l(){return e.length&&n.length?e[0].offset!==n[0].offset?e[0].offset"}function u(e){s+=""}function d(e){("start"===e.event?c:u)(e.node)}for(;e.length||n.length;){var g=l();if(s+=t(r.substring(i,g[0].offset)),i=g[0].offset,g===e){o.reverse().forEach(u);do{d(g.splice(0,1)[0]),g=l()}while(g===e&&g.length&&g[0].offset===i);o.reverse().forEach(c)}else"start"===g[0].event?o.push(g[0].node):o.pop(),d(g.splice(0,1)[0])}return s+t(r.substr(i))}});const s="",o=e=>!!e.kind;class l{constructor(e,n){this.buffer="",this.classPrefix=n.classPrefix,e.walk(this)}addText(e){this.buffer+=t(e)}openNode(e){if(!o(e))return;let n=e.kind;e.sublanguage||(n=`${this.classPrefix}${n}`),this.span(n)}closeNode(e){o(e)&&(this.buffer+=s)}value(){return this.buffer}span(e){this.buffer+=``}}class c{constructor(){this.rootNode={children:[]},this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){this.top.children.push(e)}openNode(e){const n={kind:e,children:[]};this.add(n),this.stack.push(n)}closeNode(){if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,n){return"string"==typeof n?e.addText(n):n.children&&(e.openNode(n),n.children.forEach(n=>this._walk(e,n)),e.closeNode(n)),e}static _collapse(e){"string"!=typeof e&&e.children&&(e.children.every(e=>"string"==typeof e)?e.children=[e.children.join("")]:e.children.forEach(e=>{c._collapse(e)}))}}class u extends c{constructor(e){super(),this.options=e}addKeyword(e,n){""!==e&&(this.openNode(n),this.addText(e),this.closeNode())}addText(e){""!==e&&this.add(e)}addSublanguage(e,n){const t=e.root;t.kind=n,t.sublanguage=!0,this.add(t)}toHTML(){return new l(this,this.options).value()}finalize(){return!0}}function d(e){return e?"string"==typeof e?e:e.source:null}const g="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",h={begin:"\\\\[\\s\\S]",relevance:0},f={className:"string",begin:"'",end:"'",illegal:"\\n",contains:[h]},p={className:"string",begin:'"',end:'"',illegal:"\\n",contains:[h]},b={begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},m=function(e,n,t={}){var a=r({className:"comment",begin:e,end:n,contains:[]},t);return a.contains.push(b),a.contains.push({className:"doctag",begin:"(?:TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):",relevance:0}),a},v=m("//","$"),x=m("/\\*","\\*/"),E=m("#","$");var _=Object.freeze({__proto__:null,IDENT_RE:"[a-zA-Z]\\w*",UNDERSCORE_IDENT_RE:"[a-zA-Z_]\\w*",NUMBER_RE:"\\b\\d+(\\.\\d+)?",C_NUMBER_RE:g,BINARY_NUMBER_RE:"\\b(0b[01]+)",RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",SHEBANG:(e={})=>{const n=/^#![ ]*\//;return e.binary&&(e.begin=function(...e){return e.map(e=>d(e)).join("")}(n,/.*\b/,e.binary,/\b.*/)),r({className:"meta",begin:n,end:/$/,relevance:0,"on:begin":(e,n)=>{0!==e.index&&n.ignoreMatch()}},e)},BACKSLASH_ESCAPE:h,APOS_STRING_MODE:f,QUOTE_STRING_MODE:p,PHRASAL_WORDS_MODE:b,COMMENT:m,C_LINE_COMMENT_MODE:v,C_BLOCK_COMMENT_MODE:x,HASH_COMMENT_MODE:E,NUMBER_MODE:{className:"number",begin:"\\b\\d+(\\.\\d+)?",relevance:0},C_NUMBER_MODE:{className:"number",begin:g,relevance:0},BINARY_NUMBER_MODE:{className:"number",begin:"\\b(0b[01]+)",relevance:0},CSS_NUMBER_MODE:{className:"number",begin:"\\b\\d+(\\.\\d+)?(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},REGEXP_MODE:{begin:/(?=\/[^/\n]*\/)/,contains:[{className:"regexp",begin:/\//,end:/\/[gimuy]*/,illegal:/\n/,contains:[h,{begin:/\[/,end:/\]/,relevance:0,contains:[h]}]}]},TITLE_MODE:{className:"title",begin:"[a-zA-Z]\\w*",relevance:0},UNDERSCORE_TITLE_MODE:{className:"title",begin:"[a-zA-Z_]\\w*",relevance:0},METHOD_GUARD:{begin:"\\.\\s*[a-zA-Z_]\\w*",relevance:0},END_SAME_AS_BEGIN:function(e){return Object.assign(e,{"on:begin":(e,n)=>{n.data._beginMatch=e[1]},"on:end":(e,n)=>{n.data._beginMatch!==e[1]&&n.ignoreMatch()}})}}),N="of and for in not or if then".split(" ");function w(e,n){return n?+n:function(e){return N.includes(e.toLowerCase())}(e)?0:1}const R=t,y=r,{nodeStream:k,mergeStreams:O}=i,M=Symbol("nomatch");return function(t){var a=[],i={},s={},o=[],l=!0,c=/(^(<[^>]+>|\t|)+|\n)/gm,g="Could not find the language '{}', did you forget to load/include a language module?";const h={disableAutodetect:!0,name:"Plain text",contains:[]};var f={noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:null,__emitter:u};function p(e){return f.noHighlightRe.test(e)}function b(e,n,t,r){var a={code:n,language:e};S("before:highlight",a);var i=a.result?a.result:m(a.language,a.code,t,r);return i.code=a.code,S("after:highlight",i),i}function m(e,t,a,s){var o=t;function c(e,n){var t=E.case_insensitive?n[0].toLowerCase():n[0];return Object.prototype.hasOwnProperty.call(e.keywords,t)&&e.keywords[t]}function u(){null!=y.subLanguage?function(){if(""!==A){var e=null;if("string"==typeof y.subLanguage){if(!i[y.subLanguage])return void O.addText(A);e=m(y.subLanguage,A,!0,k[y.subLanguage]),k[y.subLanguage]=e.top}else e=v(A,y.subLanguage.length?y.subLanguage:null);y.relevance>0&&(I+=e.relevance),O.addSublanguage(e.emitter,e.language)}}():function(){if(!y.keywords)return void O.addText(A);let e=0;y.keywordPatternRe.lastIndex=0;let n=y.keywordPatternRe.exec(A),t="";for(;n;){t+=A.substring(e,n.index);const r=c(y,n);if(r){const[e,a]=r;O.addText(t),t="",I+=a,O.addKeyword(n[0],e)}else t+=n[0];e=y.keywordPatternRe.lastIndex,n=y.keywordPatternRe.exec(A)}t+=A.substr(e),O.addText(t)}(),A=""}function h(e){return e.className&&O.openNode(e.className),y=Object.create(e,{parent:{value:y}})}function p(e){return 0===y.matcher.regexIndex?(A+=e[0],1):(L=!0,0)}var b={};function x(t,r){var i=r&&r[0];if(A+=t,null==i)return u(),0;if("begin"===b.type&&"end"===r.type&&b.index===r.index&&""===i){if(A+=o.slice(r.index,r.index+1),!l){const n=Error("0 width match regex");throw n.languageName=e,n.badRule=b.rule,n}return 1}if(b=r,"begin"===r.type)return function(e){var t=e[0],r=e.rule;const a=new n(r),i=[r.__beforeBegin,r["on:begin"]];for(const n of i)if(n&&(n(e,a),a.ignore))return p(t);return r&&r.endSameAsBegin&&(r.endRe=RegExp(t.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),"m")),r.skip?A+=t:(r.excludeBegin&&(A+=t),u(),r.returnBegin||r.excludeBegin||(A=t)),h(r),r.returnBegin?0:t.length}(r);if("illegal"===r.type&&!a){const e=Error('Illegal lexeme "'+i+'" for mode "'+(y.className||"")+'"');throw e.mode=y,e}if("end"===r.type){var s=function(e){var t=e[0],r=o.substr(e.index),a=function e(t,r,a){let i=function(e,n){var t=e&&e.exec(n);return t&&0===t.index}(t.endRe,a);if(i){if(t["on:end"]){const e=new n(t);t["on:end"](r,e),e.ignore&&(i=!1)}if(i){for(;t.endsParent&&t.parent;)t=t.parent;return t}}if(t.endsWithParent)return e(t.parent,r,a)}(y,e,r);if(!a)return M;var i=y;i.skip?A+=t:(i.returnEnd||i.excludeEnd||(A+=t),u(),i.excludeEnd&&(A=t));do{y.className&&O.closeNode(),y.skip||y.subLanguage||(I+=y.relevance),y=y.parent}while(y!==a.parent);return a.starts&&(a.endSameAsBegin&&(a.starts.endRe=a.endRe),h(a.starts)),i.returnEnd?0:t.length}(r);if(s!==M)return s}if("illegal"===r.type&&""===i)return 1;if(B>1e5&&B>3*r.index)throw Error("potential infinite loop, way more iterations than matches");return A+=i,i.length}var E=T(e);if(!E)throw console.error(g.replace("{}",e)),Error('Unknown language: "'+e+'"');var _=function(e){function n(n,t){return RegExp(d(n),"m"+(e.case_insensitive?"i":"")+(t?"g":""))}class t{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(e,n){n.position=this.position++,this.matchIndexes[this.matchAt]=n,this.regexes.push([n,e]),this.matchAt+=function(e){return RegExp(e.toString()+"|").exec("").length-1}(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null);const e=this.regexes.map(e=>e[1]);this.matcherRe=n(function(e,n="|"){for(var t=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./,r=0,a="",i=0;i0&&(a+=n),a+="(";o.length>0;){var l=t.exec(o);if(null==l){a+=o;break}a+=o.substring(0,l.index),o=o.substring(l.index+l[0].length),"\\"===l[0][0]&&l[1]?a+="\\"+(+l[1]+s):(a+=l[0],"("===l[0]&&r++)}a+=")"}return a}(e),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex;const n=this.matcherRe.exec(e);if(!n)return null;const t=n.findIndex((e,n)=>n>0&&void 0!==e),r=this.matchIndexes[t];return n.splice(0,t),Object.assign(n,r)}}class a{constructor(){this.rules=[],this.multiRegexes=[],this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){if(this.multiRegexes[e])return this.multiRegexes[e];const n=new t;return this.rules.slice(e).forEach(([e,t])=>n.addRule(e,t)),n.compile(),this.multiRegexes[e]=n,n}considerAll(){this.regexIndex=0}addRule(e,n){this.rules.push([e,n]),"begin"===n.type&&this.count++}exec(e){const n=this.getMatcher(this.regexIndex);n.lastIndex=this.lastIndex;const t=n.exec(e);return t&&(this.regexIndex+=t.position+1,this.regexIndex===this.count&&(this.regexIndex=0)),t}}function i(e,n){const t=e.input[e.index-1],r=e.input[e.index+e[0].length];"."!==t&&"."!==r||n.ignoreMatch()}if(e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.");return function t(s,o){const l=s;if(s.compiled)return l;s.compiled=!0,s.__beforeBegin=null,s.keywords=s.keywords||s.beginKeywords;let c=null;if("object"==typeof s.keywords&&(c=s.keywords.$pattern,delete s.keywords.$pattern),s.keywords&&(s.keywords=function(e,n){var t={};return"string"==typeof e?r("keyword",e):Object.keys(e).forEach((function(n){r(n,e[n])})),t;function r(e,r){n&&(r=r.toLowerCase()),r.split(" ").forEach((function(n){var r=n.split("|");t[r[0]]=[e,w(r[0],r[1])]}))}}(s.keywords,e.case_insensitive)),s.lexemes&&c)throw Error("ERR: Prefer `keywords.$pattern` to `mode.lexemes`, BOTH are not allowed. (see mode reference) ");return l.keywordPatternRe=n(s.lexemes||c||/\w+/,!0),o&&(s.beginKeywords&&(s.begin="\\b("+s.beginKeywords.split(" ").join("|")+")(?=\\b|\\s)",s.__beforeBegin=i),s.begin||(s.begin=/\B|\b/),l.beginRe=n(s.begin),s.endSameAsBegin&&(s.end=s.begin),s.end||s.endsWithParent||(s.end=/\B|\b/),s.end&&(l.endRe=n(s.end)),l.terminator_end=d(s.end)||"",s.endsWithParent&&o.terminator_end&&(l.terminator_end+=(s.end?"|":"")+o.terminator_end)),s.illegal&&(l.illegalRe=n(s.illegal)),void 0===s.relevance&&(s.relevance=1),s.contains||(s.contains=[]),s.contains=[].concat(...s.contains.map((function(e){return function(e){return e.variants&&!e.cached_variants&&(e.cached_variants=e.variants.map((function(n){return r(e,{variants:null},n)}))),e.cached_variants?e.cached_variants:function e(n){return!!n&&(n.endsWithParent||e(n.starts))}(e)?r(e,{starts:e.starts?r(e.starts):null}):Object.isFrozen(e)?r(e):e}("self"===e?s:e)}))),s.contains.forEach((function(e){t(e,l)})),s.starts&&t(s.starts,o),l.matcher=function(e){const n=new a;return e.contains.forEach(e=>n.addRule(e.begin,{rule:e,type:"begin"})),e.terminator_end&&n.addRule(e.terminator_end,{type:"end"}),e.illegal&&n.addRule(e.illegal,{type:"illegal"}),n}(l),l}(e)}(E),N="",y=s||_,k={},O=new f.__emitter(f);!function(){for(var e=[],n=y;n!==E;n=n.parent)n.className&&e.unshift(n.className);e.forEach(e=>O.openNode(e))}();var A="",I=0,S=0,B=0,L=!1;try{for(y.matcher.considerAll();;){B++,L?L=!1:(y.matcher.lastIndex=S,y.matcher.considerAll());const e=y.matcher.exec(o);if(!e)break;const n=x(o.substring(S,e.index),e);S=e.index+n}return x(o.substr(S)),O.closeAllNodes(),O.finalize(),N=O.toHTML(),{relevance:I,value:N,language:e,illegal:!1,emitter:O,top:y}}catch(n){if(n.message&&n.message.includes("Illegal"))return{illegal:!0,illegalBy:{msg:n.message,context:o.slice(S-100,S+100),mode:n.mode},sofar:N,relevance:0,value:R(o),emitter:O};if(l)return{illegal:!1,relevance:0,value:R(o),emitter:O,language:e,top:y,errorRaised:n};throw n}}function v(e,n){n=n||f.languages||Object.keys(i);var t=function(e){const n={relevance:0,emitter:new f.__emitter(f),value:R(e),illegal:!1,top:h};return n.emitter.addText(e),n}(e),r=t;return n.filter(T).filter(I).forEach((function(n){var a=m(n,e,!1);a.language=n,a.relevance>r.relevance&&(r=a),a.relevance>t.relevance&&(r=t,t=a)})),r.language&&(t.second_best=r),t}function x(e){return f.tabReplace||f.useBR?e.replace(c,e=>"\n"===e?f.useBR?"
    ":e:f.tabReplace?e.replace(/\t/g,f.tabReplace):e):e}function E(e){let n=null;const t=function(e){var n=e.className+" ";n+=e.parentNode?e.parentNode.className:"";const t=f.languageDetectRe.exec(n);if(t){var r=T(t[1]);return r||(console.warn(g.replace("{}",t[1])),console.warn("Falling back to no-highlight mode for this block.",e)),r?t[1]:"no-highlight"}return n.split(/\s+/).find(e=>p(e)||T(e))}(e);if(p(t))return;S("before:highlightBlock",{block:e,language:t}),f.useBR?(n=document.createElement("div")).innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n"):n=e;const r=n.textContent,a=t?b(t,r,!0):v(r),i=k(n);if(i.length){const e=document.createElement("div");e.innerHTML=a.value,a.value=O(i,k(e),r)}a.value=x(a.value),S("after:highlightBlock",{block:e,result:a}),e.innerHTML=a.value,e.className=function(e,n,t){var r=n?s[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),e.includes(r)||a.push(r),a.join(" ").trim()}(e.className,t,a.language),e.result={language:a.language,re:a.relevance,relavance:a.relevance},a.second_best&&(e.second_best={language:a.second_best.language,re:a.second_best.relevance,relavance:a.second_best.relevance})}const N=()=>{if(!N.called){N.called=!0;var e=document.querySelectorAll("pre code");a.forEach.call(e,E)}};function T(e){return e=(e||"").toLowerCase(),i[e]||i[s[e]]}function A(e,{languageName:n}){"string"==typeof e&&(e=[e]),e.forEach(e=>{s[e]=n})}function I(e){var n=T(e);return n&&!n.disableAutodetect}function S(e,n){var t=e;o.forEach((function(e){e[t]&&e[t](n)}))}Object.assign(t,{highlight:b,highlightAuto:v,fixMarkup:x,highlightBlock:E,configure:function(e){f=y(f,e)},initHighlighting:N,initHighlightingOnLoad:function(){window.addEventListener("DOMContentLoaded",N,!1)},registerLanguage:function(e,n){var r=null;try{r=n(t)}catch(n){if(console.error("Language definition for '{}' could not be registered.".replace("{}",e)),!l)throw n;console.error(n),r=h}r.name||(r.name=e),i[e]=r,r.rawDefinition=n.bind(null,t),r.aliases&&A(r.aliases,{languageName:e})},listLanguages:function(){return Object.keys(i)},getLanguage:T,registerAliases:A,requireLanguage:function(e){var n=T(e);if(n)return n;throw Error("The '{}' language is required, but not loaded.".replace("{}",e))},autoDetection:I,inherit:y,addPlugin:function(e){o.push(e)}}),t.debugMode=function(){l=!1},t.safeMode=function(){l=!0},t.versionString="10.1.1";for(const n in _)"object"==typeof _[n]&&e(_[n]);return Object.assign(t,_),t}({})}();"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs); +hljs.registerLanguage("apache",function(){"use strict";return function(e){var n={className:"number",begin:"\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?"};return{name:"Apache config",aliases:["apacheconf"],case_insensitive:!0,contains:[e.HASH_COMMENT_MODE,{className:"section",begin:"",contains:[n,{className:"number",begin:":\\d{1,5}"},e.inherit(e.QUOTE_STRING_MODE,{relevance:0})]},{className:"attribute",begin:/\w+/,relevance:0,keywords:{nomarkup:"order deny allow setenv rewriterule rewriteengine rewritecond documentroot sethandler errordocument loadmodule options header listen serverroot servername"},starts:{end:/$/,relevance:0,keywords:{literal:"on off all deny allow"},contains:[{className:"meta",begin:"\\s\\[",end:"\\]$"},{className:"variable",begin:"[\\$%]\\{",end:"\\}",contains:["self",{className:"number",begin:"[\\$%]\\d+"}]},n,{className:"number",begin:"\\d+"},e.QUOTE_STRING_MODE]}}],illegal:/\S/}}}()); +hljs.registerLanguage("bash",function(){"use strict";return function(e){const s={};Object.assign(s,{className:"variable",variants:[{begin:/\$[\w\d#@][\w\d_]*/},{begin:/\$\{/,end:/\}/,contains:[{begin:/:-/,contains:[s]}]}]});const t={className:"subst",begin:/\$\(/,end:/\)/,contains:[e.BACKSLASH_ESCAPE]},n={className:"string",begin:/"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,s,t]};t.contains.push(n);const a={begin:/\$\(\(/,end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},e.NUMBER_MODE,s]},i=e.SHEBANG({binary:"(fish|bash|zsh|sh|csh|ksh|tcsh|dash|scsh)",relevance:10}),c={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{name:"Bash",aliases:["sh","zsh"],keywords:{$pattern:/\b-?[a-z\._]+\b/,keyword:"if then else elif fi for while in do done case esac function",literal:"true false",built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp",_:"-ne -eq -lt -gt -f -d -e -s -l -a"},contains:[i,e.SHEBANG(),c,a,e.HASH_COMMENT_MODE,n,{className:"",begin:/\\"/},{className:"string",begin:/'/,end:/'/},s]}}}()); +hljs.registerLanguage("c-like",function(){"use strict";return function(e){function t(e){return"(?:"+e+")?"}var n="(decltype\\(auto\\)|"+t("[a-zA-Z_]\\w*::")+"[a-zA-Z_]\\w*"+t("<.*?>")+")",r={className:"keyword",begin:"\\b[a-z\\d_]*_t\\b"},a={className:"string",variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)",end:"'",illegal:"."},e.END_SAME_AS_BEGIN({begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},i={className:"number",variants:[{begin:"\\b(0b[01']+)"},{begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],relevance:0},s={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{"meta-keyword":"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include"},contains:[{begin:/\\\n/,relevance:0},e.inherit(a,{className:"meta-string"}),{className:"meta-string",begin:/<.*?>/,end:/$/,illegal:"\\n"},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},o={className:"title",begin:t("[a-zA-Z_]\\w*::")+e.IDENT_RE,relevance:0},c=t("[a-zA-Z_]\\w*::")+e.IDENT_RE+"\\s*\\(",l={keyword:"int float while private char char8_t char16_t char32_t catch import module export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using asm case typeid wchar_t short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignas alignof constexpr consteval constinit decltype concept co_await co_return co_yield requires noexcept static_assert thread_local restrict final override atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong new throw return and and_eq bitand bitor compl not not_eq or or_eq xor xor_eq",built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr _Bool complex _Complex imaginary _Imaginary",literal:"true false nullptr NULL"},d=[r,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,i,a],_={variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}],keywords:l,contains:d.concat([{begin:/\(/,end:/\)/,keywords:l,contains:d.concat(["self"]),relevance:0}]),relevance:0},u={className:"function",begin:"("+n+"[\\*&\\s]+)+"+c,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:l,illegal:/[^\w\s\*&:<>]/,contains:[{begin:"decltype\\(auto\\)",keywords:l,relevance:0},{begin:c,returnBegin:!0,contains:[o],relevance:0},{className:"params",begin:/\(/,end:/\)/,keywords:l,relevance:0,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,i,r,{begin:/\(/,end:/\)/,keywords:l,relevance:0,contains:["self",e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,i,r]}]},r,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,s]};return{aliases:["c","cc","h","c++","h++","hpp","hh","hxx","cxx"],keywords:l,disableAutodetect:!0,illegal:"",keywords:l,contains:["self",r]},{begin:e.IDENT_RE+"::",keywords:l},{className:"class",beginKeywords:"class struct",end:/[{;:]/,contains:[{begin://,contains:["self"]},e.TITLE_MODE]}]),exports:{preprocessor:s,strings:a,keywords:l}}}}()); +hljs.registerLanguage("c",function(){"use strict";return function(e){var n=e.getLanguage("c-like").rawDefinition();return n.name="C",n.aliases=["c","h"],n}}()); +hljs.registerLanguage("coffeescript",function(){"use strict";const e=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],n=["true","false","null","undefined","NaN","Infinity"],a=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]);return function(r){var t={keyword:e.concat(["then","unless","until","loop","by","when","and","or","is","isnt","not"]).filter((e=>n=>!e.includes(n))(["var","const","let","function","static"])).join(" "),literal:n.concat(["yes","no","on","off"]).join(" "),built_in:a.concat(["npm","print"]).join(" ")},i="[A-Za-z$_][0-9A-Za-z$_]*",s={className:"subst",begin:/#\{/,end:/}/,keywords:t},o=[r.BINARY_NUMBER_MODE,r.inherit(r.C_NUMBER_MODE,{starts:{end:"(\\s*/)?",relevance:0}}),{className:"string",variants:[{begin:/'''/,end:/'''/,contains:[r.BACKSLASH_ESCAPE]},{begin:/'/,end:/'/,contains:[r.BACKSLASH_ESCAPE]},{begin:/"""/,end:/"""/,contains:[r.BACKSLASH_ESCAPE,s]},{begin:/"/,end:/"/,contains:[r.BACKSLASH_ESCAPE,s]}]},{className:"regexp",variants:[{begin:"///",end:"///",contains:[s,r.HASH_COMMENT_MODE]},{begin:"//[gim]{0,3}(?=\\W)",relevance:0},{begin:/\/(?![ *]).*?(?![\\]).\/[gim]{0,3}(?=\W)/}]},{begin:"@"+i},{subLanguage:"javascript",excludeBegin:!0,excludeEnd:!0,variants:[{begin:"```",end:"```"},{begin:"`",end:"`"}]}];s.contains=o;var c=r.inherit(r.TITLE_MODE,{begin:i}),l={className:"params",begin:"\\([^\\(]",returnBegin:!0,contains:[{begin:/\(/,end:/\)/,keywords:t,contains:["self"].concat(o)}]};return{name:"CoffeeScript",aliases:["coffee","cson","iced"],keywords:t,illegal:/\/\*/,contains:o.concat([r.COMMENT("###","###"),r.HASH_COMMENT_MODE,{className:"function",begin:"^\\s*"+i+"\\s*=\\s*(\\(.*\\))?\\s*\\B[-=]>",end:"[-=]>",returnBegin:!0,contains:[c,l]},{begin:/[:\(,=]\s*/,relevance:0,contains:[{className:"function",begin:"(\\(.*\\))?\\s*\\B[-=]>",end:"[-=]>",returnBegin:!0,contains:[l]}]},{className:"class",beginKeywords:"class",end:"$",illegal:/[:="\[\]]/,contains:[{beginKeywords:"extends",endsWithParent:!0,illegal:/[:="\[\]]/,contains:[c]},c]},{begin:i+":",end:":",returnBegin:!0,returnEnd:!0,relevance:0}])}}}()); +hljs.registerLanguage("cpp",function(){"use strict";return function(e){var t=e.getLanguage("c-like").rawDefinition();return t.disableAutodetect=!1,t.name="C++",t.aliases=["cc","c++","h++","hpp","hh","hxx","cxx"],t}}()); +hljs.registerLanguage("csharp",function(){"use strict";return function(e){var n={keyword:"abstract as base bool break byte case catch char checked const continue decimal default delegate do double enum event explicit extern finally fixed float for foreach goto if implicit in int interface internal is lock long object operator out override params private protected public readonly ref sbyte sealed short sizeof stackalloc static string struct switch this try typeof uint ulong unchecked unsafe ushort using virtual void volatile while add alias ascending async await by descending dynamic equals from get global group into join let nameof on orderby partial remove select set value var when where yield",literal:"null false true"},i=e.inherit(e.TITLE_MODE,{begin:"[a-zA-Z](\\.?\\w)*"}),a={className:"number",variants:[{begin:"\\b(0b[01']+)"},{begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],relevance:0},s={className:"string",begin:'@"',end:'"',contains:[{begin:'""'}]},t=e.inherit(s,{illegal:/\n/}),l={className:"subst",begin:"{",end:"}",keywords:n},r=e.inherit(l,{illegal:/\n/}),c={className:"string",begin:/\$"/,end:'"',illegal:/\n/,contains:[{begin:"{{"},{begin:"}}"},e.BACKSLASH_ESCAPE,r]},o={className:"string",begin:/\$@"/,end:'"',contains:[{begin:"{{"},{begin:"}}"},{begin:'""'},l]},g=e.inherit(o,{illegal:/\n/,contains:[{begin:"{{"},{begin:"}}"},{begin:'""'},r]});l.contains=[o,c,s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.C_BLOCK_COMMENT_MODE],r.contains=[g,c,t,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.inherit(e.C_BLOCK_COMMENT_MODE,{illegal:/\n/})];var d={variants:[o,c,s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},E={begin:"<",end:">",contains:[{beginKeywords:"in out"},i]},_=e.IDENT_RE+"(<"+e.IDENT_RE+"(\\s*,\\s*"+e.IDENT_RE+")*>)?(\\[\\])?",b={begin:"@"+e.IDENT_RE,relevance:0};return{name:"C#",aliases:["cs","c#"],keywords:n,illegal:/::/,contains:[e.COMMENT("///","$",{returnBegin:!0,contains:[{className:"doctag",variants:[{begin:"///",relevance:0},{begin:"\x3c!--|--\x3e"},{begin:""}]}]}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"meta",begin:"#",end:"$",keywords:{"meta-keyword":"if else elif endif define undef warning error line region endregion pragma checksum"}},d,a,{beginKeywords:"class interface",end:/[{;=]/,illegal:/[^\s:,]/,contains:[{beginKeywords:"where class"},i,E,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"namespace",end:/[{;=]/,illegal:/[^\s:]/,contains:[i,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"meta",begin:"^\\s*\\[",excludeBegin:!0,end:"\\]",excludeEnd:!0,contains:[{className:"meta-string",begin:/"/,end:/"/}]},{beginKeywords:"new return throw await else",relevance:0},{className:"function",begin:"("+_+"\\s+)+"+e.IDENT_RE+"\\s*(\\<.+\\>)?\\s*\\(",returnBegin:!0,end:/\s*[{;=]/,excludeEnd:!0,keywords:n,contains:[{begin:e.IDENT_RE+"\\s*(\\<.+\\>)?\\s*\\(",returnBegin:!0,contains:[e.TITLE_MODE,E],relevance:0},{className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:n,relevance:0,contains:[d,a,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},b]}}}()); +hljs.registerLanguage("css",function(){"use strict";return function(e){var n={begin:/(?:[A-Z\_\.\-]+|--[a-zA-Z0-9_-]+)\s*:/,returnBegin:!0,end:";",endsWithParent:!0,contains:[{className:"attribute",begin:/\S/,end:":",excludeEnd:!0,starts:{endsWithParent:!0,excludeEnd:!0,contains:[{begin:/[\w-]+\(/,returnBegin:!0,contains:[{className:"built_in",begin:/[\w-]+/},{begin:/\(/,end:/\)/,contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.CSS_NUMBER_MODE]}]},e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,e.C_BLOCK_COMMENT_MODE,{className:"number",begin:"#[0-9A-Fa-f]+"},{className:"meta",begin:"!important"}]}}]};return{name:"CSS",case_insensitive:!0,illegal:/[=\/|'\$]/,contains:[e.C_BLOCK_COMMENT_MODE,{className:"selector-id",begin:/#[A-Za-z0-9_-]+/},{className:"selector-class",begin:/\.[A-Za-z0-9_-]+/},{className:"selector-attr",begin:/\[/,end:/\]/,illegal:"$",contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},{className:"selector-pseudo",begin:/:(:)?[a-zA-Z0-9\_\-\+\(\)"'.]+/},{begin:"@(page|font-face)",lexemes:"@[a-z-]+",keywords:"@page @font-face"},{begin:"@",end:"[{;]",illegal:/:/,returnBegin:!0,contains:[{className:"keyword",begin:/@\-?\w[\w]*(\-\w+)*/},{begin:/\s/,endsWithParent:!0,excludeEnd:!0,relevance:0,keywords:"and or not only",contains:[{begin:/[a-z-]+:/,className:"attribute"},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.CSS_NUMBER_MODE]}]},{className:"selector-tag",begin:"[a-zA-Z-][a-zA-Z0-9_-]*",relevance:0},{begin:"{",end:"}",illegal:/\S/,contains:[e.C_BLOCK_COMMENT_MODE,n]}]}}}()); +hljs.registerLanguage("diff",function(){"use strict";return function(e){return{name:"Diff",aliases:["patch"],contains:[{className:"meta",relevance:10,variants:[{begin:/^@@ +\-\d+,\d+ +\+\d+,\d+ +@@$/},{begin:/^\*\*\* +\d+,\d+ +\*\*\*\*$/},{begin:/^\-\-\- +\d+,\d+ +\-\-\-\-$/}]},{className:"comment",variants:[{begin:/Index: /,end:/$/},{begin:/={3,}/,end:/$/},{begin:/^\-{3}/,end:/$/},{begin:/^\*{3} /,end:/$/},{begin:/^\+{3}/,end:/$/},{begin:/^\*{15}$/}]},{className:"addition",begin:"^\\+",end:"$"},{className:"deletion",begin:"^\\-",end:"$"},{className:"addition",begin:"^\\!",end:"$"}]}}}()); +hljs.registerLanguage("go",function(){"use strict";return function(e){var n={keyword:"break default func interface select case map struct chan else goto package switch const fallthrough if range type continue for import return var go defer bool byte complex64 complex128 float32 float64 int8 int16 int32 int64 string uint8 uint16 uint32 uint64 int uint uintptr rune",literal:"true false iota nil",built_in:"append cap close complex copy imag len make new panic print println real recover delete"};return{name:"Go",aliases:["golang"],keywords:n,illegal:"e(n)).join("")}return function(a){var s={className:"number",relevance:0,variants:[{begin:/([\+\-]+)?[\d]+_[\d_]+/},{begin:a.NUMBER_RE}]},i=a.COMMENT();i.variants=[{begin:/;/,end:/$/},{begin:/#/,end:/$/}];var t={className:"variable",variants:[{begin:/\$[\w\d"][\w\d_]*/},{begin:/\$\{(.*?)}/}]},r={className:"literal",begin:/\bon|off|true|false|yes|no\b/},l={className:"string",contains:[a.BACKSLASH_ESCAPE],variants:[{begin:"'''",end:"'''",relevance:10},{begin:'"""',end:'"""',relevance:10},{begin:'"',end:'"'},{begin:"'",end:"'"}]},c={begin:/\[/,end:/\]/,contains:[i,r,t,l,s,"self"],relevance:0},g="("+[/[A-Za-z0-9_-]+/,/"(\\"|[^"])*"/,/'[^']*'/].map(n=>e(n)).join("|")+")";return{name:"TOML, also INI",aliases:["toml"],case_insensitive:!0,illegal:/\S/,contains:[i,{className:"section",begin:/\[+/,end:/\]+/},{begin:n(g,"(\\s*\\.\\s*",g,")*",n("(?=",/\s*=\s*[^#\s]/,")")),className:"attr",starts:{end:/$/,contains:[i,c,r,t,l,s]}}]}}}()); +hljs.registerLanguage("java",function(){"use strict";function e(e){return e?"string"==typeof e?e:e.source:null}function n(e){return a("(",e,")?")}function a(...n){return n.map(n=>e(n)).join("")}function s(...n){return"("+n.map(n=>e(n)).join("|")+")"}return function(e){var t="false synchronized int abstract float private char boolean var static null if const for true while long strictfp finally protected import native final void enum else break transient catch instanceof byte super volatile case assert short package default double public try this switch continue throws protected public private module requires exports do",i={className:"meta",begin:"@[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*",contains:[{begin:/\(/,end:/\)/,contains:["self"]}]},r=e=>a("[",e,"]+([",e,"_]*[",e,"]+)?"),c={className:"number",variants:[{begin:`\\b(0[bB]${r("01")})[lL]?`},{begin:`\\b(0${r("0-7")})[dDfFlL]?`},{begin:a(/\b0[xX]/,s(a(r("a-fA-F0-9"),/\./,r("a-fA-F0-9")),a(r("a-fA-F0-9"),/\.?/),a(/\./,r("a-fA-F0-9"))),/([pP][+-]?(\d+))?/,/[fFdDlL]?/)},{begin:a(/\b/,s(a(/\d*\./,r("\\d")),r("\\d")),/[eE][+-]?[\d]+[dDfF]?/)},{begin:a(/\b/,r(/\d/),n(/\.?/),n(r(/\d/)),/[dDfFlL]?/)}],relevance:0};return{name:"Java",aliases:["jsp"],keywords:t,illegal:/<\/|#/,contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/,relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"class",beginKeywords:"class interface",end:/[{;=]/,excludeEnd:!0,keywords:"class interface",illegal:/[:"\[\]]/,contains:[{beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"new throw return else",relevance:0},{className:"function",begin:"([À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*(<[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*(\\s*,\\s*[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*)*>)?\\s+)+"+e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:t,contains:[{begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0,contains:[e.UNDERSCORE_TITLE_MODE]},{className:"params",begin:/\(/,end:/\)/,keywords:t,relevance:0,contains:[i,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},c,i]}}}()); +hljs.registerLanguage("javascript",function(){"use strict";const e=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],n=["true","false","null","undefined","NaN","Infinity"],a=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]);function s(e){return r("(?=",e,")")}function r(...e){return e.map(e=>(function(e){return e?"string"==typeof e?e:e.source:null})(e)).join("")}return function(t){var i="[A-Za-z$_][0-9A-Za-z$_]*",c={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/},o={$pattern:"[A-Za-z$_][0-9A-Za-z$_]*",keyword:e.join(" "),literal:n.join(" "),built_in:a.join(" ")},l={className:"number",variants:[{begin:"\\b(0[bB][01]+)n?"},{begin:"\\b(0[oO][0-7]+)n?"},{begin:t.C_NUMBER_RE+"n?"}],relevance:0},E={className:"subst",begin:"\\$\\{",end:"\\}",keywords:o,contains:[]},d={begin:"html`",end:"",starts:{end:"`",returnEnd:!1,contains:[t.BACKSLASH_ESCAPE,E],subLanguage:"xml"}},g={begin:"css`",end:"",starts:{end:"`",returnEnd:!1,contains:[t.BACKSLASH_ESCAPE,E],subLanguage:"css"}},u={className:"string",begin:"`",end:"`",contains:[t.BACKSLASH_ESCAPE,E]};E.contains=[t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,d,g,u,l,t.REGEXP_MODE];var b=E.contains.concat([{begin:/\(/,end:/\)/,contains:["self"].concat(E.contains,[t.C_BLOCK_COMMENT_MODE,t.C_LINE_COMMENT_MODE])},t.C_BLOCK_COMMENT_MODE,t.C_LINE_COMMENT_MODE]),_={className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,contains:b};return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:o,contains:[t.SHEBANG({binary:"node",relevance:5}),{className:"meta",relevance:10,begin:/^\s*['"]use (strict|asm)['"]/},t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,d,g,u,t.C_LINE_COMMENT_MODE,t.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag",begin:"@[A-Za-z]+",contains:[{className:"type",begin:"\\{",end:"\\}",relevance:0},{className:"variable",begin:i+"(?=\\s*(-)|$)",endsParent:!0,relevance:0},{begin:/(?=[^\n])\s/,relevance:0}]}]}),t.C_BLOCK_COMMENT_MODE,l,{begin:r(/[{,\n]\s*/,s(r(/(((\/\/.*)|(\/\*(.|\n)*\*\/))\s*)*/,i+"\\s*:"))),relevance:0,contains:[{className:"attr",begin:i+s("\\s*:"),relevance:0}]},{begin:"("+t.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",keywords:"return throw case",contains:[t.C_LINE_COMMENT_MODE,t.C_BLOCK_COMMENT_MODE,t.REGEXP_MODE,{className:"function",begin:"(\\([^(]*(\\([^(]*(\\([^(]*\\))?\\))?\\)|"+t.UNDERSCORE_IDENT_RE+")\\s*=>",returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:t.UNDERSCORE_IDENT_RE},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:o,contains:b}]}]},{begin:/,/,relevance:0},{className:"",begin:/\s/,end:/\s*/,skip:!0},{variants:[{begin:"<>",end:""},{begin:c.begin,end:c.end}],subLanguage:"xml",contains:[{begin:c.begin,end:c.end,skip:!0,contains:["self"]}]}],relevance:0},{className:"function",beginKeywords:"function",end:/\{/,excludeEnd:!0,contains:[t.inherit(t.TITLE_MODE,{begin:i}),_],illegal:/\[|%/},{begin:/\$[(.]/},t.METHOD_GUARD,{className:"class",beginKeywords:"class",end:/[{;=]/,excludeEnd:!0,illegal:/[:"\[\]]/,contains:[{beginKeywords:"extends"},t.UNDERSCORE_TITLE_MODE]},{beginKeywords:"constructor",end:/\{/,excludeEnd:!0},{begin:"(get|set)\\s+(?="+i+"\\()",end:/{/,keywords:"get set",contains:[t.inherit(t.TITLE_MODE,{begin:i}),{begin:/\(\)/},_]}],illegal:/#(?!!)/}}}()); +hljs.registerLanguage("json",function(){"use strict";return function(n){var e={literal:"true false null"},i=[n.C_LINE_COMMENT_MODE,n.C_BLOCK_COMMENT_MODE],t=[n.QUOTE_STRING_MODE,n.C_NUMBER_MODE],a={end:",",endsWithParent:!0,excludeEnd:!0,contains:t,keywords:e},l={begin:"{",end:"}",contains:[{className:"attr",begin:/"/,end:/"/,contains:[n.BACKSLASH_ESCAPE],illegal:"\\n"},n.inherit(a,{begin:/:/})].concat(i),illegal:"\\S"},s={begin:"\\[",end:"\\]",contains:[n.inherit(a)],illegal:"\\S"};return t.push(l,s),i.forEach((function(n){t.push(n)})),{name:"JSON",contains:t,keywords:e,illegal:"\\S"}}}()); +hljs.registerLanguage("kotlin",function(){"use strict";return function(e){var n={keyword:"abstract as val var vararg get set class object open private protected public noinline crossinline dynamic final enum if else do while for when throw try catch finally import package is in fun override companion reified inline lateinit init interface annotation data sealed internal infix operator out by constructor super tailrec where const inner suspend typealias external expect actual trait volatile transient native default",built_in:"Byte Short Char Int Long Boolean Float Double Void Unit Nothing",literal:"true false null"},a={className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"@"},i={className:"subst",begin:"\\${",end:"}",contains:[e.C_NUMBER_MODE]},s={className:"variable",begin:"\\$"+e.UNDERSCORE_IDENT_RE},t={className:"string",variants:[{begin:'"""',end:'"""(?=[^"])',contains:[s,i]},{begin:"'",end:"'",illegal:/\n/,contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"',illegal:/\n/,contains:[e.BACKSLASH_ESCAPE,s,i]}]};i.contains.push(t);var r={className:"meta",begin:"@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\s*:(?:\\s*"+e.UNDERSCORE_IDENT_RE+")?"},l={className:"meta",begin:"@"+e.UNDERSCORE_IDENT_RE,contains:[{begin:/\(/,end:/\)/,contains:[e.inherit(t,{className:"meta-string"})]}]},c=e.COMMENT("/\\*","\\*/",{contains:[e.C_BLOCK_COMMENT_MODE]}),o={variants:[{className:"type",begin:e.UNDERSCORE_IDENT_RE},{begin:/\(/,end:/\)/,contains:[]}]},d=o;return d.variants[1].contains=[o],o.variants[1].contains=[d],{name:"Kotlin",aliases:["kt"],keywords:n,contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag",begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,c,{className:"keyword",begin:/\b(break|continue|return|this)\b/,starts:{contains:[{className:"symbol",begin:/@\w+/}]}},a,r,l,{className:"function",beginKeywords:"fun",end:"[(]|$",returnBegin:!0,excludeEnd:!0,keywords:n,illegal:/fun\s+(<.*>)?[^\s\(]+(\s+[^\s\(]+)\s*=/,relevance:5,contains:[{begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0,contains:[e.UNDERSCORE_TITLE_MODE]},{className:"type",begin://,keywords:"reified",relevance:0},{className:"params",begin:/\(/,end:/\)/,endsParent:!0,keywords:n,relevance:0,contains:[{begin:/:/,end:/[=,\/]/,endsWithParent:!0,contains:[o,e.C_LINE_COMMENT_MODE,c],relevance:0},e.C_LINE_COMMENT_MODE,c,r,l,t,e.C_NUMBER_MODE]},c]},{className:"class",beginKeywords:"class interface trait",end:/[:\{(]|$/,excludeEnd:!0,illegal:"extends implements",contains:[{beginKeywords:"public protected internal private constructor"},e.UNDERSCORE_TITLE_MODE,{className:"type",begin://,excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:/[,:]\s*/,end:/[<\(,]|$/,excludeBegin:!0,returnEnd:!0},r,l]},t,{className:"meta",begin:"^#!/usr/bin/env",end:"$",illegal:"\n"},{className:"number",begin:"\\b(0[bB]([01]+[01_]+[01]+|[01]+)|0[xX]([a-fA-F0-9]+[a-fA-F0-9_]+[a-fA-F0-9]+|[a-fA-F0-9]+)|(([\\d]+[\\d_]+[\\d]+|[\\d]+)(\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))?|\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))([eE][-+]?\\d+)?)[lLfF]?",relevance:0}]}}}()); +hljs.registerLanguage("less",function(){"use strict";return function(e){var n="([\\w-]+|@{[\\w-]+})",a=[],s=[],t=function(e){return{className:"string",begin:"~?"+e+".*?"+e}},r=function(e,n,a){return{className:e,begin:n,relevance:a}},i={begin:"\\(",end:"\\)",contains:s,relevance:0};s.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,t("'"),t('"'),e.CSS_NUMBER_MODE,{begin:"(url|data-uri)\\(",starts:{className:"string",end:"[\\)\\n]",excludeEnd:!0}},r("number","#[0-9A-Fa-f]+\\b"),i,r("variable","@@?[\\w-]+",10),r("variable","@{[\\w-]+}"),r("built_in","~?`[^`]*?`"),{className:"attribute",begin:"[\\w-]+\\s*:",end:":",returnBegin:!0,excludeEnd:!0},{className:"meta",begin:"!important"});var c=s.concat({begin:"{",end:"}",contains:a}),l={beginKeywords:"when",endsWithParent:!0,contains:[{beginKeywords:"and not"}].concat(s)},o={begin:n+"\\s*:",returnBegin:!0,end:"[;}]",relevance:0,contains:[{className:"attribute",begin:n,end:":",excludeEnd:!0,starts:{endsWithParent:!0,illegal:"[<=$]",relevance:0,contains:s}}]},g={className:"keyword",begin:"@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b",starts:{end:"[;{}]",returnEnd:!0,contains:s,relevance:0}},d={className:"variable",variants:[{begin:"@[\\w-]+\\s*:",relevance:15},{begin:"@[\\w-]+"}],starts:{end:"[;}]",returnEnd:!0,contains:c}},b={variants:[{begin:"[\\.#:&\\[>]",end:"[;{}]"},{begin:n,end:"{"}],returnBegin:!0,returnEnd:!0,illegal:"[<='$\"]",relevance:0,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,l,r("keyword","all\\b"),r("variable","@{[\\w-]+}"),r("selector-tag",n+"%?",0),r("selector-id","#"+n),r("selector-class","\\."+n,0),r("selector-tag","&",0),{className:"selector-attr",begin:"\\[",end:"\\]"},{className:"selector-pseudo",begin:/:(:)?[a-zA-Z0-9\_\-\+\(\)"'.]+/},{begin:"\\(",end:"\\)",contains:c},{begin:"!important"}]};return a.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,g,d,o,b),{name:"Less",case_insensitive:!0,illegal:"[=>'/<($\"]",contains:a}}}()); +hljs.registerLanguage("lua",function(){"use strict";return function(e){var t={begin:"\\[=*\\[",end:"\\]=*\\]",contains:["self"]},a=[e.COMMENT("--(?!\\[=*\\[)","$"),e.COMMENT("--\\[=*\\[","\\]=*\\]",{contains:[t],relevance:10})];return{name:"Lua",keywords:{$pattern:e.UNDERSCORE_IDENT_RE,literal:"true false nil",keyword:"and break do else elseif end for goto if in local not or repeat return then until while",built_in:"_G _ENV _VERSION __index __newindex __mode __call __metatable __tostring __len __gc __add __sub __mul __div __mod __pow __concat __unm __eq __lt __le assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall arg self coroutine resume yield status wrap create running debug getupvalue debug sethook getmetatable gethook setmetatable setlocal traceback setfenv getinfo setupvalue getlocal getregistry getfenv io lines write close flush open output type read stderr stdin input stdout popen tmpfile math log max acos huge ldexp pi cos tanh pow deg tan cosh sinh random randomseed frexp ceil floor rad abs sqrt modf asin min mod fmod log10 atan2 exp sin atan os exit setlocale date getenv difftime remove time clock tmpname rename execute package preload loadlib loaded loaders cpath config path seeall string sub upper len gfind rep find match char dump gmatch reverse byte format gsub lower table setn insert getn foreachi maxn foreach concat sort remove"},contains:a.concat([{className:"function",beginKeywords:"function",end:"\\)",contains:[e.inherit(e.TITLE_MODE,{begin:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),{className:"params",begin:"\\(",endsWithParent:!0,contains:a}].concat(a)},e.C_NUMBER_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"string",begin:"\\[=*\\[",end:"\\]=*\\]",contains:[t],relevance:5}])}}}()); +hljs.registerLanguage("makefile",function(){"use strict";return function(e){var i={className:"variable",variants:[{begin:"\\$\\("+e.UNDERSCORE_IDENT_RE+"\\)",contains:[e.BACKSLASH_ESCAPE]},{begin:/\$[@%`]+/}]}]}]};return{name:"HTML, XML",aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"],case_insensitive:!0,contains:[{className:"meta",begin:"",relevance:10,contains:[a,i,t,s,{begin:"\\[",end:"\\]",contains:[{className:"meta",begin:"",contains:[a,s,i,t]}]}]},e.COMMENT("\x3c!--","--\x3e",{relevance:10}),{begin:"<\\!\\[CDATA\\[",end:"\\]\\]>",relevance:10},n,{className:"meta",begin:/<\?xml/,end:/\?>/,relevance:10},{className:"tag",begin:")",end:">",keywords:{name:"style"},contains:[c],starts:{end:"",returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",begin:")",end:">",keywords:{name:"script"},contains:[c],starts:{end:"<\/script>",returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{className:"tag",begin:"",contains:[{className:"name",begin:/[^\/><\s]+/,relevance:0},c]}]}}}()); +hljs.registerLanguage("markdown",function(){"use strict";return function(n){const e={begin:"<",end:">",subLanguage:"xml",relevance:0},a={begin:"\\[.+?\\][\\(\\[].*?[\\)\\]]",returnBegin:!0,contains:[{className:"string",begin:"\\[",end:"\\]",excludeBegin:!0,returnEnd:!0,relevance:0},{className:"link",begin:"\\]\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0},{className:"symbol",begin:"\\]\\[",end:"\\]",excludeBegin:!0,excludeEnd:!0}],relevance:10},i={className:"strong",contains:[],variants:[{begin:/_{2}/,end:/_{2}/},{begin:/\*{2}/,end:/\*{2}/}]},s={className:"emphasis",contains:[],variants:[{begin:/\*(?!\*)/,end:/\*/},{begin:/_(?!_)/,end:/_/,relevance:0}]};i.contains.push(s),s.contains.push(i);var c=[e,a];return i.contains=i.contains.concat(c),s.contains=s.contains.concat(c),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:c=c.concat(i,s)},{begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n",contains:c}]}]},e,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)",end:"\\s+",excludeEnd:!0},i,s,{className:"quote",begin:"^>\\s+",contains:c,end:"$"},{className:"code",variants:[{begin:"(`{3,})(.|\\n)*?\\1`*[ ]*"},{begin:"(~{3,})(.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))",contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{begin:"^[-\\*]{3,}",end:"$"},a,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}}}()); +hljs.registerLanguage("nginx",function(){"use strict";return function(e){var n={className:"variable",variants:[{begin:/\$\d+/},{begin:/\$\{/,end:/}/},{begin:"[\\$\\@]"+e.UNDERSCORE_IDENT_RE}]},a={endsWithParent:!0,keywords:{$pattern:"[a-z/_]+",literal:"on off yes no true false none blocked debug info notice warn error crit select break last permanent redirect kqueue rtsig epoll poll /dev/poll"},relevance:0,illegal:"=>",contains:[e.HASH_COMMENT_MODE,{className:"string",contains:[e.BACKSLASH_ESCAPE,n],variants:[{begin:/"/,end:/"/},{begin:/'/,end:/'/}]},{begin:"([a-z]+):/",end:"\\s",endsWithParent:!0,excludeEnd:!0,contains:[n]},{className:"regexp",contains:[e.BACKSLASH_ESCAPE,n],variants:[{begin:"\\s\\^",end:"\\s|{|;",returnEnd:!0},{begin:"~\\*?\\s+",end:"\\s|{|;",returnEnd:!0},{begin:"\\*(\\.[a-z\\-]+)+"},{begin:"([a-z\\-]+\\.)+\\*"}]},{className:"number",begin:"\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?\\b"},{className:"number",begin:"\\b\\d+[kKmMgGdshdwy]*\\b",relevance:0},n]};return{name:"Nginx config",aliases:["nginxconf"],contains:[e.HASH_COMMENT_MODE,{begin:e.UNDERSCORE_IDENT_RE+"\\s+{",returnBegin:!0,end:"{",contains:[{className:"section",begin:e.UNDERSCORE_IDENT_RE}],relevance:0},{begin:e.UNDERSCORE_IDENT_RE+"\\s",end:";|{",returnBegin:!0,contains:[{className:"attribute",begin:e.UNDERSCORE_IDENT_RE,starts:a}],relevance:0}],illegal:"[^\\s\\}]"}}}()); +hljs.registerLanguage("objectivec",function(){"use strict";return function(e){var n=/[a-zA-Z@][a-zA-Z0-9_]*/,_={$pattern:n,keyword:"@interface @class @protocol @implementation"};return{name:"Objective-C",aliases:["mm","objc","obj-c"],keywords:{$pattern:n,keyword:"int float while char export sizeof typedef const struct for union unsigned long volatile static bool mutable if do return goto void enum else break extern asm case short default double register explicit signed typename this switch continue wchar_t inline readonly assign readwrite self @synchronized id typeof nonatomic super unichar IBOutlet IBAction strong weak copy in out inout bycopy byref oneway __strong __weak __block __autoreleasing @private @protected @public @try @property @end @throw @catch @finally @autoreleasepool @synthesize @dynamic @selector @optional @required @encode @package @import @defs @compatibility_alias __bridge __bridge_transfer __bridge_retained __bridge_retain __covariant __contravariant __kindof _Nonnull _Nullable _Null_unspecified __FUNCTION__ __PRETTY_FUNCTION__ __attribute__ getter setter retain unsafe_unretained nonnull nullable null_unspecified null_resettable class instancetype NS_DESIGNATED_INITIALIZER NS_UNAVAILABLE NS_REQUIRES_SUPER NS_RETURNS_INNER_POINTER NS_INLINE NS_AVAILABLE NS_DEPRECATED NS_ENUM NS_OPTIONS NS_SWIFT_UNAVAILABLE NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END NS_REFINED_FOR_SWIFT NS_SWIFT_NAME NS_SWIFT_NOTHROW NS_DURING NS_HANDLER NS_ENDHANDLER NS_VALUERETURN NS_VOIDRETURN",literal:"false true FALSE TRUE nil YES NO NULL",built_in:"BOOL dispatch_once_t dispatch_queue_t dispatch_sync dispatch_async dispatch_once"},illegal:"/,end:/$/,illegal:"\\n"},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"class",begin:"("+_.keyword.split(" ").join("|")+")\\b",end:"({|$)",excludeEnd:!0,keywords:_,contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"\\."+e.UNDERSCORE_IDENT_RE,relevance:0}]}}}()); +hljs.registerLanguage("perl",function(){"use strict";return function(e){var n={$pattern:/[\w.]+/,keyword:"getpwent getservent quotemeta msgrcv scalar kill dbmclose undef lc ma syswrite tr send umask sysopen shmwrite vec qx utime local oct semctl localtime readpipe do return format read sprintf dbmopen pop getpgrp not getpwnam rewinddir qq fileno qw endprotoent wait sethostent bless s|0 opendir continue each sleep endgrent shutdown dump chomp connect getsockname die socketpair close flock exists index shmget sub for endpwent redo lstat msgctl setpgrp abs exit select print ref gethostbyaddr unshift fcntl syscall goto getnetbyaddr join gmtime symlink semget splice x|0 getpeername recv log setsockopt cos last reverse gethostbyname getgrnam study formline endhostent times chop length gethostent getnetent pack getprotoent getservbyname rand mkdir pos chmod y|0 substr endnetent printf next open msgsnd readdir use unlink getsockopt getpriority rindex wantarray hex system getservbyport endservent int chr untie rmdir prototype tell listen fork shmread ucfirst setprotoent else sysseek link getgrgid shmctl waitpid unpack getnetbyname reset chdir grep split require caller lcfirst until warn while values shift telldir getpwuid my getprotobynumber delete and sort uc defined srand accept package seekdir getprotobyname semop our rename seek if q|0 chroot sysread setpwent no crypt getc chown sqrt write setnetent setpriority foreach tie sin msgget map stat getlogin unless elsif truncate exec keys glob tied closedir ioctl socket readlink eval xor readline binmode setservent eof ord bind alarm pipe atan2 getgrent exp time push setgrent gt lt or ne m|0 break given say state when"},t={className:"subst",begin:"[$@]\\{",end:"\\}",keywords:n},s={begin:"->{",end:"}"},r={variants:[{begin:/\$\d/},{begin:/[\$%@](\^\w\b|#\w+(::\w+)*|{\w+}|\w+(::\w*)*)/},{begin:/[\$%@][^\s\w{]/,relevance:0}]},i=[e.BACKSLASH_ESCAPE,t,r],a=[r,e.HASH_COMMENT_MODE,e.COMMENT("^\\=\\w","\\=cut",{endsWithParent:!0}),s,{className:"string",contains:i,variants:[{begin:"q[qwxr]?\\s*\\(",end:"\\)",relevance:5},{begin:"q[qwxr]?\\s*\\[",end:"\\]",relevance:5},{begin:"q[qwxr]?\\s*\\{",end:"\\}",relevance:5},{begin:"q[qwxr]?\\s*\\|",end:"\\|",relevance:5},{begin:"q[qwxr]?\\s*\\<",end:"\\>",relevance:5},{begin:"qw\\s+q",end:"q",relevance:5},{begin:"'",end:"'",contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"'},{begin:"`",end:"`",contains:[e.BACKSLASH_ESCAPE]},{begin:"{\\w+}",contains:[],relevance:0},{begin:"-?\\w+\\s*\\=\\>",contains:[],relevance:0}]},{className:"number",begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",relevance:0},{begin:"(\\/\\/|"+e.RE_STARTERS_RE+"|\\b(split|return|print|reverse|grep)\\b)\\s*",keywords:"split return print reverse grep",relevance:0,contains:[e.HASH_COMMENT_MODE,{className:"regexp",begin:"(s|tr|y)/(\\\\.|[^/])*/(\\\\.|[^/])*/[a-z]*",relevance:10},{className:"regexp",begin:"(m|qr)?/",end:"/[a-z]*",contains:[e.BACKSLASH_ESCAPE],relevance:0}]},{className:"function",beginKeywords:"sub",end:"(\\s*\\(.*?\\))?[;{]",excludeEnd:!0,relevance:5,contains:[e.TITLE_MODE]},{begin:"-\\w\\b",relevance:0},{begin:"^__DATA__$",end:"^__END__$",subLanguage:"mojolicious",contains:[{begin:"^@@.*",end:"$",className:"comment"}]}];return t.contains=a,s.contains=a,{name:"Perl",aliases:["pl","pm"],keywords:n,contains:a}}}()); +hljs.registerLanguage("php",function(){"use strict";return function(e){var r={begin:"\\$+[a-zA-Z_-ÿ][a-zA-Z0-9_-ÿ]*"},t={className:"meta",variants:[{begin:/<\?php/,relevance:10},{begin:/<\?[=]?/},{begin:/\?>/}]},a={className:"string",contains:[e.BACKSLASH_ESCAPE,t],variants:[{begin:'b"',end:'"'},{begin:"b'",end:"'"},e.inherit(e.APOS_STRING_MODE,{illegal:null}),e.inherit(e.QUOTE_STRING_MODE,{illegal:null})]},n={variants:[e.BINARY_NUMBER_MODE,e.C_NUMBER_MODE]},i={keyword:"__CLASS__ __DIR__ __FILE__ __FUNCTION__ __LINE__ __METHOD__ __NAMESPACE__ __TRAIT__ die echo exit include include_once print require require_once array abstract and as binary bool boolean break callable case catch class clone const continue declare default do double else elseif empty enddeclare endfor endforeach endif endswitch endwhile eval extends final finally float for foreach from global goto if implements instanceof insteadof int integer interface isset iterable list new object or private protected public real return string switch throw trait try unset use var void while xor yield",literal:"false null true",built_in:"Error|0 AppendIterator ArgumentCountError ArithmeticError ArrayIterator ArrayObject AssertionError BadFunctionCallException BadMethodCallException CachingIterator CallbackFilterIterator CompileError Countable DirectoryIterator DivisionByZeroError DomainException EmptyIterator ErrorException Exception FilesystemIterator FilterIterator GlobIterator InfiniteIterator InvalidArgumentException IteratorIterator LengthException LimitIterator LogicException MultipleIterator NoRewindIterator OutOfBoundsException OutOfRangeException OuterIterator OverflowException ParentIterator ParseError RangeException RecursiveArrayIterator RecursiveCachingIterator RecursiveCallbackFilterIterator RecursiveDirectoryIterator RecursiveFilterIterator RecursiveIterator RecursiveIteratorIterator RecursiveRegexIterator RecursiveTreeIterator RegexIterator RuntimeException SeekableIterator SplDoublyLinkedList SplFileInfo SplFileObject SplFixedArray SplHeap SplMaxHeap SplMinHeap SplObjectStorage SplObserver SplObserver SplPriorityQueue SplQueue SplStack SplSubject SplSubject SplTempFileObject TypeError UnderflowException UnexpectedValueException ArrayAccess Closure Generator Iterator IteratorAggregate Serializable Throwable Traversable WeakReference Directory __PHP_Incomplete_Class parent php_user_filter self static stdClass"};return{aliases:["php","php3","php4","php5","php6","php7"],case_insensitive:!0,keywords:i,contains:[e.HASH_COMMENT_MODE,e.COMMENT("//","$",{contains:[t]}),e.COMMENT("/\\*","\\*/",{contains:[{className:"doctag",begin:"@[A-Za-z]+"}]}),e.COMMENT("__halt_compiler.+?;",!1,{endsWithParent:!0,keywords:"__halt_compiler"}),{className:"string",begin:/<<<['"]?\w+['"]?$/,end:/^\w+;?$/,contains:[e.BACKSLASH_ESCAPE,{className:"subst",variants:[{begin:/\$\w+/},{begin:/\{\$/,end:/\}/}]}]},t,{className:"keyword",begin:/\$this\b/},r,{begin:/(::|->)+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/},{className:"function",beginKeywords:"fn function",end:/[;{]/,excludeEnd:!0,illegal:"[$%\\[]",contains:[e.UNDERSCORE_TITLE_MODE,{className:"params",begin:"\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0,keywords:i,contains:["self",r,e.C_BLOCK_COMMENT_MODE,a,n]}]},{className:"class",beginKeywords:"class interface",end:"{",excludeEnd:!0,illegal:/[:\(\$"]/,contains:[{beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"namespace",end:";",illegal:/[\.']/,contains:[e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"use",end:";",contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"=>"},a,n]}}}()); +hljs.registerLanguage("php-template",function(){"use strict";return function(n){return{name:"PHP template",subLanguage:"xml",contains:[{begin:/<\?(php|=)?/,end:/\?>/,subLanguage:"php",contains:[{begin:"/\\*",end:"\\*/",skip:!0},{begin:'b"',end:'"',skip:!0},{begin:"b'",end:"'",skip:!0},n.inherit(n.APOS_STRING_MODE,{illegal:null,className:null,contains:null,skip:!0}),n.inherit(n.QUOTE_STRING_MODE,{illegal:null,className:null,contains:null,skip:!0})]}]}}}()); +hljs.registerLanguage("plaintext",function(){"use strict";return function(t){return{name:"Plain text",aliases:["text","txt"],disableAutodetect:!0}}}()); +hljs.registerLanguage("properties",function(){"use strict";return function(e){var n="[ \\t\\f]*",t="("+n+"[:=]"+n+"|[ \\t\\f]+)",a="([^\\\\:= \\t\\f\\n]|\\\\.)+",s={end:t,relevance:0,starts:{className:"string",end:/$/,relevance:0,contains:[{begin:"\\\\\\n"}]}};return{name:".properties",case_insensitive:!0,illegal:/\S/,contains:[e.COMMENT("^\\s*[!#]","$"),{begin:"([^\\\\\\W:= \\t\\f\\n]|\\\\.)+"+t,returnBegin:!0,contains:[{className:"attr",begin:"([^\\\\\\W:= \\t\\f\\n]|\\\\.)+",endsParent:!0,relevance:0}],starts:s},{begin:a+t,returnBegin:!0,relevance:0,contains:[{className:"meta",begin:a,endsParent:!0,relevance:0}],starts:s},{className:"attr",relevance:0,begin:a+n+"$"}]}}}()); +hljs.registerLanguage("python",function(){"use strict";return function(e){var n={keyword:"and elif is global as in if from raise for except finally print import pass return exec else break not with class assert yield try while continue del or def lambda async await nonlocal|10",built_in:"Ellipsis NotImplemented",literal:"False None True"},a={className:"meta",begin:/^(>>>|\.\.\.) /},i={className:"subst",begin:/\{/,end:/\}/,keywords:n,illegal:/#/},s={begin:/\{\{/,relevance:0},r={className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:/(u|b)?r?'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,a],relevance:10},{begin:/(u|b)?r?"""/,end:/"""/,contains:[e.BACKSLASH_ESCAPE,a],relevance:10},{begin:/(fr|rf|f)'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,a,s,i]},{begin:/(fr|rf|f)"""/,end:/"""/,contains:[e.BACKSLASH_ESCAPE,a,s,i]},{begin:/(u|r|ur)'/,end:/'/,relevance:10},{begin:/(u|r|ur)"/,end:/"/,relevance:10},{begin:/(b|br)'/,end:/'/},{begin:/(b|br)"/,end:/"/},{begin:/(fr|rf|f)'/,end:/'/,contains:[e.BACKSLASH_ESCAPE,s,i]},{begin:/(fr|rf|f)"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,s,i]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},l={className:"number",relevance:0,variants:[{begin:e.BINARY_NUMBER_RE+"[lLjJ]?"},{begin:"\\b(0o[0-7]+)[lLjJ]?"},{begin:e.C_NUMBER_RE+"[lLjJ]?"}]},t={className:"params",variants:[{begin:/\(\s*\)/,skip:!0,className:null},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,contains:["self",a,l,r,e.HASH_COMMENT_MODE]}]};return i.contains=[r,l,a],{name:"Python",aliases:["py","gyp","ipython"],keywords:n,illegal:/(<\/|->|\?)|=>/,contains:[a,l,{beginKeywords:"if",relevance:0},r,e.HASH_COMMENT_MODE,{variants:[{className:"function",beginKeywords:"def"},{className:"class",beginKeywords:"class"}],end:/:/,illegal:/[${=;\n,]/,contains:[e.UNDERSCORE_TITLE_MODE,t,{begin:/->/,endsWithParent:!0,keywords:"None"}]},{className:"meta",begin:/^[\t ]*@/,end:/$/},{begin:/\b(print|exec)\(/}]}}}()); +hljs.registerLanguage("python-repl",function(){"use strict";return function(n){return{aliases:["pycon"],contains:[{className:"meta",starts:{end:/ |$/,starts:{end:"$",subLanguage:"python"}},variants:[{begin:/^>>>(?=[ ]|$)/},{begin:/^\.\.\.(?=[ ]|$)/}]}]}}}()); +hljs.registerLanguage("ruby",function(){"use strict";return function(e){var n="[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?",a={keyword:"and then defined module in return redo if BEGIN retry end for self when next until do begin unless END rescue else break undef not super class case require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor",literal:"true false nil"},s={className:"doctag",begin:"@[A-Za-z]+"},i={begin:"#<",end:">"},r=[e.COMMENT("#","$",{contains:[s]}),e.COMMENT("^\\=begin","^\\=end",{contains:[s],relevance:10}),e.COMMENT("^__END__","\\n$")],c={className:"subst",begin:"#\\{",end:"}",keywords:a},t={className:"string",contains:[e.BACKSLASH_ESCAPE,c],variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/`/,end:/`/},{begin:"%[qQwWx]?\\(",end:"\\)"},{begin:"%[qQwWx]?\\[",end:"\\]"},{begin:"%[qQwWx]?{",end:"}"},{begin:"%[qQwWx]?<",end:">"},{begin:"%[qQwWx]?/",end:"/"},{begin:"%[qQwWx]?%",end:"%"},{begin:"%[qQwWx]?-",end:"-"},{begin:"%[qQwWx]?\\|",end:"\\|"},{begin:/\B\?(\\\d{1,3}|\\x[A-Fa-f0-9]{1,2}|\\u[A-Fa-f0-9]{4}|\\?\S)\b/},{begin:/<<[-~]?'?(\w+)(?:.|\n)*?\n\s*\1\b/,returnBegin:!0,contains:[{begin:/<<[-~]?'?/},e.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/,contains:[e.BACKSLASH_ESCAPE,c]})]}]},b={className:"params",begin:"\\(",end:"\\)",endsParent:!0,keywords:a},d=[t,i,{className:"class",beginKeywords:"class module",end:"$|;",illegal:/=/,contains:[e.inherit(e.TITLE_MODE,{begin:"[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?"}),{begin:"<\\s*",contains:[{begin:"("+e.IDENT_RE+"::)?"+e.IDENT_RE}]}].concat(r)},{className:"function",beginKeywords:"def",end:"$|;",contains:[e.inherit(e.TITLE_MODE,{begin:n}),b].concat(r)},{begin:e.IDENT_RE+"::"},{className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"(\\!|\\?)?:",relevance:0},{className:"symbol",begin:":(?!\\s)",contains:[t,{begin:n}],relevance:0},{className:"number",begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",relevance:0},{begin:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},{className:"params",begin:/\|/,end:/\|/,keywords:a},{begin:"("+e.RE_STARTERS_RE+"|unless)\\s*",keywords:"unless",contains:[i,{className:"regexp",contains:[e.BACKSLASH_ESCAPE,c],illegal:/\n/,variants:[{begin:"/",end:"/[a-z]*"},{begin:"%r{",end:"}[a-z]*"},{begin:"%r\\(",end:"\\)[a-z]*"},{begin:"%r!",end:"![a-z]*"},{begin:"%r\\[",end:"\\][a-z]*"}]}].concat(r),relevance:0}].concat(r);c.contains=d,b.contains=d;var g=[{begin:/^\s*=>/,starts:{end:"$",contains:d}},{className:"meta",begin:"^([>?]>|[\\w#]+\\(\\w+\\):\\d+:\\d+>|(\\w+-)?\\d+\\.\\d+\\.\\d(p\\d+)?[^>]+>)",starts:{end:"$",contains:d}}];return{name:"Ruby",aliases:["rb","gemspec","podspec","thor","irb"],keywords:a,illegal:/\/\*/,contains:r.concat(g).concat(d)}}}()); +hljs.registerLanguage("rust",function(){"use strict";return function(e){var n="([ui](8|16|32|64|128|size)|f(32|64))?",t="drop i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize f32 f64 str char bool Box Option Result String Vec Copy Send Sized Sync Drop Fn FnMut FnOnce ToOwned Clone Debug PartialEq PartialOrd Eq Ord AsRef AsMut Into From Default Iterator Extend IntoIterator DoubleEndedIterator ExactSizeIterator SliceConcatExt ToString assert! assert_eq! bitflags! bytes! cfg! col! concat! concat_idents! debug_assert! debug_assert_eq! env! panic! file! format! format_args! include_bin! include_str! line! local_data_key! module_path! option_env! print! println! select! stringify! try! unimplemented! unreachable! vec! write! writeln! macro_rules! assert_ne! debug_assert_ne!";return{name:"Rust",aliases:["rs"],keywords:{$pattern:e.IDENT_RE+"!?",keyword:"abstract as async await become box break const continue crate do dyn else enum extern false final fn for if impl in let loop macro match mod move mut override priv pub ref return self Self static struct super trait true try type typeof unsafe unsized use virtual where while yield",literal:"true false Some None Ok Err",built_in:t},illegal:""}]}}}()); +hljs.registerLanguage("scss",function(){"use strict";return function(e){var t={className:"variable",begin:"(\\$[a-zA-Z-][a-zA-Z0-9_-]*)\\b"},i={className:"number",begin:"#[0-9A-Fa-f]+"};return e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,e.C_BLOCK_COMMENT_MODE,{name:"SCSS",case_insensitive:!0,illegal:"[=/|']",contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"selector-id",begin:"\\#[A-Za-z0-9_-]+",relevance:0},{className:"selector-class",begin:"\\.[A-Za-z0-9_-]+",relevance:0},{className:"selector-attr",begin:"\\[",end:"\\]",illegal:"$"},{className:"selector-tag",begin:"\\b(a|abbr|acronym|address|area|article|aside|audio|b|base|big|blockquote|body|br|button|canvas|caption|cite|code|col|colgroup|command|datalist|dd|del|details|dfn|div|dl|dt|em|embed|fieldset|figcaption|figure|footer|form|frame|frameset|(h[1-6])|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|keygen|label|legend|li|link|map|mark|meta|meter|nav|noframes|noscript|object|ol|optgroup|option|output|p|param|pre|progress|q|rp|rt|ruby|samp|script|section|select|small|span|strike|strong|style|sub|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|tt|ul|var|video)\\b",relevance:0},{className:"selector-pseudo",begin:":(visited|valid|root|right|required|read-write|read-only|out-range|optional|only-of-type|only-child|nth-of-type|nth-last-of-type|nth-last-child|nth-child|not|link|left|last-of-type|last-child|lang|invalid|indeterminate|in-range|hover|focus|first-of-type|first-line|first-letter|first-child|first|enabled|empty|disabled|default|checked|before|after|active)"},{className:"selector-pseudo",begin:"::(after|before|choices|first-letter|first-line|repeat-index|repeat-item|selection|value)"},t,{className:"attribute",begin:"\\b(src|z-index|word-wrap|word-spacing|word-break|width|widows|white-space|visibility|vertical-align|unicode-bidi|transition-timing-function|transition-property|transition-duration|transition-delay|transition|transform-style|transform-origin|transform|top|text-underline-position|text-transform|text-shadow|text-rendering|text-overflow|text-indent|text-decoration-style|text-decoration-line|text-decoration-color|text-decoration|text-align-last|text-align|tab-size|table-layout|right|resize|quotes|position|pointer-events|perspective-origin|perspective|page-break-inside|page-break-before|page-break-after|padding-top|padding-right|padding-left|padding-bottom|padding|overflow-y|overflow-x|overflow-wrap|overflow|outline-width|outline-style|outline-offset|outline-color|outline|orphans|order|opacity|object-position|object-fit|normal|none|nav-up|nav-right|nav-left|nav-index|nav-down|min-width|min-height|max-width|max-height|mask|marks|margin-top|margin-right|margin-left|margin-bottom|margin|list-style-type|list-style-position|list-style-image|list-style|line-height|letter-spacing|left|justify-content|initial|inherit|ime-mode|image-orientation|image-resolution|image-rendering|icon|hyphens|height|font-weight|font-variant-ligatures|font-variant|font-style|font-stretch|font-size-adjust|font-size|font-language-override|font-kerning|font-feature-settings|font-family|font|float|flex-wrap|flex-shrink|flex-grow|flex-flow|flex-direction|flex-basis|flex|filter|empty-cells|display|direction|cursor|counter-reset|counter-increment|content|column-width|column-span|column-rule-width|column-rule-style|column-rule-color|column-rule|column-gap|column-fill|column-count|columns|color|clip-path|clip|clear|caption-side|break-inside|break-before|break-after|box-sizing|box-shadow|box-decoration-break|bottom|border-width|border-top-width|border-top-style|border-top-right-radius|border-top-left-radius|border-top-color|border-top|border-style|border-spacing|border-right-width|border-right-style|border-right-color|border-right|border-radius|border-left-width|border-left-style|border-left-color|border-left|border-image-width|border-image-source|border-image-slice|border-image-repeat|border-image-outset|border-image|border-color|border-collapse|border-bottom-width|border-bottom-style|border-bottom-right-radius|border-bottom-left-radius|border-bottom-color|border-bottom|border|background-size|background-repeat|background-position|background-origin|background-image|background-color|background-clip|background-attachment|background-blend-mode|background|backface-visibility|auto|animation-timing-function|animation-play-state|animation-name|animation-iteration-count|animation-fill-mode|animation-duration|animation-direction|animation-delay|animation|align-self|align-items|align-content)\\b",illegal:"[^\\s]"},{begin:"\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\b"},{begin:":",end:";",contains:[t,i,e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,{className:"meta",begin:"!important"}]},{begin:"@(page|font-face)",lexemes:"@[a-z-]+",keywords:"@page @font-face"},{begin:"@",end:"[{;]",returnBegin:!0,keywords:"and or not only",contains:[{begin:"@[a-z-]+",className:"keyword"},t,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,i,e.CSS_NUMBER_MODE]}]}}}()); +hljs.registerLanguage("shell",function(){"use strict";return function(s){return{name:"Shell Session",aliases:["console"],contains:[{className:"meta",begin:"^\\s{0,3}[/\\w\\d\\[\\]()@-]*[>%$#]",starts:{end:"$",subLanguage:"bash"}}]}}}()); +hljs.registerLanguage("sql",function(){"use strict";return function(e){var t=e.COMMENT("--","$");return{name:"SQL",case_insensitive:!0,illegal:/[<>{}*]/,contains:[{beginKeywords:"begin end start commit rollback savepoint lock alter create drop rename call delete do handler insert load replace select truncate update set show pragma grant merge describe use explain help declare prepare execute deallocate release unlock purge reset change stop analyze cache flush optimize repair kill install uninstall checksum restore check backup revoke comment values with",end:/;/,endsWithParent:!0,keywords:{$pattern:/[\w\.]+/,keyword:"as abort abs absolute acc acce accep accept access accessed accessible account acos action activate add addtime admin administer advanced advise aes_decrypt aes_encrypt after agent aggregate ali alia alias all allocate allow alter always analyze ancillary and anti any anydata anydataset anyschema anytype apply archive archived archivelog are as asc ascii asin assembly assertion associate asynchronous at atan atn2 attr attri attrib attribu attribut attribute attributes audit authenticated authentication authid authors auto autoallocate autodblink autoextend automatic availability avg backup badfile basicfile before begin beginning benchmark between bfile bfile_base big bigfile bin binary_double binary_float binlog bit_and bit_count bit_length bit_or bit_xor bitmap blob_base block blocksize body both bound bucket buffer_cache buffer_pool build bulk by byte byteordermark bytes cache caching call calling cancel capacity cascade cascaded case cast catalog category ceil ceiling chain change changed char_base char_length character_length characters characterset charindex charset charsetform charsetid check checksum checksum_agg child choose chr chunk class cleanup clear client clob clob_base clone close cluster_id cluster_probability cluster_set clustering coalesce coercibility col collate collation collect colu colum column column_value columns columns_updated comment commit compact compatibility compiled complete composite_limit compound compress compute concat concat_ws concurrent confirm conn connec connect connect_by_iscycle connect_by_isleaf connect_by_root connect_time connection consider consistent constant constraint constraints constructor container content contents context contributors controlfile conv convert convert_tz corr corr_k corr_s corresponding corruption cos cost count count_big counted covar_pop covar_samp cpu_per_call cpu_per_session crc32 create creation critical cross cube cume_dist curdate current current_date current_time current_timestamp current_user cursor curtime customdatum cycle data database databases datafile datafiles datalength date_add date_cache date_format date_sub dateadd datediff datefromparts datename datepart datetime2fromparts day day_to_second dayname dayofmonth dayofweek dayofyear days db_role_change dbtimezone ddl deallocate declare decode decompose decrement decrypt deduplicate def defa defau defaul default defaults deferred defi defin define degrees delayed delegate delete delete_all delimited demand dense_rank depth dequeue des_decrypt des_encrypt des_key_file desc descr descri describ describe descriptor deterministic diagnostics difference dimension direct_load directory disable disable_all disallow disassociate discardfile disconnect diskgroup distinct distinctrow distribute distributed div do document domain dotnet double downgrade drop dumpfile duplicate duration each edition editionable editions element ellipsis else elsif elt empty enable enable_all enclosed encode encoding encrypt end end-exec endian enforced engine engines enqueue enterprise entityescaping eomonth error errors escaped evalname evaluate event eventdata events except exception exceptions exchange exclude excluding execu execut execute exempt exists exit exp expire explain explode export export_set extended extent external external_1 external_2 externally extract failed failed_login_attempts failover failure far fast feature_set feature_value fetch field fields file file_name_convert filesystem_like_logging final finish first first_value fixed flash_cache flashback floor flush following follows for forall force foreign form forma format found found_rows freelist freelists freepools fresh from from_base64 from_days ftp full function general generated get get_format get_lock getdate getutcdate global global_name globally go goto grant grants greatest group group_concat group_id grouping grouping_id groups gtid_subtract guarantee guard handler hash hashkeys having hea head headi headin heading heap help hex hierarchy high high_priority hosts hour hours http id ident_current ident_incr ident_seed identified identity idle_time if ifnull ignore iif ilike ilm immediate import in include including increment index indexes indexing indextype indicator indices inet6_aton inet6_ntoa inet_aton inet_ntoa infile initial initialized initially initrans inmemory inner innodb input insert install instance instantiable instr interface interleaved intersect into invalidate invisible is is_free_lock is_ipv4 is_ipv4_compat is_not is_not_null is_used_lock isdate isnull isolation iterate java join json json_exists keep keep_duplicates key keys kill language large last last_day last_insert_id last_value lateral lax lcase lead leading least leaves left len lenght length less level levels library like like2 like4 likec limit lines link list listagg little ln load load_file lob lobs local localtime localtimestamp locate locator lock locked log log10 log2 logfile logfiles logging logical logical_reads_per_call logoff logon logs long loop low low_priority lower lpad lrtrim ltrim main make_set makedate maketime managed management manual map mapping mask master master_pos_wait match matched materialized max maxextents maximize maxinstances maxlen maxlogfiles maxloghistory maxlogmembers maxsize maxtrans md5 measures median medium member memcompress memory merge microsecond mid migration min minextents minimum mining minus minute minutes minvalue missing mod mode model modification modify module monitoring month months mount move movement multiset mutex name name_const names nan national native natural nav nchar nclob nested never new newline next nextval no no_write_to_binlog noarchivelog noaudit nobadfile nocheck nocompress nocopy nocycle nodelay nodiscardfile noentityescaping noguarantee nokeep nologfile nomapping nomaxvalue nominimize nominvalue nomonitoring none noneditionable nonschema noorder nopr nopro noprom nopromp noprompt norely noresetlogs noreverse normal norowdependencies noschemacheck noswitch not nothing notice notnull notrim novalidate now nowait nth_value nullif nulls num numb numbe nvarchar nvarchar2 object ocicoll ocidate ocidatetime ociduration ociinterval ociloblocator ocinumber ociref ocirefcursor ocirowid ocistring ocitype oct octet_length of off offline offset oid oidindex old on online only opaque open operations operator optimal optimize option optionally or oracle oracle_date oradata ord ordaudio orddicom orddoc order ordimage ordinality ordvideo organization orlany orlvary out outer outfile outline output over overflow overriding package pad parallel parallel_enable parameters parent parse partial partition partitions pascal passing password password_grace_time password_lock_time password_reuse_max password_reuse_time password_verify_function patch path patindex pctincrease pctthreshold pctused pctversion percent percent_rank percentile_cont percentile_disc performance period period_add period_diff permanent physical pi pipe pipelined pivot pluggable plugin policy position post_transaction pow power pragma prebuilt precedes preceding precision prediction prediction_cost prediction_details prediction_probability prediction_set prepare present preserve prior priority private private_sga privileges procedural procedure procedure_analyze processlist profiles project prompt protection public publishingservername purge quarter query quick quiesce quota quotename radians raise rand range rank raw read reads readsize rebuild record records recover recovery recursive recycle redo reduced ref reference referenced references referencing refresh regexp_like register regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy reject rekey relational relative relaylog release release_lock relies_on relocate rely rem remainder rename repair repeat replace replicate replication required reset resetlogs resize resource respect restore restricted result result_cache resumable resume retention return returning returns reuse reverse revoke right rlike role roles rollback rolling rollup round row row_count rowdependencies rowid rownum rows rtrim rules safe salt sample save savepoint sb1 sb2 sb4 scan schema schemacheck scn scope scroll sdo_georaster sdo_topo_geometry search sec_to_time second seconds section securefile security seed segment select self semi sequence sequential serializable server servererror session session_user sessions_per_user set sets settings sha sha1 sha2 share shared shared_pool short show shrink shutdown si_averagecolor si_colorhistogram si_featurelist si_positionalcolor si_stillimage si_texture siblings sid sign sin size size_t sizes skip slave sleep smalldatetimefromparts smallfile snapshot some soname sort soundex source space sparse spfile split sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_small_result sql_variant_property sqlcode sqldata sqlerror sqlname sqlstate sqrt square standalone standby start starting startup statement static statistics stats_binomial_test stats_crosstab stats_ks_test stats_mode stats_mw_test stats_one_way_anova stats_t_test_ stats_t_test_indep stats_t_test_one stats_t_test_paired stats_wsr_test status std stddev stddev_pop stddev_samp stdev stop storage store stored str str_to_date straight_join strcmp strict string struct stuff style subdate subpartition subpartitions substitutable substr substring subtime subtring_index subtype success sum suspend switch switchoffset switchover sync synchronous synonym sys sys_xmlagg sysasm sysaux sysdate sysdatetimeoffset sysdba sysoper system system_user sysutcdatetime table tables tablespace tablesample tan tdo template temporary terminated tertiary_weights test than then thread through tier ties time time_format time_zone timediff timefromparts timeout timestamp timestampadd timestampdiff timezone_abbr timezone_minute timezone_region to to_base64 to_date to_days to_seconds todatetimeoffset trace tracking transaction transactional translate translation treat trigger trigger_nestlevel triggers trim truncate try_cast try_convert try_parse type ub1 ub2 ub4 ucase unarchived unbounded uncompress under undo unhex unicode uniform uninstall union unique unix_timestamp unknown unlimited unlock unnest unpivot unrecoverable unsafe unsigned until untrusted unusable unused update updated upgrade upped upper upsert url urowid usable usage use use_stored_outlines user user_data user_resources users using utc_date utc_timestamp uuid uuid_short validate validate_password_strength validation valist value values var var_samp varcharc vari varia variab variabl variable variables variance varp varraw varrawc varray verify version versions view virtual visible void wait wallet warning warnings week weekday weekofyear wellformed when whene whenev wheneve whenever where while whitespace window with within without work wrapped xdb xml xmlagg xmlattributes xmlcast xmlcolattval xmlelement xmlexists xmlforest xmlindex xmlnamespaces xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltype xor year year_to_month years yearweek",literal:"true false null unknown",built_in:"array bigint binary bit blob bool boolean char character date dec decimal float int int8 integer interval number numeric real record serial serial8 smallint text time timestamp tinyint varchar varchar2 varying void"},contains:[{className:"string",begin:"'",end:"'",contains:[{begin:"''"}]},{className:"string",begin:'"',end:'"',contains:[{begin:'""'}]},{className:"string",begin:"`",end:"`"},e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE,t,e.HASH_COMMENT_MODE]},e.C_BLOCK_COMMENT_MODE,t,e.HASH_COMMENT_MODE]}}}()); +hljs.registerLanguage("swift",function(){"use strict";return function(e){var i={keyword:"#available #colorLiteral #column #else #elseif #endif #file #fileLiteral #function #if #imageLiteral #line #selector #sourceLocation _ __COLUMN__ __FILE__ __FUNCTION__ __LINE__ Any as as! as? associatedtype associativity break case catch class continue convenience default defer deinit didSet do dynamic dynamicType else enum extension fallthrough false fileprivate final for func get guard if import in indirect infix init inout internal is lazy left let mutating nil none nonmutating open operator optional override postfix precedence prefix private protocol Protocol public repeat required rethrows return right self Self set static struct subscript super switch throw throws true try try! try? Type typealias unowned var weak where while willSet",literal:"true false nil",built_in:"abs advance alignof alignofValue anyGenerator assert assertionFailure bridgeFromObjectiveC bridgeFromObjectiveCUnconditional bridgeToObjectiveC bridgeToObjectiveCUnconditional c compactMap contains count countElements countLeadingZeros debugPrint debugPrintln distance dropFirst dropLast dump encodeBitsAsWords enumerate equal fatalError filter find getBridgedObjectiveCType getVaList indices insertionSort isBridgedToObjectiveC isBridgedVerbatimToObjectiveC isUniquelyReferenced isUniquelyReferencedNonObjC join lazy lexicographicalCompare map max maxElement min minElement numericCast overlaps partition posix precondition preconditionFailure print println quickSort readLine reduce reflect reinterpretCast reverse roundUpToAlignment sizeof sizeofValue sort split startsWith stride strideof strideofValue swap toString transcode underestimateCount unsafeAddressOf unsafeBitCast unsafeDowncast unsafeUnwrap unsafeReflect withExtendedLifetime withObjectAtPlusZero withUnsafePointer withUnsafePointerToObject withUnsafeMutablePointer withUnsafeMutablePointers withUnsafePointer withUnsafePointers withVaList zip"},n=e.COMMENT("/\\*","\\*/",{contains:["self"]}),t={className:"subst",begin:/\\\(/,end:"\\)",keywords:i,contains:[]},a={className:"string",contains:[e.BACKSLASH_ESCAPE,t],variants:[{begin:/"""/,end:/"""/},{begin:/"/,end:/"/}]},r={className:"number",begin:"\\b([\\d_]+(\\.[\\deE_]+)?|0x[a-fA-F0-9_]+(\\.[a-fA-F0-9p_]+)?|0b[01_]+|0o[0-7_]+)\\b",relevance:0};return t.contains=[r],{name:"Swift",keywords:i,contains:[a,e.C_LINE_COMMENT_MODE,n,{className:"type",begin:"\\b[A-Z][\\wÀ-ʸ']*[!?]"},{className:"type",begin:"\\b[A-Z][\\wÀ-ʸ']*",relevance:0},r,{className:"function",beginKeywords:"func",end:"{",excludeEnd:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/[A-Za-z$_][0-9A-Za-z$_]*/}),{begin://},{className:"params",begin:/\(/,end:/\)/,endsParent:!0,keywords:i,contains:["self",r,a,e.C_BLOCK_COMMENT_MODE,{begin:":"}],illegal:/["']/}],illegal:/\[|%/},{className:"class",beginKeywords:"struct protocol class extension enum",keywords:i,end:"\\{",excludeEnd:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/})]},{className:"meta",begin:"(@discardableResult|@warn_unused_result|@exported|@lazy|@noescape|@NSCopying|@NSManaged|@objc|@objcMembers|@convention|@required|@noreturn|@IBAction|@IBDesignable|@IBInspectable|@IBOutlet|@infix|@prefix|@postfix|@autoclosure|@testable|@available|@nonobjc|@NSApplicationMain|@UIApplicationMain|@dynamicMemberLookup|@propertyWrapper)\\b"},{beginKeywords:"import",end:/$/,contains:[e.C_LINE_COMMENT_MODE,n]}]}}}()); +hljs.registerLanguage("typescript",function(){"use strict";const e=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],n=["true","false","null","undefined","NaN","Infinity"],a=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]);return function(r){var t={$pattern:"[A-Za-z$_][0-9A-Za-z$_]*",keyword:e.concat(["type","namespace","typedef","interface","public","private","protected","implements","declare","abstract","readonly"]).join(" "),literal:n.join(" "),built_in:a.concat(["any","void","number","boolean","string","object","never","enum"]).join(" ")},s={className:"meta",begin:"@[A-Za-z$_][0-9A-Za-z$_]*"},i={className:"number",variants:[{begin:"\\b(0[bB][01]+)n?"},{begin:"\\b(0[oO][0-7]+)n?"},{begin:r.C_NUMBER_RE+"n?"}],relevance:0},o={className:"subst",begin:"\\$\\{",end:"\\}",keywords:t,contains:[]},c={begin:"html`",end:"",starts:{end:"`",returnEnd:!1,contains:[r.BACKSLASH_ESCAPE,o],subLanguage:"xml"}},l={begin:"css`",end:"",starts:{end:"`",returnEnd:!1,contains:[r.BACKSLASH_ESCAPE,o],subLanguage:"css"}},E={className:"string",begin:"`",end:"`",contains:[r.BACKSLASH_ESCAPE,o]};o.contains=[r.APOS_STRING_MODE,r.QUOTE_STRING_MODE,c,l,E,i,r.REGEXP_MODE];var d={begin:"\\(",end:/\)/,keywords:t,contains:["self",r.QUOTE_STRING_MODE,r.APOS_STRING_MODE,r.NUMBER_MODE]},u={className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:t,contains:[r.C_LINE_COMMENT_MODE,r.C_BLOCK_COMMENT_MODE,s,d]};return{name:"TypeScript",aliases:["ts"],keywords:t,contains:[r.SHEBANG(),{className:"meta",begin:/^\s*['"]use strict['"]/},r.APOS_STRING_MODE,r.QUOTE_STRING_MODE,c,l,E,r.C_LINE_COMMENT_MODE,r.C_BLOCK_COMMENT_MODE,i,{begin:"("+r.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",keywords:"return throw case",contains:[r.C_LINE_COMMENT_MODE,r.C_BLOCK_COMMENT_MODE,r.REGEXP_MODE,{className:"function",begin:"(\\([^(]*(\\([^(]*(\\([^(]*\\))?\\))?\\)|"+r.UNDERSCORE_IDENT_RE+")\\s*=>",returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:r.UNDERSCORE_IDENT_RE},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:t,contains:d.contains}]}]}],relevance:0},{className:"function",beginKeywords:"function",end:/[\{;]/,excludeEnd:!0,keywords:t,contains:["self",r.inherit(r.TITLE_MODE,{begin:"[A-Za-z$_][0-9A-Za-z$_]*"}),u],illegal:/%/,relevance:0},{beginKeywords:"constructor",end:/[\{;]/,excludeEnd:!0,contains:["self",u]},{begin:/module\./,keywords:{built_in:"module"},relevance:0},{beginKeywords:"module",end:/\{/,excludeEnd:!0},{beginKeywords:"interface",end:/\{/,excludeEnd:!0,keywords:"interface extends"},{begin:/\$[(.]/},{begin:"\\."+r.IDENT_RE,relevance:0},s,d]}}}()); +hljs.registerLanguage("yaml",function(){"use strict";return function(e){var n="true false yes no null",a="[\\w#;/?:@&=+$,.~*\\'()[\\]]+",s={className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable",variants:[{begin:"{{",end:"}}"},{begin:"%{",end:"}"}]}]},i=e.inherit(s,{variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),l={end:",",endsWithParent:!0,excludeEnd:!0,contains:[],keywords:n,relevance:0},t={begin:"{",end:"}",contains:[l],illegal:"\\n",relevance:0},g={begin:"\\[",end:"\\]",contains:[l],illegal:"\\n",relevance:0},b=[{className:"attr",variants:[{begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)"}]},{className:"meta",begin:"^---s*$",relevance:10},{className:"string",begin:"[\\|>]([0-9]?[+-])?[ ]*\\n( *)[\\S ]+\\n(\\2[\\S ]+\\n?)*"},{begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:"!\\w+!"+a},{className:"type",begin:"!<"+a+">"},{className:"type",begin:"!"+a},{className:"type",begin:"!!"+a},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta",begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"\\-(?=[ ]|$)",relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:n,keywords:{literal:n}},{className:"number",begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b"},{className:"number",begin:e.C_NUMBER_RE+"\\b"},t,g,s],c=[...b];return c.pop(),c.push(i),l.contains=c,{name:"YAML",case_insensitive:!0,aliases:["yml","YAML"],contains:b}}}()); +hljs.registerLanguage("armasm",function(){"use strict";return function(s){const e={variants:[s.COMMENT("^[ \\t]*(?=#)","$",{relevance:0,excludeBegin:!0}),s.COMMENT("[;@]","$",{relevance:0}),s.C_LINE_COMMENT_MODE,s.C_BLOCK_COMMENT_MODE]};return{name:"ARM Assembly",case_insensitive:!0,aliases:["arm"],keywords:{$pattern:"\\.?"+s.IDENT_RE,meta:".2byte .4byte .align .ascii .asciz .balign .byte .code .data .else .end .endif .endm .endr .equ .err .exitm .extern .global .hword .if .ifdef .ifndef .include .irp .long .macro .rept .req .section .set .skip .space .text .word .arm .thumb .code16 .code32 .force_thumb .thumb_func .ltorg ALIAS ALIGN ARM AREA ASSERT ATTR CN CODE CODE16 CODE32 COMMON CP DATA DCB DCD DCDU DCDO DCFD DCFDU DCI DCQ DCQU DCW DCWU DN ELIF ELSE END ENDFUNC ENDIF ENDP ENTRY EQU EXPORT EXPORTAS EXTERN FIELD FILL FUNCTION GBLA GBLL GBLS GET GLOBAL IF IMPORT INCBIN INCLUDE INFO KEEP LCLA LCLL LCLS LTORG MACRO MAP MEND MEXIT NOFP OPT PRESERVE8 PROC QN READONLY RELOC REQUIRE REQUIRE8 RLIST FN ROUT SETA SETL SETS SN SPACE SUBT THUMB THUMBX TTL WHILE WEND ",built_in:"r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r13 r14 r15 pc lr sp ip sl sb fp a1 a2 a3 a4 v1 v2 v3 v4 v5 v6 v7 v8 f0 f1 f2 f3 f4 f5 f6 f7 p0 p1 p2 p3 p4 p5 p6 p7 p8 p9 p10 p11 p12 p13 p14 p15 c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15 q0 q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12 q13 q14 q15 cpsr_c cpsr_x cpsr_s cpsr_f cpsr_cx cpsr_cxs cpsr_xs cpsr_xsf cpsr_sf cpsr_cxsf spsr_c spsr_x spsr_s spsr_f spsr_cx spsr_cxs spsr_xs spsr_xsf spsr_sf spsr_cxsf s0 s1 s2 s3 s4 s5 s6 s7 s8 s9 s10 s11 s12 s13 s14 s15 s16 s17 s18 s19 s20 s21 s22 s23 s24 s25 s26 s27 s28 s29 s30 s31 d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 d10 d11 d12 d13 d14 d15 d16 d17 d18 d19 d20 d21 d22 d23 d24 d25 d26 d27 d28 d29 d30 d31 {PC} {VAR} {TRUE} {FALSE} {OPT} {CONFIG} {ENDIAN} {CODESIZE} {CPU} {FPU} {ARCHITECTURE} {PCSTOREOFFSET} {ARMASM_VERSION} {INTER} {ROPI} {RWPI} {SWST} {NOSWST} . @"},contains:[{className:"keyword",begin:"\\b(adc|(qd?|sh?|u[qh]?)?add(8|16)?|usada?8|(q|sh?|u[qh]?)?(as|sa)x|and|adrl?|sbc|rs[bc]|asr|b[lx]?|blx|bxj|cbn?z|tb[bh]|bic|bfc|bfi|[su]bfx|bkpt|cdp2?|clz|clrex|cmp|cmn|cpsi[ed]|cps|setend|dbg|dmb|dsb|eor|isb|it[te]{0,3}|lsl|lsr|ror|rrx|ldm(([id][ab])|f[ds])?|ldr((s|ex)?[bhd])?|movt?|mvn|mra|mar|mul|[us]mull|smul[bwt][bt]|smu[as]d|smmul|smmla|mla|umlaal|smlal?([wbt][bt]|d)|mls|smlsl?[ds]|smc|svc|sev|mia([bt]{2}|ph)?|mrr?c2?|mcrr2?|mrs|msr|orr|orn|pkh(tb|bt)|rbit|rev(16|sh)?|sel|[su]sat(16)?|nop|pop|push|rfe([id][ab])?|stm([id][ab])?|str(ex)?[bhd]?|(qd?)?sub|(sh?|q|u[qh]?)?sub(8|16)|[su]xt(a?h|a?b(16)?)|srs([id][ab])?|swpb?|swi|smi|tst|teq|wfe|wfi|yield)(eq|ne|cs|cc|mi|pl|vs|vc|hi|ls|ge|lt|gt|le|al|hs|lo)?[sptrx]?(?=\\s)"},e,s.QUOTE_STRING_MODE,{className:"string",begin:"'",end:"[^\\\\]'",relevance:0},{className:"title",begin:"\\|",end:"\\|",illegal:"\\n",relevance:0},{className:"number",variants:[{begin:"[#$=]?0x[0-9a-f]+"},{begin:"[#$=]?0b[01]+"},{begin:"[#$=]\\d+"},{begin:"\\b\\d+"}],relevance:0},{className:"symbol",variants:[{begin:"^[ \\t]*[a-z_\\.\\$][a-z0-9_\\.\\$]+:"},{begin:"^[a-z_\\.\\$][a-z0-9_\\.\\$]+"},{begin:"[=#]\\w+"}],relevance:0}]}}}()); +hljs.registerLanguage("d",function(){"use strict";return function(e){var a={$pattern:e.UNDERSCORE_IDENT_RE,keyword:"abstract alias align asm assert auto body break byte case cast catch class const continue debug default delete deprecated do else enum export extern final finally for foreach foreach_reverse|10 goto if immutable import in inout int interface invariant is lazy macro mixin module new nothrow out override package pragma private protected public pure ref return scope shared static struct super switch synchronized template this throw try typedef typeid typeof union unittest version void volatile while with __FILE__ __LINE__ __gshared|10 __thread __traits __DATE__ __EOF__ __TIME__ __TIMESTAMP__ __VENDOR__ __VERSION__",built_in:"bool cdouble cent cfloat char creal dchar delegate double dstring float function idouble ifloat ireal long real short string ubyte ucent uint ulong ushort wchar wstring",literal:"false null true"},d="((0|[1-9][\\d_]*)|0[bB][01_]+|0[xX]([\\da-fA-F][\\da-fA-F_]*|_[\\da-fA-F][\\da-fA-F_]*))",n="\\\\(['\"\\?\\\\abfnrtv]|u[\\dA-Fa-f]{4}|[0-7]{1,3}|x[\\dA-Fa-f]{2}|U[\\dA-Fa-f]{8})|&[a-zA-Z\\d]{2,};",t={className:"number",begin:"\\b"+d+"(L|u|U|Lu|LU|uL|UL)?",relevance:0},_={className:"number",begin:"\\b(((0[xX](([\\da-fA-F][\\da-fA-F_]*|_[\\da-fA-F][\\da-fA-F_]*)\\.([\\da-fA-F][\\da-fA-F_]*|_[\\da-fA-F][\\da-fA-F_]*)|\\.?([\\da-fA-F][\\da-fA-F_]*|_[\\da-fA-F][\\da-fA-F_]*))[pP][+-]?(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d))|((0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d)(\\.\\d*|([eE][+-]?(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d)))|\\d+\\.(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d)(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d)|\\.(0|[1-9][\\d_]*)([eE][+-]?(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d))?))([fF]|L|i|[fF]i|Li)?|"+d+"(i|[fF]i|Li))",relevance:0},r={className:"string",begin:"'("+n+"|.)",end:"'",illegal:"."},i={className:"string",begin:'"',contains:[{begin:n,relevance:0}],end:'"[cwd]?'},s=e.COMMENT("\\/\\+","\\+\\/",{contains:["self"],relevance:10});return{name:"D",keywords:a,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,s,{className:"string",begin:'x"[\\da-fA-F\\s\\n\\r]*"[cwd]?',relevance:10},i,{className:"string",begin:'[rq]"',end:'"[cwd]?',relevance:5},{className:"string",begin:"`",end:"`[cwd]?"},{className:"string",begin:'q"\\{',end:'\\}"'},_,t,r,{className:"meta",begin:"^#!",end:"$",relevance:5},{className:"meta",begin:"#(line)",end:"$",relevance:5},{className:"keyword",begin:"@[a-zA-Z_][a-zA-Z_\\d]*"}]}}}()); +hljs.registerLanguage("handlebars",function(){"use strict";function e(...e){return e.map(e=>(function(e){return e?"string"==typeof e?e:e.source:null})(e)).join("")}return function(n){const a={"builtin-name":"action bindattr collection component concat debugger each each-in get hash if in input link-to loc log lookup mut outlet partial query-params render template textarea unbound unless view with yield"},t=/\[.*?\]/,s=/[^\s!"#%&'()*+,.\/;<=>@\[\\\]^`{|}~]+/,i=e("(",/'.*?'/,"|",/".*?"/,"|",t,"|",s,"|",/\.|\//,")+"),r=e("(",t,"|",s,")(?==)"),l={begin:i,lexemes:/[\w.\/]+/},c=n.inherit(l,{keywords:{literal:"true false undefined null"}}),o={begin:/\(/,end:/\)/},m={className:"attr",begin:r,relevance:0,starts:{begin:/=/,end:/=/,starts:{contains:[n.NUMBER_MODE,n.QUOTE_STRING_MODE,n.APOS_STRING_MODE,c,o]}}},d={contains:[n.NUMBER_MODE,n.QUOTE_STRING_MODE,n.APOS_STRING_MODE,{begin:/as\s+\|/,keywords:{keyword:"as"},end:/\|/,contains:[{begin:/\w+/}]},m,c,o],returnEnd:!0},g=n.inherit(l,{className:"name",keywords:a,starts:n.inherit(d,{end:/\)/})});o.contains=[g];const u=n.inherit(l,{keywords:a,className:"name",starts:n.inherit(d,{end:/}}/})}),b=n.inherit(l,{keywords:a,className:"name"}),h=n.inherit(l,{className:"name",keywords:a,starts:n.inherit(d,{end:/}}/})});return{name:"Handlebars",aliases:["hbs","html.hbs","html.handlebars","htmlbars"],case_insensitive:!0,subLanguage:"xml",contains:[{begin:/\\\{\{/,skip:!0},{begin:/\\\\(?=\{\{)/,skip:!0},n.COMMENT(/\{\{!--/,/--\}\}/),n.COMMENT(/\{\{!/,/\}\}/),{className:"template-tag",begin:/\{\{\{\{(?!\/)/,end:/\}\}\}\}/,contains:[u],starts:{end:/\{\{\{\{\//,returnEnd:!0,subLanguage:"xml"}},{className:"template-tag",begin:/\{\{\{\{\//,end:/\}\}\}\}/,contains:[b]},{className:"template-tag",begin:/\{\{#/,end:/\}\}/,contains:[u]},{className:"template-tag",begin:/\{\{(?=else\}\})/,end:/\}\}/,keywords:"else"},{className:"template-tag",begin:/\{\{\//,end:/\}\}/,contains:[b]},{className:"template-variable",begin:/\{\{\{/,end:/\}\}\}/,contains:[h]},{className:"template-variable",begin:/\{\{/,end:/\}\}/,contains:[h]}]}}}()); +hljs.registerLanguage("haskell",function(){"use strict";return function(e){var n={variants:[e.COMMENT("--","$"),e.COMMENT("{-","-}",{contains:["self"]})]},i={className:"meta",begin:"{-#",end:"#-}"},a={className:"meta",begin:"^#",end:"$"},s={className:"type",begin:"\\b[A-Z][\\w']*",relevance:0},l={begin:"\\(",end:"\\)",illegal:'"',contains:[i,a,{className:"type",begin:"\\b[A-Z][\\w]*(\\((\\.\\.|,|\\w+)\\))?"},e.inherit(e.TITLE_MODE,{begin:"[_a-z][\\w']*"}),n]};return{name:"Haskell",aliases:["hs"],keywords:"let in if then else case of where do module import hiding qualified type data newtype deriving class instance as default infix infixl infixr foreign export ccall stdcall cplusplus jvm dotnet safe unsafe family forall mdo proc rec",contains:[{beginKeywords:"module",end:"where",keywords:"module where",contains:[l,n],illegal:"\\W\\.|;"},{begin:"\\bimport\\b",end:"$",keywords:"import qualified as hiding",contains:[l,n],illegal:"\\W\\.|;"},{className:"class",begin:"^(\\s*)?(class|instance)\\b",end:"where",keywords:"class family instance where",contains:[s,l,n]},{className:"class",begin:"\\b(data|(new)?type)\\b",end:"$",keywords:"data family type newtype deriving",contains:[i,s,l,{begin:"{",end:"}",contains:l.contains},n]},{beginKeywords:"default",end:"$",contains:[s,l,n]},{beginKeywords:"infix infixl infixr",end:"$",contains:[e.C_NUMBER_MODE,n]},{begin:"\\bforeign\\b",end:"$",keywords:"foreign import export ccall stdcall cplusplus jvm dotnet safe unsafe",contains:[s,e.QUOTE_STRING_MODE,n]},{className:"meta",begin:"#!\\/usr\\/bin\\/env runhaskell",end:"$"},i,a,e.QUOTE_STRING_MODE,e.C_NUMBER_MODE,s,e.inherit(e.TITLE_MODE,{begin:"^[_a-z][\\w']*"}),n,{begin:"->|<-"}]}}}()); +hljs.registerLanguage("julia",function(){"use strict";return function(e){var r="[A-Za-z_\\u00A1-\\uFFFF][A-Za-z_0-9\\u00A1-\\uFFFF]*",t={$pattern:r,keyword:"in isa where baremodule begin break catch ccall const continue do else elseif end export false finally for function global if import importall let local macro module quote return true try using while type immutable abstract bitstype typealias ",literal:"true false ARGS C_NULL DevNull ENDIAN_BOM ENV I Inf Inf16 Inf32 Inf64 InsertionSort JULIA_HOME LOAD_PATH MergeSort NaN NaN16 NaN32 NaN64 PROGRAM_FILE QuickSort RoundDown RoundFromZero RoundNearest RoundNearestTiesAway RoundNearestTiesUp RoundToZero RoundUp STDERR STDIN STDOUT VERSION catalan e|0 eu|0 eulergamma golden im nothing pi γ π φ ",built_in:"ANY AbstractArray AbstractChannel AbstractFloat AbstractMatrix AbstractRNG AbstractSerializer AbstractSet AbstractSparseArray AbstractSparseMatrix AbstractSparseVector AbstractString AbstractUnitRange AbstractVecOrMat AbstractVector Any ArgumentError Array AssertionError Associative Base64DecodePipe Base64EncodePipe Bidiagonal BigFloat BigInt BitArray BitMatrix BitVector Bool BoundsError BufferStream CachingPool CapturedException CartesianIndex CartesianRange Cchar Cdouble Cfloat Channel Char Cint Cintmax_t Clong Clonglong ClusterManager Cmd CodeInfo Colon Complex Complex128 Complex32 Complex64 CompositeException Condition ConjArray ConjMatrix ConjVector Cptrdiff_t Cshort Csize_t Cssize_t Cstring Cuchar Cuint Cuintmax_t Culong Culonglong Cushort Cwchar_t Cwstring DataType Date DateFormat DateTime DenseArray DenseMatrix DenseVecOrMat DenseVector Diagonal Dict DimensionMismatch Dims DirectIndexString Display DivideError DomainError EOFError EachLine Enum Enumerate ErrorException Exception ExponentialBackOff Expr Factorization FileMonitor Float16 Float32 Float64 Function Future GlobalRef GotoNode HTML Hermitian IO IOBuffer IOContext IOStream IPAddr IPv4 IPv6 IndexCartesian IndexLinear IndexStyle InexactError InitError Int Int128 Int16 Int32 Int64 Int8 IntSet Integer InterruptException InvalidStateException Irrational KeyError LabelNode LinSpace LineNumberNode LoadError LowerTriangular MIME Matrix MersenneTwister Method MethodError MethodTable Module NTuple NewvarNode NullException Nullable Number ObjectIdDict OrdinalRange OutOfMemoryError OverflowError Pair ParseError PartialQuickSort PermutedDimsArray Pipe PollingFileWatcher ProcessExitedException Ptr QuoteNode RandomDevice Range RangeIndex Rational RawFD ReadOnlyMemoryError Real ReentrantLock Ref Regex RegexMatch RemoteChannel RemoteException RevString RoundingMode RowVector SSAValue SegmentationFault SerializationState Set SharedArray SharedMatrix SharedVector Signed SimpleVector Slot SlotNumber SparseMatrixCSC SparseVector StackFrame StackOverflowError StackTrace StepRange StepRangeLen StridedArray StridedMatrix StridedVecOrMat StridedVector String SubArray SubString SymTridiagonal Symbol Symmetric SystemError TCPSocket Task Text TextDisplay Timer Tridiagonal Tuple Type TypeError TypeMapEntry TypeMapLevel TypeName TypeVar TypedSlot UDPSocket UInt UInt128 UInt16 UInt32 UInt64 UInt8 UndefRefError UndefVarError UnicodeError UniformScaling Union UnionAll UnitRange Unsigned UpperTriangular Val Vararg VecElement VecOrMat Vector VersionNumber Void WeakKeyDict WeakRef WorkerConfig WorkerPool "},a={keywords:t,illegal:/<\//},n={className:"subst",begin:/\$\(/,end:/\)/,keywords:t},o={className:"variable",begin:"\\$"+r},i={className:"string",contains:[e.BACKSLASH_ESCAPE,n,o],variants:[{begin:/\w*"""/,end:/"""\w*/,relevance:10},{begin:/\w*"/,end:/"\w*/}]},l={className:"string",contains:[e.BACKSLASH_ESCAPE,n,o],begin:"`",end:"`"},s={className:"meta",begin:"@"+r};return a.name="Julia",a.contains=[{className:"number",begin:/(\b0x[\d_]*(\.[\d_]*)?|0x\.\d[\d_]*)p[-+]?\d+|\b0[box][a-fA-F0-9][a-fA-F0-9_]*|(\b\d[\d_]*(\.[\d_]*)?|\.\d[\d_]*)([eEfF][-+]?\d+)?/,relevance:0},{className:"string",begin:/'(.|\\[xXuU][a-zA-Z0-9]+)'/},i,l,s,{className:"comment",variants:[{begin:"#=",end:"=#",relevance:10},{begin:"#",end:"$"}]},e.HASH_COMMENT_MODE,{className:"keyword",begin:"\\b(((abstract|primitive)\\s+)type|(mutable\\s+)?struct)\\b"},{begin:/<:/}],n.contains=a.contains,a}}()); +hljs.registerLanguage("nim",function(){"use strict";return function(e){return{name:"Nim",aliases:["nim"],keywords:{keyword:"addr and as asm bind block break case cast const continue converter discard distinct div do elif else end enum except export finally for from func generic if import in include interface is isnot iterator let macro method mixin mod nil not notin object of or out proc ptr raise ref return shl shr static template try tuple type using var when while with without xor yield",literal:"shared guarded stdin stdout stderr result true false",built_in:"int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 float float32 float64 bool char string cstring pointer expr stmt void auto any range array openarray varargs seq set clong culong cchar cschar cshort cint csize clonglong cfloat cdouble clongdouble cuchar cushort cuint culonglong cstringarray semistatic"},contains:[{className:"meta",begin:/{\./,end:/\.}/,relevance:10},{className:"string",begin:/[a-zA-Z]\w*"/,end:/"/,contains:[{begin:/""/}]},{className:"string",begin:/([a-zA-Z]\w*)?"""/,end:/"""/},e.QUOTE_STRING_MODE,{className:"type",begin:/\b[A-Z]\w+\b/,relevance:0},{className:"number",relevance:0,variants:[{begin:/\b(0[xX][0-9a-fA-F][_0-9a-fA-F]*)('?[iIuU](8|16|32|64))?/},{begin:/\b(0o[0-7][_0-7]*)('?[iIuUfF](8|16|32|64))?/},{begin:/\b(0(b|B)[01][_01]*)('?[iIuUfF](8|16|32|64))?/},{begin:/\b(\d[_\d]*)('?[iIuUfF](8|16|32|64))?/}]},e.HASH_COMMENT_MODE]}}}()); +hljs.registerLanguage("nix",function(){"use strict";return function(e){var n={keyword:"rec with let in inherit assert if else then",literal:"true false or and null",built_in:"import abort baseNameOf dirOf isNull builtins map removeAttrs throw toString derivation"},i={className:"subst",begin:/\$\{/,end:/}/,keywords:n},t={className:"string",contains:[i],variants:[{begin:"''",end:"''"},{begin:'"',end:'"'}]},s=[e.NUMBER_MODE,e.HASH_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,t,{begin:/[a-zA-Z0-9-_]+(\s*=)/,returnBegin:!0,relevance:0,contains:[{className:"attr",begin:/\S+/}]}];return i.contains=s,{name:"Nix",aliases:["nixos"],keywords:n,contains:s}}}()); +hljs.registerLanguage("r",function(){"use strict";return function(e){var n="([a-zA-Z]|\\.[a-zA-Z.])[a-zA-Z0-9._]*";return{name:"R",contains:[e.HASH_COMMENT_MODE,{begin:n,keywords:{$pattern:n,keyword:"function if in break next repeat else for return switch while try tryCatch stop warning require library attach detach source setMethod setGeneric setGroupGeneric setClass ...",literal:"NULL NA TRUE FALSE T F Inf NaN NA_integer_|10 NA_real_|10 NA_character_|10 NA_complex_|10"},relevance:0},{className:"number",begin:"0[xX][0-9a-fA-F]+[Li]?\\b",relevance:0},{className:"number",begin:"\\d+(?:[eE][+\\-]?\\d*)?L\\b",relevance:0},{className:"number",begin:"\\d+\\.(?!\\d)(?:i\\b)?",relevance:0},{className:"number",begin:"\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d*)?i?\\b",relevance:0},{className:"number",begin:"\\.\\d+(?:[eE][+\\-]?\\d*)?i?\\b",relevance:0},{begin:"`",end:"`",relevance:0},{className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:'"',end:'"'},{begin:"'",end:"'"}]}]}}}()); +hljs.registerLanguage("scala",function(){"use strict";return function(e){var n={className:"subst",variants:[{begin:"\\$[A-Za-z0-9_]+"},{begin:"\\${",end:"}"}]},a={className:"string",variants:[{begin:'"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{begin:'"""',end:'"""',relevance:10},{begin:'[a-z]+"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE,n]},{className:"string",begin:'[a-z]+"""',end:'"""',contains:[n],relevance:10}]},s={className:"type",begin:"\\b[A-Z][A-Za-z0-9_]*",relevance:0},t={className:"title",begin:/[^0-9\n\t "'(),.`{}\[\]:;][^\n\t "'(),.`{}\[\]:;]+|[^0-9\n\t "'(),.`{}\[\]:;=]/,relevance:0},i={className:"class",beginKeywords:"class object trait type",end:/[:={\[\n;]/,excludeEnd:!0,contains:[{beginKeywords:"extends with",relevance:10},{begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0,relevance:0,contains:[s]},{className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,relevance:0,contains:[s]},t]},l={className:"function",beginKeywords:"def",end:/[:={\[(\n;]/,excludeEnd:!0,contains:[t]};return{name:"Scala",keywords:{literal:"true false null",keyword:"type yield lazy override def with val var sealed abstract private trait object if forSome for while throw finally protected extends import final return else break new catch super class case package default try this match continue throws implicit"},contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,{className:"symbol",begin:"'\\w[\\w\\d_]*(?!')"},s,l,i,e.C_NUMBER_MODE,{className:"meta",begin:"@[A-Za-z]+"}]}}}()); +hljs.registerLanguage("x86asm",function(){"use strict";return function(s){return{name:"Intel x86 Assembly",case_insensitive:!0,keywords:{$pattern:"[.%]?"+s.IDENT_RE,keyword:"lock rep repe repz repne repnz xaquire xrelease bnd nobnd aaa aad aam aas adc add and arpl bb0_reset bb1_reset bound bsf bsr bswap bt btc btr bts call cbw cdq cdqe clc cld cli clts cmc cmp cmpsb cmpsd cmpsq cmpsw cmpxchg cmpxchg486 cmpxchg8b cmpxchg16b cpuid cpu_read cpu_write cqo cwd cwde daa das dec div dmint emms enter equ f2xm1 fabs fadd faddp fbld fbstp fchs fclex fcmovb fcmovbe fcmove fcmovnb fcmovnbe fcmovne fcmovnu fcmovu fcom fcomi fcomip fcomp fcompp fcos fdecstp fdisi fdiv fdivp fdivr fdivrp femms feni ffree ffreep fiadd ficom ficomp fidiv fidivr fild fimul fincstp finit fist fistp fisttp fisub fisubr fld fld1 fldcw fldenv fldl2e fldl2t fldlg2 fldln2 fldpi fldz fmul fmulp fnclex fndisi fneni fninit fnop fnsave fnstcw fnstenv fnstsw fpatan fprem fprem1 fptan frndint frstor fsave fscale fsetpm fsin fsincos fsqrt fst fstcw fstenv fstp fstsw fsub fsubp fsubr fsubrp ftst fucom fucomi fucomip fucomp fucompp fxam fxch fxtract fyl2x fyl2xp1 hlt ibts icebp idiv imul in inc incbin insb insd insw int int01 int1 int03 int3 into invd invpcid invlpg invlpga iret iretd iretq iretw jcxz jecxz jrcxz jmp jmpe lahf lar lds lea leave les lfence lfs lgdt lgs lidt lldt lmsw loadall loadall286 lodsb lodsd lodsq lodsw loop loope loopne loopnz loopz lsl lss ltr mfence monitor mov movd movq movsb movsd movsq movsw movsx movsxd movzx mul mwait neg nop not or out outsb outsd outsw packssdw packsswb packuswb paddb paddd paddsb paddsiw paddsw paddusb paddusw paddw pand pandn pause paveb pavgusb pcmpeqb pcmpeqd pcmpeqw pcmpgtb pcmpgtd pcmpgtw pdistib pf2id pfacc pfadd pfcmpeq pfcmpge pfcmpgt pfmax pfmin pfmul pfrcp pfrcpit1 pfrcpit2 pfrsqit1 pfrsqrt pfsub pfsubr pi2fd pmachriw pmaddwd pmagw pmulhriw pmulhrwa pmulhrwc pmulhw pmullw pmvgezb pmvlzb pmvnzb pmvzb pop popa popad popaw popf popfd popfq popfw por prefetch prefetchw pslld psllq psllw psrad psraw psrld psrlq psrlw psubb psubd psubsb psubsiw psubsw psubusb psubusw psubw punpckhbw punpckhdq punpckhwd punpcklbw punpckldq punpcklwd push pusha pushad pushaw pushf pushfd pushfq pushfw pxor rcl rcr rdshr rdmsr rdpmc rdtsc rdtscp ret retf retn rol ror rdm rsdc rsldt rsm rsts sahf sal salc sar sbb scasb scasd scasq scasw sfence sgdt shl shld shr shrd sidt sldt skinit smi smint smintold smsw stc std sti stosb stosd stosq stosw str sub svdc svldt svts swapgs syscall sysenter sysexit sysret test ud0 ud1 ud2b ud2 ud2a umov verr verw fwait wbinvd wrshr wrmsr xadd xbts xchg xlatb xlat xor cmove cmovz cmovne cmovnz cmova cmovnbe cmovae cmovnb cmovb cmovnae cmovbe cmovna cmovg cmovnle cmovge cmovnl cmovl cmovnge cmovle cmovng cmovc cmovnc cmovo cmovno cmovs cmovns cmovp cmovpe cmovnp cmovpo je jz jne jnz ja jnbe jae jnb jb jnae jbe jna jg jnle jge jnl jl jnge jle jng jc jnc jo jno js jns jpo jnp jpe jp sete setz setne setnz seta setnbe setae setnb setnc setb setnae setcset setbe setna setg setnle setge setnl setl setnge setle setng sets setns seto setno setpe setp setpo setnp addps addss andnps andps cmpeqps cmpeqss cmpleps cmpless cmpltps cmpltss cmpneqps cmpneqss cmpnleps cmpnless cmpnltps cmpnltss cmpordps cmpordss cmpunordps cmpunordss cmpps cmpss comiss cvtpi2ps cvtps2pi cvtsi2ss cvtss2si cvttps2pi cvttss2si divps divss ldmxcsr maxps maxss minps minss movaps movhps movlhps movlps movhlps movmskps movntps movss movups mulps mulss orps rcpps rcpss rsqrtps rsqrtss shufps sqrtps sqrtss stmxcsr subps subss ucomiss unpckhps unpcklps xorps fxrstor fxrstor64 fxsave fxsave64 xgetbv xsetbv xsave xsave64 xsaveopt xsaveopt64 xrstor xrstor64 prefetchnta prefetcht0 prefetcht1 prefetcht2 maskmovq movntq pavgb pavgw pextrw pinsrw pmaxsw pmaxub pminsw pminub pmovmskb pmulhuw psadbw pshufw pf2iw pfnacc pfpnacc pi2fw pswapd maskmovdqu clflush movntdq movnti movntpd movdqa movdqu movdq2q movq2dq paddq pmuludq pshufd pshufhw pshuflw pslldq psrldq psubq punpckhqdq punpcklqdq addpd addsd andnpd andpd cmpeqpd cmpeqsd cmplepd cmplesd cmpltpd cmpltsd cmpneqpd cmpneqsd cmpnlepd cmpnlesd cmpnltpd cmpnltsd cmpordpd cmpordsd cmpunordpd cmpunordsd cmppd comisd cvtdq2pd cvtdq2ps cvtpd2dq cvtpd2pi cvtpd2ps cvtpi2pd cvtps2dq cvtps2pd cvtsd2si cvtsd2ss cvtsi2sd cvtss2sd cvttpd2pi cvttpd2dq cvttps2dq cvttsd2si divpd divsd maxpd maxsd minpd minsd movapd movhpd movlpd movmskpd movupd mulpd mulsd orpd shufpd sqrtpd sqrtsd subpd subsd ucomisd unpckhpd unpcklpd xorpd addsubpd addsubps haddpd haddps hsubpd hsubps lddqu movddup movshdup movsldup clgi stgi vmcall vmclear vmfunc vmlaunch vmload vmmcall vmptrld vmptrst vmread vmresume vmrun vmsave vmwrite vmxoff vmxon invept invvpid pabsb pabsw pabsd palignr phaddw phaddd phaddsw phsubw phsubd phsubsw pmaddubsw pmulhrsw pshufb psignb psignw psignd extrq insertq movntsd movntss lzcnt blendpd blendps blendvpd blendvps dppd dpps extractps insertps movntdqa mpsadbw packusdw pblendvb pblendw pcmpeqq pextrb pextrd pextrq phminposuw pinsrb pinsrd pinsrq pmaxsb pmaxsd pmaxud pmaxuw pminsb pminsd pminud pminuw pmovsxbw pmovsxbd pmovsxbq pmovsxwd pmovsxwq pmovsxdq pmovzxbw pmovzxbd pmovzxbq pmovzxwd pmovzxwq pmovzxdq pmuldq pmulld ptest roundpd roundps roundsd roundss crc32 pcmpestri pcmpestrm pcmpistri pcmpistrm pcmpgtq popcnt getsec pfrcpv pfrsqrtv movbe aesenc aesenclast aesdec aesdeclast aesimc aeskeygenassist vaesenc vaesenclast vaesdec vaesdeclast vaesimc vaeskeygenassist vaddpd vaddps vaddsd vaddss vaddsubpd vaddsubps vandpd vandps vandnpd vandnps vblendpd vblendps vblendvpd vblendvps vbroadcastss vbroadcastsd vbroadcastf128 vcmpeq_ospd vcmpeqpd vcmplt_ospd vcmpltpd vcmple_ospd vcmplepd vcmpunord_qpd vcmpunordpd vcmpneq_uqpd vcmpneqpd vcmpnlt_uspd vcmpnltpd vcmpnle_uspd vcmpnlepd vcmpord_qpd vcmpordpd vcmpeq_uqpd vcmpnge_uspd vcmpngepd vcmpngt_uspd vcmpngtpd vcmpfalse_oqpd vcmpfalsepd vcmpneq_oqpd vcmpge_ospd vcmpgepd vcmpgt_ospd vcmpgtpd vcmptrue_uqpd vcmptruepd vcmplt_oqpd vcmple_oqpd vcmpunord_spd vcmpneq_uspd vcmpnlt_uqpd vcmpnle_uqpd vcmpord_spd vcmpeq_uspd vcmpnge_uqpd vcmpngt_uqpd vcmpfalse_ospd vcmpneq_ospd vcmpge_oqpd vcmpgt_oqpd vcmptrue_uspd vcmppd vcmpeq_osps vcmpeqps vcmplt_osps vcmpltps vcmple_osps vcmpleps vcmpunord_qps vcmpunordps vcmpneq_uqps vcmpneqps vcmpnlt_usps vcmpnltps vcmpnle_usps vcmpnleps vcmpord_qps vcmpordps vcmpeq_uqps vcmpnge_usps vcmpngeps vcmpngt_usps vcmpngtps vcmpfalse_oqps vcmpfalseps vcmpneq_oqps vcmpge_osps vcmpgeps vcmpgt_osps vcmpgtps vcmptrue_uqps vcmptrueps vcmplt_oqps vcmple_oqps vcmpunord_sps vcmpneq_usps vcmpnlt_uqps vcmpnle_uqps vcmpord_sps vcmpeq_usps vcmpnge_uqps vcmpngt_uqps vcmpfalse_osps vcmpneq_osps vcmpge_oqps vcmpgt_oqps vcmptrue_usps vcmpps vcmpeq_ossd vcmpeqsd vcmplt_ossd vcmpltsd vcmple_ossd vcmplesd vcmpunord_qsd vcmpunordsd vcmpneq_uqsd vcmpneqsd vcmpnlt_ussd vcmpnltsd vcmpnle_ussd vcmpnlesd vcmpord_qsd vcmpordsd vcmpeq_uqsd vcmpnge_ussd vcmpngesd vcmpngt_ussd vcmpngtsd vcmpfalse_oqsd vcmpfalsesd vcmpneq_oqsd vcmpge_ossd vcmpgesd vcmpgt_ossd vcmpgtsd vcmptrue_uqsd vcmptruesd vcmplt_oqsd vcmple_oqsd vcmpunord_ssd vcmpneq_ussd vcmpnlt_uqsd vcmpnle_uqsd vcmpord_ssd vcmpeq_ussd vcmpnge_uqsd vcmpngt_uqsd vcmpfalse_ossd vcmpneq_ossd vcmpge_oqsd vcmpgt_oqsd vcmptrue_ussd vcmpsd vcmpeq_osss vcmpeqss vcmplt_osss vcmpltss vcmple_osss vcmpless vcmpunord_qss vcmpunordss vcmpneq_uqss vcmpneqss vcmpnlt_usss vcmpnltss vcmpnle_usss vcmpnless vcmpord_qss vcmpordss vcmpeq_uqss vcmpnge_usss vcmpngess vcmpngt_usss vcmpngtss vcmpfalse_oqss vcmpfalsess vcmpneq_oqss vcmpge_osss vcmpgess vcmpgt_osss vcmpgtss vcmptrue_uqss vcmptruess vcmplt_oqss vcmple_oqss vcmpunord_sss vcmpneq_usss vcmpnlt_uqss vcmpnle_uqss vcmpord_sss vcmpeq_usss vcmpnge_uqss vcmpngt_uqss vcmpfalse_osss vcmpneq_osss vcmpge_oqss vcmpgt_oqss vcmptrue_usss vcmpss vcomisd vcomiss vcvtdq2pd vcvtdq2ps vcvtpd2dq vcvtpd2ps vcvtps2dq vcvtps2pd vcvtsd2si vcvtsd2ss vcvtsi2sd vcvtsi2ss vcvtss2sd vcvtss2si vcvttpd2dq vcvttps2dq vcvttsd2si vcvttss2si vdivpd vdivps vdivsd vdivss vdppd vdpps vextractf128 vextractps vhaddpd vhaddps vhsubpd vhsubps vinsertf128 vinsertps vlddqu vldqqu vldmxcsr vmaskmovdqu vmaskmovps vmaskmovpd vmaxpd vmaxps vmaxsd vmaxss vminpd vminps vminsd vminss vmovapd vmovaps vmovd vmovq vmovddup vmovdqa vmovqqa vmovdqu vmovqqu vmovhlps vmovhpd vmovhps vmovlhps vmovlpd vmovlps vmovmskpd vmovmskps vmovntdq vmovntqq vmovntdqa vmovntpd vmovntps vmovsd vmovshdup vmovsldup vmovss vmovupd vmovups vmpsadbw vmulpd vmulps vmulsd vmulss vorpd vorps vpabsb vpabsw vpabsd vpacksswb vpackssdw vpackuswb vpackusdw vpaddb vpaddw vpaddd vpaddq vpaddsb vpaddsw vpaddusb vpaddusw vpalignr vpand vpandn vpavgb vpavgw vpblendvb vpblendw vpcmpestri vpcmpestrm vpcmpistri vpcmpistrm vpcmpeqb vpcmpeqw vpcmpeqd vpcmpeqq vpcmpgtb vpcmpgtw vpcmpgtd vpcmpgtq vpermilpd vpermilps vperm2f128 vpextrb vpextrw vpextrd vpextrq vphaddw vphaddd vphaddsw vphminposuw vphsubw vphsubd vphsubsw vpinsrb vpinsrw vpinsrd vpinsrq vpmaddwd vpmaddubsw vpmaxsb vpmaxsw vpmaxsd vpmaxub vpmaxuw vpmaxud vpminsb vpminsw vpminsd vpminub vpminuw vpminud vpmovmskb vpmovsxbw vpmovsxbd vpmovsxbq vpmovsxwd vpmovsxwq vpmovsxdq vpmovzxbw vpmovzxbd vpmovzxbq vpmovzxwd vpmovzxwq vpmovzxdq vpmulhuw vpmulhrsw vpmulhw vpmullw vpmulld vpmuludq vpmuldq vpor vpsadbw vpshufb vpshufd vpshufhw vpshuflw vpsignb vpsignw vpsignd vpslldq vpsrldq vpsllw vpslld vpsllq vpsraw vpsrad vpsrlw vpsrld vpsrlq vptest vpsubb vpsubw vpsubd vpsubq vpsubsb vpsubsw vpsubusb vpsubusw vpunpckhbw vpunpckhwd vpunpckhdq vpunpckhqdq vpunpcklbw vpunpcklwd vpunpckldq vpunpcklqdq vpxor vrcpps vrcpss vrsqrtps vrsqrtss vroundpd vroundps vroundsd vroundss vshufpd vshufps vsqrtpd vsqrtps vsqrtsd vsqrtss vstmxcsr vsubpd vsubps vsubsd vsubss vtestps vtestpd vucomisd vucomiss vunpckhpd vunpckhps vunpcklpd vunpcklps vxorpd vxorps vzeroall vzeroupper pclmullqlqdq pclmulhqlqdq pclmullqhqdq pclmulhqhqdq pclmulqdq vpclmullqlqdq vpclmulhqlqdq vpclmullqhqdq vpclmulhqhqdq vpclmulqdq vfmadd132ps vfmadd132pd vfmadd312ps vfmadd312pd vfmadd213ps vfmadd213pd vfmadd123ps vfmadd123pd vfmadd231ps vfmadd231pd vfmadd321ps vfmadd321pd vfmaddsub132ps vfmaddsub132pd vfmaddsub312ps vfmaddsub312pd vfmaddsub213ps vfmaddsub213pd vfmaddsub123ps vfmaddsub123pd vfmaddsub231ps vfmaddsub231pd vfmaddsub321ps vfmaddsub321pd vfmsub132ps vfmsub132pd vfmsub312ps vfmsub312pd vfmsub213ps vfmsub213pd vfmsub123ps vfmsub123pd vfmsub231ps vfmsub231pd vfmsub321ps vfmsub321pd vfmsubadd132ps vfmsubadd132pd vfmsubadd312ps vfmsubadd312pd vfmsubadd213ps vfmsubadd213pd vfmsubadd123ps vfmsubadd123pd vfmsubadd231ps vfmsubadd231pd vfmsubadd321ps vfmsubadd321pd vfnmadd132ps vfnmadd132pd vfnmadd312ps vfnmadd312pd vfnmadd213ps vfnmadd213pd vfnmadd123ps vfnmadd123pd vfnmadd231ps vfnmadd231pd vfnmadd321ps vfnmadd321pd vfnmsub132ps vfnmsub132pd vfnmsub312ps vfnmsub312pd vfnmsub213ps vfnmsub213pd vfnmsub123ps vfnmsub123pd vfnmsub231ps vfnmsub231pd vfnmsub321ps vfnmsub321pd vfmadd132ss vfmadd132sd vfmadd312ss vfmadd312sd vfmadd213ss vfmadd213sd vfmadd123ss vfmadd123sd vfmadd231ss vfmadd231sd vfmadd321ss vfmadd321sd vfmsub132ss vfmsub132sd vfmsub312ss vfmsub312sd vfmsub213ss vfmsub213sd vfmsub123ss vfmsub123sd vfmsub231ss vfmsub231sd vfmsub321ss vfmsub321sd vfnmadd132ss vfnmadd132sd vfnmadd312ss vfnmadd312sd vfnmadd213ss vfnmadd213sd vfnmadd123ss vfnmadd123sd vfnmadd231ss vfnmadd231sd vfnmadd321ss vfnmadd321sd vfnmsub132ss vfnmsub132sd vfnmsub312ss vfnmsub312sd vfnmsub213ss vfnmsub213sd vfnmsub123ss vfnmsub123sd vfnmsub231ss vfnmsub231sd vfnmsub321ss vfnmsub321sd rdfsbase rdgsbase rdrand wrfsbase wrgsbase vcvtph2ps vcvtps2ph adcx adox rdseed clac stac xstore xcryptecb xcryptcbc xcryptctr xcryptcfb xcryptofb montmul xsha1 xsha256 llwpcb slwpcb lwpval lwpins vfmaddpd vfmaddps vfmaddsd vfmaddss vfmaddsubpd vfmaddsubps vfmsubaddpd vfmsubaddps vfmsubpd vfmsubps vfmsubsd vfmsubss vfnmaddpd vfnmaddps vfnmaddsd vfnmaddss vfnmsubpd vfnmsubps vfnmsubsd vfnmsubss vfrczpd vfrczps vfrczsd vfrczss vpcmov vpcomb vpcomd vpcomq vpcomub vpcomud vpcomuq vpcomuw vpcomw vphaddbd vphaddbq vphaddbw vphadddq vphaddubd vphaddubq vphaddubw vphaddudq vphadduwd vphadduwq vphaddwd vphaddwq vphsubbw vphsubdq vphsubwd vpmacsdd vpmacsdqh vpmacsdql vpmacssdd vpmacssdqh vpmacssdql vpmacsswd vpmacssww vpmacswd vpmacsww vpmadcsswd vpmadcswd vpperm vprotb vprotd vprotq vprotw vpshab vpshad vpshaq vpshaw vpshlb vpshld vpshlq vpshlw vbroadcasti128 vpblendd vpbroadcastb vpbroadcastw vpbroadcastd vpbroadcastq vpermd vpermpd vpermps vpermq vperm2i128 vextracti128 vinserti128 vpmaskmovd vpmaskmovq vpsllvd vpsllvq vpsravd vpsrlvd vpsrlvq vgatherdpd vgatherqpd vgatherdps vgatherqps vpgatherdd vpgatherqd vpgatherdq vpgatherqq xabort xbegin xend xtest andn bextr blci blcic blsi blsic blcfill blsfill blcmsk blsmsk blsr blcs bzhi mulx pdep pext rorx sarx shlx shrx tzcnt tzmsk t1mskc valignd valignq vblendmpd vblendmps vbroadcastf32x4 vbroadcastf64x4 vbroadcasti32x4 vbroadcasti64x4 vcompresspd vcompressps vcvtpd2udq vcvtps2udq vcvtsd2usi vcvtss2usi vcvttpd2udq vcvttps2udq vcvttsd2usi vcvttss2usi vcvtudq2pd vcvtudq2ps vcvtusi2sd vcvtusi2ss vexpandpd vexpandps vextractf32x4 vextractf64x4 vextracti32x4 vextracti64x4 vfixupimmpd vfixupimmps vfixupimmsd vfixupimmss vgetexppd vgetexpps vgetexpsd vgetexpss vgetmantpd vgetmantps vgetmantsd vgetmantss vinsertf32x4 vinsertf64x4 vinserti32x4 vinserti64x4 vmovdqa32 vmovdqa64 vmovdqu32 vmovdqu64 vpabsq vpandd vpandnd vpandnq vpandq vpblendmd vpblendmq vpcmpltd vpcmpled vpcmpneqd vpcmpnltd vpcmpnled vpcmpd vpcmpltq vpcmpleq vpcmpneqq vpcmpnltq vpcmpnleq vpcmpq vpcmpequd vpcmpltud vpcmpleud vpcmpnequd vpcmpnltud vpcmpnleud vpcmpud vpcmpequq vpcmpltuq vpcmpleuq vpcmpnequq vpcmpnltuq vpcmpnleuq vpcmpuq vpcompressd vpcompressq vpermi2d vpermi2pd vpermi2ps vpermi2q vpermt2d vpermt2pd vpermt2ps vpermt2q vpexpandd vpexpandq vpmaxsq vpmaxuq vpminsq vpminuq vpmovdb vpmovdw vpmovqb vpmovqd vpmovqw vpmovsdb vpmovsdw vpmovsqb vpmovsqd vpmovsqw vpmovusdb vpmovusdw vpmovusqb vpmovusqd vpmovusqw vpord vporq vprold vprolq vprolvd vprolvq vprord vprorq vprorvd vprorvq vpscatterdd vpscatterdq vpscatterqd vpscatterqq vpsraq vpsravq vpternlogd vpternlogq vptestmd vptestmq vptestnmd vptestnmq vpxord vpxorq vrcp14pd vrcp14ps vrcp14sd vrcp14ss vrndscalepd vrndscaleps vrndscalesd vrndscaless vrsqrt14pd vrsqrt14ps vrsqrt14sd vrsqrt14ss vscalefpd vscalefps vscalefsd vscalefss vscatterdpd vscatterdps vscatterqpd vscatterqps vshuff32x4 vshuff64x2 vshufi32x4 vshufi64x2 kandnw kandw kmovw knotw kortestw korw kshiftlw kshiftrw kunpckbw kxnorw kxorw vpbroadcastmb2q vpbroadcastmw2d vpconflictd vpconflictq vplzcntd vplzcntq vexp2pd vexp2ps vrcp28pd vrcp28ps vrcp28sd vrcp28ss vrsqrt28pd vrsqrt28ps vrsqrt28sd vrsqrt28ss vgatherpf0dpd vgatherpf0dps vgatherpf0qpd vgatherpf0qps vgatherpf1dpd vgatherpf1dps vgatherpf1qpd vgatherpf1qps vscatterpf0dpd vscatterpf0dps vscatterpf0qpd vscatterpf0qps vscatterpf1dpd vscatterpf1dps vscatterpf1qpd vscatterpf1qps prefetchwt1 bndmk bndcl bndcu bndcn bndmov bndldx bndstx sha1rnds4 sha1nexte sha1msg1 sha1msg2 sha256rnds2 sha256msg1 sha256msg2 hint_nop0 hint_nop1 hint_nop2 hint_nop3 hint_nop4 hint_nop5 hint_nop6 hint_nop7 hint_nop8 hint_nop9 hint_nop10 hint_nop11 hint_nop12 hint_nop13 hint_nop14 hint_nop15 hint_nop16 hint_nop17 hint_nop18 hint_nop19 hint_nop20 hint_nop21 hint_nop22 hint_nop23 hint_nop24 hint_nop25 hint_nop26 hint_nop27 hint_nop28 hint_nop29 hint_nop30 hint_nop31 hint_nop32 hint_nop33 hint_nop34 hint_nop35 hint_nop36 hint_nop37 hint_nop38 hint_nop39 hint_nop40 hint_nop41 hint_nop42 hint_nop43 hint_nop44 hint_nop45 hint_nop46 hint_nop47 hint_nop48 hint_nop49 hint_nop50 hint_nop51 hint_nop52 hint_nop53 hint_nop54 hint_nop55 hint_nop56 hint_nop57 hint_nop58 hint_nop59 hint_nop60 hint_nop61 hint_nop62 hint_nop63",built_in:"ip eip rip al ah bl bh cl ch dl dh sil dil bpl spl r8b r9b r10b r11b r12b r13b r14b r15b ax bx cx dx si di bp sp r8w r9w r10w r11w r12w r13w r14w r15w eax ebx ecx edx esi edi ebp esp eip r8d r9d r10d r11d r12d r13d r14d r15d rax rbx rcx rdx rsi rdi rbp rsp r8 r9 r10 r11 r12 r13 r14 r15 cs ds es fs gs ss st st0 st1 st2 st3 st4 st5 st6 st7 mm0 mm1 mm2 mm3 mm4 mm5 mm6 mm7 xmm0 xmm1 xmm2 xmm3 xmm4 xmm5 xmm6 xmm7 xmm8 xmm9 xmm10 xmm11 xmm12 xmm13 xmm14 xmm15 xmm16 xmm17 xmm18 xmm19 xmm20 xmm21 xmm22 xmm23 xmm24 xmm25 xmm26 xmm27 xmm28 xmm29 xmm30 xmm31 ymm0 ymm1 ymm2 ymm3 ymm4 ymm5 ymm6 ymm7 ymm8 ymm9 ymm10 ymm11 ymm12 ymm13 ymm14 ymm15 ymm16 ymm17 ymm18 ymm19 ymm20 ymm21 ymm22 ymm23 ymm24 ymm25 ymm26 ymm27 ymm28 ymm29 ymm30 ymm31 zmm0 zmm1 zmm2 zmm3 zmm4 zmm5 zmm6 zmm7 zmm8 zmm9 zmm10 zmm11 zmm12 zmm13 zmm14 zmm15 zmm16 zmm17 zmm18 zmm19 zmm20 zmm21 zmm22 zmm23 zmm24 zmm25 zmm26 zmm27 zmm28 zmm29 zmm30 zmm31 k0 k1 k2 k3 k4 k5 k6 k7 bnd0 bnd1 bnd2 bnd3 cr0 cr1 cr2 cr3 cr4 cr8 dr0 dr1 dr2 dr3 dr8 tr3 tr4 tr5 tr6 tr7 r0 r1 r2 r3 r4 r5 r6 r7 r0b r1b r2b r3b r4b r5b r6b r7b r0w r1w r2w r3w r4w r5w r6w r7w r0d r1d r2d r3d r4d r5d r6d r7d r0h r1h r2h r3h r0l r1l r2l r3l r4l r5l r6l r7l r8l r9l r10l r11l r12l r13l r14l r15l db dw dd dq dt ddq do dy dz resb resw resd resq rest resdq reso resy resz incbin equ times byte word dword qword nosplit rel abs seg wrt strict near far a32 ptr",meta:"%define %xdefine %+ %undef %defstr %deftok %assign %strcat %strlen %substr %rotate %elif %else %endif %if %ifmacro %ifctx %ifidn %ifidni %ifid %ifnum %ifstr %iftoken %ifempty %ifenv %error %warning %fatal %rep %endrep %include %push %pop %repl %pathsearch %depend %use %arg %stacksize %local %line %comment %endcomment .nolist __FILE__ __LINE__ __SECT__ __BITS__ __OUTPUT_FORMAT__ __DATE__ __TIME__ __DATE_NUM__ __TIME_NUM__ __UTC_DATE__ __UTC_TIME__ __UTC_DATE_NUM__ __UTC_TIME_NUM__ __PASS__ struc endstruc istruc at iend align alignb sectalign daz nodaz up down zero default option assume public bits use16 use32 use64 default section segment absolute extern global common cpu float __utf16__ __utf16le__ __utf16be__ __utf32__ __utf32le__ __utf32be__ __float8__ __float16__ __float32__ __float64__ __float80m__ __float80e__ __float128l__ __float128h__ __Infinity__ __QNaN__ __SNaN__ Inf NaN QNaN SNaN float8 float16 float32 float64 float80m float80e float128l float128h __FLOAT_DAZ__ __FLOAT_ROUND__ __FLOAT__"},contains:[s.COMMENT(";","$",{relevance:0}),{className:"number",variants:[{begin:"\\b(?:([0-9][0-9_]*)?\\.[0-9_]*(?:[eE][+-]?[0-9_]+)?|(0[Xx])?[0-9][0-9_]*\\.?[0-9_]*(?:[pP](?:[+-]?[0-9_]+)?)?)\\b",relevance:0},{begin:"\\$[0-9][0-9A-Fa-f]*",relevance:0},{begin:"\\b(?:[0-9A-Fa-f][0-9A-Fa-f_]*[Hh]|[0-9][0-9_]*[DdTt]?|[0-7][0-7_]*[QqOo]|[0-1][0-1_]*[BbYy])\\b"},{begin:"\\b(?:0[Xx][0-9A-Fa-f_]+|0[DdTt][0-9_]+|0[QqOo][0-7_]+|0[BbYy][0-1_]+)\\b"}]},s.QUOTE_STRING_MODE,{className:"string",variants:[{begin:"'",end:"[^\\\\]'"},{begin:"`",end:"[^\\\\]`"}],relevance:0},{className:"symbol",variants:[{begin:"^\\s*[A-Za-z._?][A-Za-z0-9_$#@~.?]*(:|\\s+label)"},{begin:"^\\s*%%[A-Za-z0-9_$#@~.?]*:"}],relevance:0},{className:"subst",begin:"%[0-9]+",relevance:0},{className:"subst",begin:"%!S+",relevance:0},{className:"meta",begin:/^\s*\.[\w_-]+/}]}}}()); \ No newline at end of file diff --git a/typ/mdbook/assets/mark-09e88c2c.min.js b/typ/mdbook/assets/mark-09e88c2c.min.js new file mode 100644 index 00000000..16362318 --- /dev/null +++ b/typ/mdbook/assets/mark-09e88c2c.min.js @@ -0,0 +1,7 @@ +/*!*************************************************** +* mark.js v8.11.1 +* https://markjs.io/ +* Copyright (c) 2014–2018, Julian Kühnel +* Released under the MIT license https://git.io/vwTVl +*****************************************************/ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Mark=t()}(this,function(){"use strict";var e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},t=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},n=function(){function e(e,t){for(var n=0;n1&&void 0!==arguments[1])||arguments[1],i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:5e3;t(this,e),this.ctx=n,this.iframes=r,this.exclude=i,this.iframesTimeout=o}return n(e,[{key:"getContexts",value:function(){var e=[];return(void 0!==this.ctx&&this.ctx?NodeList.prototype.isPrototypeOf(this.ctx)?Array.prototype.slice.call(this.ctx):Array.isArray(this.ctx)?this.ctx:"string"==typeof this.ctx?Array.prototype.slice.call(document.querySelectorAll(this.ctx)):[this.ctx]:[]).forEach(function(t){var n=e.filter(function(e){return e.contains(t)}).length>0;-1!==e.indexOf(t)||n||e.push(t)}),e}},{key:"getIframeContents",value:function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:function(){},r=void 0;try{var i=e.contentWindow;if(r=i.document,!i||!r)throw new Error("iframe inaccessible")}catch(e){n()}r&&t(r)}},{key:"isIframeBlank",value:function(e){var t="about:blank",n=e.getAttribute("src").trim();return e.contentWindow.location.href===t&&n!==t&&n}},{key:"observeIframeLoad",value:function(e,t,n){var r=this,i=!1,o=null,a=function a(){if(!i){i=!0,clearTimeout(o);try{r.isIframeBlank(e)||(e.removeEventListener("load",a),r.getIframeContents(e,t,n))}catch(e){n()}}};e.addEventListener("load",a),o=setTimeout(a,this.iframesTimeout)}},{key:"onIframeReady",value:function(e,t,n){try{"complete"===e.contentWindow.document.readyState?this.isIframeBlank(e)?this.observeIframeLoad(e,t,n):this.getIframeContents(e,t,n):this.observeIframeLoad(e,t,n)}catch(e){n()}}},{key:"waitForIframes",value:function(e,t){var n=this,r=0;this.forEachIframe(e,function(){return!0},function(e){r++,n.waitForIframes(e.querySelector("html"),function(){--r||t()})},function(e){e||t()})}},{key:"forEachIframe",value:function(t,n,r){var i=this,o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:function(){},a=t.querySelectorAll("iframe"),s=a.length,c=0;a=Array.prototype.slice.call(a);var u=function(){--s<=0&&o(c)};s||u(),a.forEach(function(t){e.matches(t,i.exclude)?u():i.onIframeReady(t,function(e){n(t)&&(c++,r(e)),u()},u)})}},{key:"createIterator",value:function(e,t,n){return document.createNodeIterator(e,t,n,!1)}},{key:"createInstanceOnIframe",value:function(t){return new e(t.querySelector("html"),this.iframes)}},{key:"compareNodeIframe",value:function(e,t,n){if(e.compareDocumentPosition(n)&Node.DOCUMENT_POSITION_PRECEDING){if(null===t)return!0;if(t.compareDocumentPosition(n)&Node.DOCUMENT_POSITION_FOLLOWING)return!0}return!1}},{key:"getIteratorNode",value:function(e){var t=e.previousNode();return{prevNode:t,node:null===t?e.nextNode():e.nextNode()&&e.nextNode()}}},{key:"checkIframeFilter",value:function(e,t,n,r){var i=!1,o=!1;return r.forEach(function(e,t){e.val===n&&(i=t,o=e.handled)}),this.compareNodeIframe(e,t,n)?(!1!==i||o?!1===i||o||(r[i].handled=!0):r.push({val:n,handled:!0}),!0):(!1===i&&r.push({val:n,handled:!1}),!1)}},{key:"handleOpenIframes",value:function(e,t,n,r){var i=this;e.forEach(function(e){e.handled||i.getIframeContents(e.val,function(e){i.createInstanceOnIframe(e).forEachNode(t,n,r)})})}},{key:"iterateThroughNodes",value:function(e,t,n,r,i){for(var o,a=this,s=this.createIterator(t,e,r),c=[],u=[],l=void 0,h=void 0;void 0,o=a.getIteratorNode(s),h=o.prevNode,l=o.node;)this.iframes&&this.forEachIframe(t,function(e){return a.checkIframeFilter(l,h,e,c)},function(t){a.createInstanceOnIframe(t).forEachNode(e,function(e){return u.push(e)},r)}),u.push(l);u.forEach(function(e){n(e)}),this.iframes&&this.handleOpenIframes(c,e,n,r),i()}},{key:"forEachNode",value:function(e,t,n){var r=this,i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:function(){},o=this.getContexts(),a=o.length;a||i(),o.forEach(function(o){var s=function(){r.iterateThroughNodes(e,o,t,n,function(){--a<=0&&i()})};r.iframes?r.waitForIframes(o,s):s()})}}],[{key:"matches",value:function(e,t){var n="string"==typeof t?[t]:t,r=e.matches||e.matchesSelector||e.msMatchesSelector||e.mozMatchesSelector||e.oMatchesSelector||e.webkitMatchesSelector;if(r){var i=!1;return n.every(function(t){return!r.call(e,t)||(i=!0,!1)}),i}return!1}}]),e}(),o=function(){function e(n){t(this,e),this.opt=r({},{diacritics:!0,synonyms:{},accuracy:"partially",caseSensitive:!1,ignoreJoiners:!1,ignorePunctuation:[],wildcards:"disabled"},n)}return n(e,[{key:"create",value:function(e){return"disabled"!==this.opt.wildcards&&(e=this.setupWildcardsRegExp(e)),e=this.escapeStr(e),Object.keys(this.opt.synonyms).length&&(e=this.createSynonymsRegExp(e)),(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(e=this.setupIgnoreJoinersRegExp(e)),this.opt.diacritics&&(e=this.createDiacriticsRegExp(e)),e=this.createMergedBlanksRegExp(e),(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(e=this.createJoinersRegExp(e)),"disabled"!==this.opt.wildcards&&(e=this.createWildcardsRegExp(e)),e=this.createAccuracyRegExp(e),new RegExp(e,"gm"+(this.opt.caseSensitive?"":"i"))}},{key:"escapeStr",value:function(e){return e.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")}},{key:"createSynonymsRegExp",value:function(e){var t=this.opt.synonyms,n=this.opt.caseSensitive?"":"i",r=this.opt.ignoreJoiners||this.opt.ignorePunctuation.length?"\0":"";for(var i in t)if(t.hasOwnProperty(i)){var o=t[i],a="disabled"!==this.opt.wildcards?this.setupWildcardsRegExp(i):this.escapeStr(i),s="disabled"!==this.opt.wildcards?this.setupWildcardsRegExp(o):this.escapeStr(o);""!==a&&""!==s&&(e=e.replace(new RegExp("("+this.escapeStr(a)+"|"+this.escapeStr(s)+")","gm"+n),r+"("+this.processSynonyms(a)+"|"+this.processSynonyms(s)+")"+r))}return e}},{key:"processSynonyms",value:function(e){return(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(e=this.setupIgnoreJoinersRegExp(e)),e}},{key:"setupWildcardsRegExp",value:function(e){return(e=e.replace(/(?:\\)*\?/g,function(e){return"\\"===e.charAt(0)?"?":""})).replace(/(?:\\)*\*/g,function(e){return"\\"===e.charAt(0)?"*":""})}},{key:"createWildcardsRegExp",value:function(e){var t="withSpaces"===this.opt.wildcards;return e.replace(/\u0001/g,t?"[\\S\\s]?":"\\S?").replace(/\u0002/g,t?"[\\S\\s]*?":"\\S*")}},{key:"setupIgnoreJoinersRegExp",value:function(e){return e.replace(/[^(|)\\]/g,function(e,t,n){var r=n.charAt(t+1);return/[(|)\\]/.test(r)||""===r?e:e+"\0"})}},{key:"createJoinersRegExp",value:function(e){var t=[],n=this.opt.ignorePunctuation;return Array.isArray(n)&&n.length&&t.push(this.escapeStr(n.join(""))),this.opt.ignoreJoiners&&t.push("\\u00ad\\u200b\\u200c\\u200d"),t.length?e.split(/\u0000+/).join("["+t.join("")+"]*"):e}},{key:"createDiacriticsRegExp",value:function(e){var t=this.opt.caseSensitive?"":"i",n=this.opt.caseSensitive?["aàáảãạăằắẳẵặâầấẩẫậäåāą","AÀÁẢÃẠĂẰẮẲẴẶÂẦẤẨẪẬÄÅĀĄ","cçćč","CÇĆČ","dđď","DĐĎ","eèéẻẽẹêềếểễệëěēę","EÈÉẺẼẸÊỀẾỂỄỆËĚĒĘ","iìíỉĩịîïī","IÌÍỈĨỊÎÏĪ","lł","LŁ","nñňń","NÑŇŃ","oòóỏõọôồốổỗộơởỡớờợöøō","OÒÓỎÕỌÔỒỐỔỖỘƠỞỠỚỜỢÖØŌ","rř","RŘ","sšśșş","SŠŚȘŞ","tťțţ","TŤȚŢ","uùúủũụưừứửữựûüůū","UÙÚỦŨỤƯỪỨỬỮỰÛÜŮŪ","yýỳỷỹỵÿ","YÝỲỶỸỴŸ","zžżź","ZŽŻŹ"]:["aàáảãạăằắẳẵặâầấẩẫậäåāąAÀÁẢÃẠĂẰẮẲẴẶÂẦẤẨẪẬÄÅĀĄ","cçćčCÇĆČ","dđďDĐĎ","eèéẻẽẹêềếểễệëěēęEÈÉẺẼẸÊỀẾỂỄỆËĚĒĘ","iìíỉĩịîïīIÌÍỈĨỊÎÏĪ","lłLŁ","nñňńNÑŇŃ","oòóỏõọôồốổỗộơởỡớờợöøōOÒÓỎÕỌÔỒỐỔỖỘƠỞỠỚỜỢÖØŌ","rřRŘ","sšśșşSŠŚȘŞ","tťțţTŤȚŢ","uùúủũụưừứửữựûüůūUÙÚỦŨỤƯỪỨỬỮỰÛÜŮŪ","yýỳỷỹỵÿYÝỲỶỸỴŸ","zžżźZŽŻŹ"],r=[];return e.split("").forEach(function(i){n.every(function(n){if(-1!==n.indexOf(i)){if(r.indexOf(n)>-1)return!1;e=e.replace(new RegExp("["+n+"]","gm"+t),"["+n+"]"),r.push(n)}return!0})}),e}},{key:"createMergedBlanksRegExp",value:function(e){return e.replace(/[\s]+/gim,"[\\s]+")}},{key:"createAccuracyRegExp",value:function(e){var t=this,n=this.opt.accuracy,r="string"==typeof n?n:n.value,i="";switch(("string"==typeof n?[]:n.limiters).forEach(function(e){i+="|"+t.escapeStr(e)}),r){case"partially":default:return"()("+e+")";case"complementary":return"()([^"+(i="\\s"+(i||this.escapeStr("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~¡¿")))+"]*"+e+"[^"+i+"]*)";case"exactly":return"(^|\\s"+i+")("+e+")(?=$|\\s"+i+")"}}}]),e}(),a=function(){function a(e){t(this,a),this.ctx=e,this.ie=!1;var n=window.navigator.userAgent;(n.indexOf("MSIE")>-1||n.indexOf("Trident")>-1)&&(this.ie=!0)}return n(a,[{key:"log",value:function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"debug",r=this.opt.log;this.opt.debug&&"object"===(void 0===r?"undefined":e(r))&&"function"==typeof r[n]&&r[n]("mark.js: "+t)}},{key:"getSeparatedKeywords",value:function(e){var t=this,n=[];return e.forEach(function(e){t.opt.separateWordSearch?e.split(" ").forEach(function(e){e.trim()&&-1===n.indexOf(e)&&n.push(e)}):e.trim()&&-1===n.indexOf(e)&&n.push(e)}),{keywords:n.sort(function(e,t){return t.length-e.length}),length:n.length}}},{key:"isNumeric",value:function(e){return Number(parseFloat(e))==e}},{key:"checkRanges",value:function(e){var t=this;if(!Array.isArray(e)||"[object Object]"!==Object.prototype.toString.call(e[0]))return this.log("markRanges() will only accept an array of objects"),this.opt.noMatch(e),[];var n=[],r=0;return e.sort(function(e,t){return e.start-t.start}).forEach(function(e){var i=t.callNoMatchOnInvalidRanges(e,r),o=i.start,a=i.end;i.valid&&(e.start=o,e.length=a-o,n.push(e),r=a)}),n}},{key:"callNoMatchOnInvalidRanges",value:function(e,t){var n=void 0,r=void 0,i=!1;return e&&void 0!==e.start?(r=(n=parseInt(e.start,10))+parseInt(e.length,10),this.isNumeric(e.start)&&this.isNumeric(e.length)&&r-t>0&&r-n>0?i=!0:(this.log("Ignoring invalid or overlapping range: "+JSON.stringify(e)),this.opt.noMatch(e))):(this.log("Ignoring invalid range: "+JSON.stringify(e)),this.opt.noMatch(e)),{start:n,end:r,valid:i}}},{key:"checkWhitespaceRanges",value:function(e,t,n){var r=void 0,i=!0,o=n.length,a=t-o,s=parseInt(e.start,10)-a;return(r=(s=s>o?o:s)+parseInt(e.length,10))>o&&(r=o,this.log("End range automatically set to the max value of "+o)),s<0||r-s<0||s>o||r>o?(i=!1,this.log("Invalid range: "+JSON.stringify(e)),this.opt.noMatch(e)):""===n.substring(s,r).replace(/\s+/g,"")&&(i=!1,this.log("Skipping whitespace only range: "+JSON.stringify(e)),this.opt.noMatch(e)),{start:s,end:r,valid:i}}},{key:"getTextNodes",value:function(e){var t=this,n="",r=[];this.iterator.forEachNode(NodeFilter.SHOW_TEXT,function(e){r.push({start:n.length,end:(n+=e.textContent).length,node:e})},function(e){return t.matchesExclude(e.parentNode)?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT},function(){e({value:n,nodes:r})})}},{key:"matchesExclude",value:function(e){return i.matches(e,this.opt.exclude.concat(["script","style","title","head","html"]))}},{key:"wrapRangeInTextNode",value:function(e,t,n){var r=this.opt.element?this.opt.element:"mark",i=e.splitText(t),o=i.splitText(n-t),a=document.createElement(r);return a.setAttribute("data-markjs","true"),this.opt.className&&a.setAttribute("class",this.opt.className),a.textContent=i.textContent,i.parentNode.replaceChild(a,i),o}},{key:"wrapRangeInMappedTextNode",value:function(e,t,n,r,i){var o=this;e.nodes.every(function(a,s){var c=e.nodes[s+1];if(void 0===c||c.start>t){if(!r(a.node))return!1;var u=t-a.start,l=(n>a.end?a.end:n)-a.start,h=e.value.substr(0,a.start),f=e.value.substr(l+a.start);if(a.node=o.wrapRangeInTextNode(a.node,u,l),e.value=h+f,e.nodes.forEach(function(t,n){n>=s&&(e.nodes[n].start>0&&n!==s&&(e.nodes[n].start-=l),e.nodes[n].end-=l)}),n-=l,i(a.node.previousSibling,a.start),!(n>a.end))return!1;t=a.end}return!0})}},{key:"wrapGroups",value:function(e,t,n,r){return r((e=this.wrapRangeInTextNode(e,t,t+n)).previousSibling),e}},{key:"separateGroups",value:function(e,t,n,r,i){for(var o=t.length,a=1;a-1&&r(t[a],e)&&(e=this.wrapGroups(e,s,t[a].length,i))}return e}},{key:"wrapMatches",value:function(e,t,n,r,i){var o=this,a=0===t?0:t+1;this.getTextNodes(function(t){t.nodes.forEach(function(t){t=t.node;for(var i=void 0;null!==(i=e.exec(t.textContent))&&""!==i[a];){if(o.opt.separateGroups)t=o.separateGroups(t,i,a,n,r);else{if(!n(i[a],t))continue;var s=i.index;if(0!==a)for(var c=1;c tag. + mark_exclude = ['text'], + marker = new Mark(content), + URL_SEARCH_PARAM = 'search', + URL_MARK_PARAM = 'highlight'; + + let current_searchterm = '', + doc_urls = [], + search_options = { + bool: 'AND', + expand: true, + fields: { + title: {boost: 1}, + body: {boost: 1}, + breadcrumbs: {boost: 0}, + }, + }, + searchindex = null, + results_options = { + teaser_word_count: 30, + limit_results: 30, + }, + teaser_count = 0; + + function hasFocus() { + return searchbar === document.activeElement; + } + + function removeChildren(elem) { + while (elem.firstChild) { + elem.removeChild(elem.firstChild); + } + } + + // Helper to parse a url into its building blocks. + function parseURL(url) { + const a = document.createElement('a'); + a.href = url; + return { + source: url, + protocol: a.protocol.replace(':', ''), + host: a.hostname, + port: a.port, + params: (function() { + const ret = {}; + const seg = a.search.replace(/^\?/, '').split('&'); + for (const part of seg) { + if (!part) { + continue; + } + const s = part.split('='); + ret[s[0]] = s[1]; + } + return ret; + })(), + file: (a.pathname.match(/\/([^/?#]+)$/i) || ['', ''])[1], + hash: a.hash.replace('#', ''), + path: a.pathname.replace(/^([^/])/, '/$1'), + }; + } + + // Helper to recreate a url string from its building blocks. + function renderURL(urlobject) { + let url = urlobject.protocol + '://' + urlobject.host; + if (urlobject.port !== '') { + url += ':' + urlobject.port; + } + url += urlobject.path; + let joiner = '?'; + for (const prop in urlobject.params) { + if (Object.prototype.hasOwnProperty.call(urlobject.params, prop)) { + url += joiner + prop + '=' + urlobject.params[prop]; + joiner = '&'; + } + } + if (urlobject.hash !== '') { + url += '#' + urlobject.hash; + } + return url; + } + + // Helper to escape html special chars for displaying the teasers + const escapeHTML = (function() { + const MAP = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + '\'': ''', + }; + const repl = function(c) { + return MAP[c]; + }; + return function(s) { + return s.replace(/[&<>'"]/g, repl); + }; + })(); + + function formatSearchMetric(count, searchterm) { + if (count === 1) { + return count + ' search result for \'' + searchterm + '\':'; + } else if (count === 0) { + return 'No search results for \'' + searchterm + '\'.'; + } else { + return count + ' search results for \'' + searchterm + '\':'; + } + } + + function formatSearchResult(result, searchterms) { + const teaser = makeTeaser(escapeHTML(result.doc.body), searchterms); + teaser_count++; + + // The ?URL_MARK_PARAM= parameter belongs in between the page and the #heading-anchor + const url = doc_urls[result.ref].split('#'); + if (url.length === 1) { // no anchor found + url.push(''); + } + + // encodeURIComponent escapes all chars that could allow an XSS except + // for '. Due to that we also manually replace ' with its url-encoded + // representation (%27). + const encoded_search = encodeURIComponent(searchterms.join(' ')).replace(/'/g, '%27'); + + return '
    ' + + result.doc.breadcrumbs + '' + + '' + teaser + ''; + } + + function makeTeaser(body, searchterms) { + // The strategy is as follows: + // First, assign a value to each word in the document: + // Words that correspond to search terms (stemmer aware): 40 + // Normal words: 2 + // First word in a sentence: 8 + // Then use a sliding window with a constant number of words and count the + // sum of the values of the words within the window. Then use the window that got the + // maximum sum. If there are multiple maximas, then get the last one. + // Enclose the terms in . + const stemmed_searchterms = searchterms.map(function(w) { + return elasticlunr.stemmer(w.toLowerCase()); + }); + const searchterm_weight = 40; + const weighted = []; // contains elements of ["word", weight, index_in_document] + // split in sentences, then words + const sentences = body.toLowerCase().split('. '); + let index = 0; + let value = 0; + let searchterm_found = false; + for (const sentenceindex in sentences) { + const words = sentences[sentenceindex].split(' '); + value = 8; + for (const wordindex in words) { + const word = words[wordindex]; + if (word.length > 0) { + for (const searchtermindex in stemmed_searchterms) { + if (elasticlunr.stemmer(word).startsWith( + stemmed_searchterms[searchtermindex]) + ) { + value = searchterm_weight; + searchterm_found = true; + } + } + weighted.push([word, value, index]); + value = 2; + } + index += word.length; + index += 1; // ' ' or '.' if last word in sentence + } + index += 1; // because we split at a two-char boundary '. ' + } + + if (weighted.length === 0) { + return body; + } + + const window_weight = []; + const window_size = Math.min(weighted.length, results_options.teaser_word_count); + + let cur_sum = 0; + for (let wordindex = 0; wordindex < window_size; wordindex++) { + cur_sum += weighted[wordindex][1]; + } + window_weight.push(cur_sum); + for (let wordindex = 0; wordindex < weighted.length - window_size; wordindex++) { + cur_sum -= weighted[wordindex][1]; + cur_sum += weighted[wordindex + window_size][1]; + window_weight.push(cur_sum); + } + + let max_sum_window_index = 0; + if (searchterm_found) { + let max_sum = 0; + // backwards + for (let i = window_weight.length - 1; i >= 0; i--) { + if (window_weight[i] > max_sum) { + max_sum = window_weight[i]; + max_sum_window_index = i; + } + } + } else { + max_sum_window_index = 0; + } + + // add around searchterms + const teaser_split = []; + index = weighted[max_sum_window_index][2]; + for (let i = max_sum_window_index; i < max_sum_window_index + window_size; i++) { + const word = weighted[i]; + if (index < word[2]) { + // missing text from index to start of `word` + teaser_split.push(body.substring(index, word[2])); + index = word[2]; + } + if (word[1] === searchterm_weight) { + teaser_split.push(''); + } + index = word[2] + word[0].length; + teaser_split.push(body.substring(word[2], index)); + if (word[1] === searchterm_weight) { + teaser_split.push(''); + } + } + + return teaser_split.join(''); + } + + function init(config) { + results_options = config.results_options; + search_options = config.search_options; + doc_urls = config.doc_urls; + searchindex = elasticlunr.Index.load(config.index); + + searchbar_outer.classList.remove('searching'); + + searchbar.focus(); + + const searchterm = searchbar.value.trim(); + if (searchterm !== '') { + searchbar.classList.add('active'); + doSearch(searchterm); + } + } + + function initSearchInteractions(config) { + // Set up events + searchicon.addEventListener('click', () => { + searchIconClickHandler(); + }, false); + searchbar.addEventListener('keyup', () => { + searchbarKeyUpHandler(); + }, false); + document.addEventListener('keydown', e => { + globalKeyHandler(e); + }, false); + // If the user uses the browser buttons, do the same as if a reload happened + window.onpopstate = () => { + doSearchOrMarkFromUrl(); + }; + // Suppress "submit" events so the page doesn't reload when the user presses Enter + document.addEventListener('submit', e => { + e.preventDefault(); + }, false); + + // If reloaded, do the search or mark again, depending on the current url parameters + doSearchOrMarkFromUrl(); + + // Exported functions + config.hasFocus = hasFocus; + } + + initSearchInteractions(window.search); + + function unfocusSearchbar() { + // hacky, but just focusing a div only works once + const tmp = document.createElement('input'); + tmp.setAttribute('style', 'position: absolute; opacity: 0;'); + searchicon.appendChild(tmp); + tmp.focus(); + tmp.remove(); + } + + // On reload or browser history backwards/forwards events, parse the url and do search or mark + function doSearchOrMarkFromUrl() { + // Check current URL for search request + const url = parseURL(window.location.href); + if (Object.prototype.hasOwnProperty.call(url.params, URL_SEARCH_PARAM) + && url.params[URL_SEARCH_PARAM] !== '') { + showSearch(true); + searchbar.value = decodeURIComponent( + (url.params[URL_SEARCH_PARAM] + '').replace(/\+/g, '%20')); + searchbarKeyUpHandler(); // -> doSearch() + } else { + showSearch(false); + } + + if (Object.prototype.hasOwnProperty.call(url.params, URL_MARK_PARAM)) { + const words = decodeURIComponent(url.params[URL_MARK_PARAM]).split(' '); + marker.mark(words, { + exclude: mark_exclude, + }); + + const markers = document.querySelectorAll('mark'); + const hide = () => { + for (let i = 0; i < markers.length; i++) { + markers[i].classList.add('fade-out'); + window.setTimeout(() => { + marker.unmark(); + }, 300); + } + // also removes the `?URL_MARK_PARAM=` search param so that + // in-page navigation doesn't make highlights unexpectedly appear again + setSearchUrlParameters('', 'replace'); + }; + + for (let i = 0; i < markers.length; i++) { + markers[i].addEventListener('click', hide); + } + } + } + + // Eventhandler for keyevents on `document` + function globalKeyHandler(e) { + if (e.altKey || + e.ctrlKey || + e.metaKey || + e.shiftKey || + e.target.type === 'textarea' || + e.target.type === 'text' || + !hasFocus() && mdbook_something_else_has_focus(e) + ) { + return; + } + + if (e.key === 'Escape') { + e.preventDefault(); + searchbar.classList.remove('active'); + setSearchUrlParameters('', + searchbar.value.trim() !== '' ? 'push' : 'replace'); + if (hasFocus()) { + unfocusSearchbar(); + } + showSearch(false); + marker.unmark(); + } else if (!hasFocus() && (e.key === 's' || e.key === '/')) { + e.preventDefault(); + showSearch(true); + window.scrollTo(0, 0); + searchbar.select(); + } else if (hasFocus() && (e.key === 'ArrowDown' + || e.key === 'Enter')) { + e.preventDefault(); + const first = searchresults.firstElementChild; + if (first !== null) { + unfocusSearchbar(); + first.classList.add('focus'); + if (e.key === 'Enter') { + window.location.assign(first.querySelector('a')); + } + } + } else if (!hasFocus() && (e.key === 'ArrowDown' + || e.key === 'ArrowUp' + || e.key === 'Enter')) { + // not `:focus` because browser does annoying scrolling + const focused = searchresults.querySelector('li.focus'); + if (!focused) { + return; + } + e.preventDefault(); + if (e.key === 'ArrowDown') { + const next = focused.nextElementSibling; + if (next) { + focused.classList.remove('focus'); + next.classList.add('focus'); + } + } else if (e.key === 'ArrowUp') { + focused.classList.remove('focus'); + const prev = focused.previousElementSibling; + if (prev) { + prev.classList.add('focus'); + } else { + searchbar.select(); + } + } else { // Enter + window.location.assign(focused.querySelector('a')); + } + } + } + + function loadSearchScript(url, id) { + if (document.getElementById(id)) { + return; + } + searchbar_outer.classList.add('searching'); + + const script = document.createElement('script'); + script.src = url; + script.id = id; + script.onload = () => init(window.search); + script.onerror = error => { + console.error(`Failed to load \`${url}\`: ${error}`); + }; + document.head.append(script); + } + + function showSearch(yes) { + if (yes) { + loadSearchScript( + window.path_to_searchindex_js || + path_to_root + 'searchindex-8ec871e3.js', + 'mdbook-search-index'); + search_wrap.classList.remove('hidden'); + searchicon.setAttribute('aria-expanded', 'true'); + } else { + search_wrap.classList.add('hidden'); + searchicon.setAttribute('aria-expanded', 'false'); + const results = searchresults.children; + for (let i = 0; i < results.length; i++) { + results[i].classList.remove('focus'); + } + } + } + + function showResults(yes) { + if (yes) { + searchresults_outer.classList.remove('hidden'); + } else { + searchresults_outer.classList.add('hidden'); + } + } + + // Eventhandler for search icon + function searchIconClickHandler() { + if (search_wrap.classList.contains('hidden')) { + showSearch(true); + window.scrollTo(0, 0); + searchbar.select(); + } else { + showSearch(false); + } + } + + // Eventhandler for keyevents while the searchbar is focused + function searchbarKeyUpHandler() { + const searchterm = searchbar.value.trim(); + if (searchterm !== '') { + searchbar.classList.add('active'); + doSearch(searchterm); + } else { + searchbar.classList.remove('active'); + showResults(false); + removeChildren(searchresults); + } + + setSearchUrlParameters(searchterm, 'push_if_new_search_else_replace'); + + // Remove marks + marker.unmark(); + } + + // Update current url with ?URL_SEARCH_PARAM= parameter, remove ?URL_MARK_PARAM and + // `#heading-anchor`. `action` can be one of "push", "replace", + // "push_if_new_search_else_replace" and replaces or pushes a new browser history item. + // "push_if_new_search_else_replace" pushes if there is no `?URL_SEARCH_PARAM=abc` yet. + function setSearchUrlParameters(searchterm, action) { + const url = parseURL(window.location.href); + const first_search = !Object.prototype.hasOwnProperty.call(url.params, URL_SEARCH_PARAM); + + if (searchterm !== '' || action === 'push_if_new_search_else_replace') { + url.params[URL_SEARCH_PARAM] = searchterm; + delete url.params[URL_MARK_PARAM]; + url.hash = ''; + } else { + delete url.params[URL_MARK_PARAM]; + delete url.params[URL_SEARCH_PARAM]; + } + // A new search will also add a new history item, so the user can go back + // to the page prior to searching. A updated search term will only replace + // the url. + if (action === 'push' || action === 'push_if_new_search_else_replace' && first_search ) { + history.pushState({}, document.title, renderURL(url)); + } else if (action === 'replace' || + action === 'push_if_new_search_else_replace' && + !first_search + ) { + history.replaceState({}, document.title, renderURL(url)); + } + } + + function doSearch(searchterm) { + // Don't search the same twice + if (current_searchterm === searchterm) { + return; + } + searchbar_outer.classList.add('searching'); + if (searchindex === null) { + return; + } + + current_searchterm = searchterm; + + // Do the actual search + const results = searchindex.search(searchterm, search_options); + const resultcount = Math.min(results.length, results_options.limit_results); + + // Display search metrics + searchresults_header.innerText = formatSearchMetric(resultcount, searchterm); + + // Clear and insert results + const searchterms = searchterm.split(' '); + removeChildren(searchresults); + for (let i = 0; i < resultcount ; i++) { + const resultElem = document.createElement('li'); + resultElem.innerHTML = formatSearchResult(results[i], searchterms); + searchresults.appendChild(resultElem); + } + + // Display results + showResults(true); + searchbar_outer.classList.remove('searching'); + } + + // Exported functions + search.hasFocus = hasFocus; +})(window.search); diff --git a/typ/mdbook/assets/searchindex-8ec871e3.js b/typ/mdbook/assets/searchindex-8ec871e3.js new file mode 100644 index 00000000..f494646a --- /dev/null +++ b/typ/mdbook/assets/searchindex-8ec871e3.js @@ -0,0 +1 @@ +window.search = Object.assign(window.search, JSON.parse('{"results_options":{"limit_results":30,"teaser_word_count":30},"search_options":{"bool":"OR","expand":true,"fields":{"body":{"boost":1},"breadcrumbs":{"boost":1},"title":{"boost":2}}},"doc_urls":["intro/index.html#introduction","intro/index.html#who-embedded-rust-is-for","intro/index.html#scope","intro/index.html#who-this-book-is-for","intro/index.html#assumptions-and-prerequisites","intro/index.html#other-resources","intro/index.html#translations","intro/index.html#how-to-use-this-book","intro/index.html#contributing-to-this-book","intro/index.html#re-using-this-material","intro/hardware.html#meet-your-hardware","intro/hardware.html#stm32f3discovery-the-f3","intro/no-std.html#a-no_std-rust-environment","intro/no-std.html#hosted-environments","intro/no-std.html#bare-metal-environments","intro/no-std.html#the-libstd-runtime","intro/no-std.html#summary","intro/no-std.html#overview","intro/no-std.html#see-also","intro/tooling.html#tooling","intro/tooling.html#cargo-generate-or-git","intro/tooling.html#cargo-binutils","intro/tooling.html#qemu-system-arm","intro/tooling.html#tooling-for-embedded-rust-debugging","intro/tooling.html#overview","intro/tooling.html#software-that-drives-debugging-tools","intro/tooling.html#probe-rs","intro/tooling.html#openocd-open-on-chip-debugger","intro/tooling.html#debuggers","intro/tooling.html#probe-rs-visual-studio-code-extension","intro/tooling.html#trace32","intro/tooling.html#gdb-gnu-debugger","intro/tooling.html#probes","intro/tooling.html#rusty-probe","intro/tooling.html#st-link","intro/tooling.html#j-link","intro/tooling.html#mcu-link","intro/install.html#installing-the-tools","intro/install.html#rust-toolchain","intro/install.html#cargo-binutils","intro/install.html#cargo-generate","intro/install.html#os-specific-instructions","intro/install/linux.html#linux","intro/install/linux.html#packages","intro/install/linux.html#udev-rules","intro/install/macos.html#macos","intro/install/macos.html#install-tools-with-homebrew","intro/install/macos.html#install-tools-with-macports","intro/install/windows.html#windows","intro/install/windows.html#arm-none-eabi-gdb","intro/install/windows.html#openocd","intro/install/windows.html#qemu","intro/install/windows.html#st-link-usb-driver","intro/install/verify.html#verify-installation","start/index.html#getting-started","start/qemu.html#qemu","start/qemu.html#creating-a-non-standard-rust-program","start/qemu.html#using-cargo-generate","start/qemu.html#using-git","start/qemu.html#using-neither","start/qemu.html#program-overview","start/qemu.html#cross-compiling","start/qemu.html#inspecting","start/qemu.html#running","start/qemu.html#debugging","start/hardware.html#hardware","start/hardware.html#know-your-hardware","start/hardware.html#configuring","start/hardware.html#debugging","start/registers.html#memory-mapped-registers","start/registers.html#board-crate","start/registers.html#micro-architecture-crate","start/registers.html#using-a-peripheral-access-crate-pac","start/registers.html#reading","start/registers.html#writing","start/registers.html#modifying","start/registers.html#using-a-hal-crate","start/semihosting.html#semihosting","start/panicking.html#panicking","start/panicking.html#an-example","start/exceptions.html#exceptions","start/exceptions.html#a-complete-example","start/exceptions.html#the-default-exception-handler","start/exceptions.html#the-hard-fault-handler","start/interrupts.html#interrupts","peripherals/index.html#peripherals","peripherals/index.html#what-are-peripherals","peripherals/index.html#linear-and-real-memory-space","peripherals/index.html#memory-mapped-peripherals","peripherals/a-first-attempt.html#a-first-attempt","peripherals/a-first-attempt.html#the-registers","peripherals/a-first-attempt.html#the-c-approach","peripherals/a-first-attempt.html#volatile-accesses","peripherals/a-first-attempt.html#the-rusty-wrapper","peripherals/borrowck.html#mutable-global-state","peripherals/borrowck.html#what-should-our-rules-be","peripherals/borrowck.html#the-borrow-checker","peripherals/singletons.html#singletons","peripherals/singletons.html#but-why-cant-we-just-use-global-variables","peripherals/singletons.html#how-do-we-do-this-in-rust","peripherals/singletons.html#existing-library-support","peripherals/singletons.html#but-why","peripherals/singletons.html#treat-your-hardware-like-data","static-guarantees/index.html#static-guarantees","static-guarantees/typestate-programming.html#typestate-programming","static-guarantees/typestate-programming.html#strong-types","static-guarantees/state-machines.html#peripherals-as-state-machines","static-guarantees/state-machines.html#hardware-representation","static-guarantees/design-contracts.html#design-contracts","static-guarantees/design-contracts.html#type-states","static-guarantees/design-contracts.html#compile-time-functional-safety","static-guarantees/zero-cost-abstractions.html#zero-cost-abstractions","static-guarantees/zero-cost-abstractions.html#zero-sized-types","static-guarantees/zero-cost-abstractions.html#nesting","portability/index.html#portability","portability/index.html#what-is-embedded-hal","portability/index.html#users-of-the-embedded-hal","portability/index.html#hal-implementation","portability/index.html#driver","portability/index.html#application","concurrency/index.html#concurrency","concurrency/index.html#no-concurrency","concurrency/index.html#global-mutable-data","concurrency/index.html#critical-sections","concurrency/index.html#atomic-access","concurrency/index.html#abstractions-send-and-sync","concurrency/index.html#mutexes","concurrency/index.html#sharing-peripherals","concurrency/index.html#rtic","concurrency/index.html#embassy","concurrency/index.html#real-time-operating-systems","concurrency/index.html#multiple-cores","collections/index.html#collections","collections/index.html#using-alloc","collections/index.html#using-heapless","collections/index.html#trade-offs","collections/index.html#out-of-memory-and-error-handling","collections/index.html#memory-usage","collections/index.html#worst-case-execution-time-wcet","collections/index.html#ease-of-use","design-patterns/index.html#design-patterns","design-patterns/hal/index.html#hal-design-patterns","design-patterns/hal/checklist.html#hal-design-patterns-checklist","design-patterns/hal/naming.html#naming","design-patterns/hal/naming.html#the-crate-is-named-appropriately-c-crate-name","design-patterns/hal/interoperability.html#interoperability","design-patterns/hal/interoperability.html#wrapper-types-provide-a-destructor-method-c-free","design-patterns/hal/interoperability.html#hals-reexport-their-register-access-crate-c-reexport-pac","design-patterns/hal/interoperability.html#types-implement-the-embedded-hal-traits-c-hal-traits","design-patterns/hal/predictability.html#predictability","design-patterns/hal/predictability.html#constructors-are-used-instead-of-extension-traits-c-ctor","design-patterns/hal/predictability.html#methods-are-decorated-with-inline-where-appropriate-c-inline","design-patterns/hal/gpio.html#recommendations-for-gpio-interfaces","design-patterns/hal/gpio.html#pin-types-are-zero-sized-by-default-c-zst-pin","design-patterns/hal/gpio.html#pin-types-provide-methods-to-erase-pin-and-port-c-erased-pin","design-patterns/hal/gpio.html#pin-state-should-be-encoded-as-type-parameters-c-pin-state","c-tips/index.html#tips-for-embedded-c-developers","c-tips/index.html#preprocessor","c-tips/index.html#compile-time-code-selection","c-tips/index.html#compile-time-sizes-and-computation","c-tips/index.html#macros","c-tips/index.html#build-system","c-tips/index.html#cross-compiling","c-tips/index.html#iterators-vs-array-access","c-tips/index.html#references-vs-pointers","c-tips/index.html#volatile-access","c-tips/index.html#packed-and-aligned-types","c-tips/index.html#other-resources","interoperability/index.html#interoperability","interoperability/index.html#interoperability-with-other-build-systems","interoperability/index.html#interoperability-with-rtoss","interoperability/c-with-rust.html#a-little-c-with-your-rust","interoperability/c-with-rust.html#defining-the-interface","interoperability/c-with-rust.html#wrapping-c-functions-and-datatypes","interoperability/c-with-rust.html#automatically-generating-the-interface","interoperability/c-with-rust.html#building-your-cc-code","interoperability/c-with-rust.html#rust-buildrs-build-scripts","interoperability/c-with-rust.html#triggering-external-build-systems","interoperability/c-with-rust.html#building-cc-code-with-the-cc-crate","interoperability/rust-with-c.html#a-little-rust-with-your-c","interoperability/rust-with-c.html#setting-up-a-project","interoperability/rust-with-c.html#building-a-c-api","interoperability/rust-with-c.html#no_mangle","interoperability/rust-with-c.html#extern-c","interoperability/rust-with-c.html#linking-and-greater-project-context","unsorted/index.html#unsorted-topics","unsorted/speed-vs-size.html#optimizations-the-speed-size-tradeoff","unsorted/speed-vs-size.html#no-optimizations","unsorted/speed-vs-size.html#optimizing-dependencies","unsorted/speed-vs-size.html#optimize-for-speed","unsorted/speed-vs-size.html#optimize-for-size","unsorted/math.html#performing-math-functionality-with-no_std","appendix/glossary.html#appendix-a-glossary","appendix/glossary.html#bsp","appendix/glossary.html#fpu","appendix/glossary.html#hal","appendix/glossary.html#i2c","appendix/glossary.html#pac","appendix/glossary.html#spi","appendix/glossary.html#svd","appendix/glossary.html#uart","appendix/glossary.html#usart"],"index":{"fields":["title","body","breadcrumbs"],"pipeline":["trimmer","stopWordFilter","stemmer"],"ref":"id","version":"0.9.5","index":{"body":{"root":{"docs":{},"df":0,"0":{"docs":{"100":{"tf":1.0},"107":{"tf":2.23606797749979},"108":{"tf":2.23606797749979},"111":{"tf":2.0},"122":{"tf":2.0},"123":{"tf":1.7320508075688772},"124":{"tf":1.0},"125":{"tf":1.0},"154":{"tf":1.4142135623730951},"16":{"tf":1.0},"166":{"tf":3.872983346207417},"187":{"tf":1.4142135623730951},"188":{"tf":1.7320508075688772},"62":{"tf":1.7320508075688772},"63":{"tf":1.4142135623730951},"79":{"tf":1.0},"80":{"tf":1.0},"81":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0}},"df":20,".":{"docs":{},"df":0,"1":{"docs":{},"df":0,".":{"docs":{},"df":0,"0":{"docs":{"58":{"tf":1.0}},"df":1},"4":{"docs":{"19":{"tf":1.0}},"df":1}},"0":{"docs":{},"df":0,".":{"docs":{},"df":0,"0":{"docs":{"50":{"tf":1.0},"53":{"tf":1.4142135623730951},"68":{"tf":1.0}},"df":3}}}},"2":{"docs":{},"df":0,".":{"docs":{},"df":0,"0":{"docs":{"81":{"tf":1.0}},"df":1}},"6":{"docs":{"63":{"tf":1.0}},"df":1}},"3":{"docs":{},"df":0,".":{"docs":{},"df":0,"1":{"docs":{"81":{"tf":1.0}},"df":1}}},"5":{"docs":{},"df":0,".":{"docs":{},"df":0,"7":{"docs":{"81":{"tf":1.0}},"df":1}}},"6":{"docs":{},"df":0,".":{"docs":{},"df":0,"1":{"docs":{},"df":0,"/":{"docs":{},"df":0,"s":{"docs":{},"df":0,"r":{"docs":{},"df":0,"c":{"docs":{},"df":0,"/":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"b":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{},"df":0,"s":{"docs":{},"df":0,":":{"docs":{},"df":0,"4":{"docs":{},"df":0,"7":{"docs":{},"df":0,"3":{"docs":{"64":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}}}},"3":{"docs":{"81":{"tf":1.0}},"df":1}}},"8":{"docs":{"19":{"tf":1.0}},"df":1}},"0":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0},"62":{"tf":3.0}},"df":3,"0":{"docs":{"166":{"tf":1.0}},"df":1},"1":{"docs":{"44":{"tf":1.0}},"df":1}},"1":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0},"62":{"tf":1.7320508075688772}},"df":3,"8":{"docs":{"44":{"tf":1.0}},"df":1}},"4":{"docs":{},"df":0,"8":{"docs":{},"df":0,"3":{"docs":{},"df":0,":":{"docs":{},"df":0,"3":{"docs":{},"df":0,"7":{"docs":{},"df":0,"4":{"docs":{},"df":0,"b":{"docs":{"44":{"tf":1.0}},"df":1}}}}}}}},"9":{"docs":{"189":{"tf":1.0},"190":{"tf":1.0}},"df":2},"u":{"docs":{},"df":0,"1":{"docs":{},"df":0,"6":{"docs":{"163":{"tf":1.0}},"df":1}},"3":{"docs":{},"df":0,"2":{"docs":{"159":{"tf":1.0}},"df":1}}},"x":{"docs":{},"df":0,"0":{"docs":{"62":{"tf":3.7416573867739413}},"df":1,"0":{"docs":{"90":{"tf":1.0}},"df":1,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{"61":{"tf":1.0},"68":{"tf":1.0},"83":{"tf":1.4142135623730951}},"df":3},"3":{"docs":{"83":{"tf":1.0}},"df":1}}}},"_":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{"87":{"tf":1.7320508075688772}},"df":1}}}}}},"7":{"docs":{},"df":0,"_":{"docs":{},"df":0,"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"f":{"docs":{"87":{"tf":1.0}},"df":1}}}}}}}},"2":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"_":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{"88":{"tf":1.0}},"df":1}}}}}}}},"4":{"docs":{"90":{"tf":1.0}},"df":1,"8":{"docs":{},"df":0,"3":{"docs":{"53":{"tf":1.0},"68":{"tf":1.0}},"df":2}}},"8":{"docs":{"90":{"tf":1.0}},"df":1,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{"67":{"tf":1.0}},"df":1}}},"4":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{"68":{"tf":1.0}},"df":1}},"9":{"docs":{},"df":0,"6":{"docs":{"68":{"tf":1.0}},"df":1}},"a":{"docs":{},"df":0,"c":{"docs":{"68":{"tf":1.0}},"df":1},"e":{"docs":{"68":{"tf":1.0}},"df":1}},"b":{"docs":{},"df":0,"0":{"docs":{"68":{"tf":1.0}},"df":1},"4":{"docs":{"68":{"tf":1.0}},"df":1},"8":{"docs":{"68":{"tf":1.0}},"df":1},"c":{"docs":{"68":{"tf":1.0}},"df":1}}},"5":{"docs":{},"df":0,"0":{"docs":{},"df":0,"2":{"docs":{"68":{"tf":1.0}},"df":1}}},"9":{"docs":{},"df":0,"4":{"docs":{},"df":0,"a":{"docs":{"83":{"tf":1.0}},"df":1}}}},"1":{"docs":{},"df":0,"6":{"docs":{},"df":0,"d":{"docs":{},"df":0,"f":{"docs":{"83":{"tf":1.0}},"df":1}},"e":{"docs":{},"df":0,"2":{"docs":{"83":{"tf":1.0}},"df":1}}}},"3":{"docs":{},"df":0,"2":{"docs":{},"df":0,"e":{"docs":{},"df":0,"8":{"docs":{"83":{"tf":1.0}},"df":1}}}},"_":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{"66":{"tf":1.0}},"df":1}}}}}}}},"c":{"docs":{"90":{"tf":1.0}},"df":1}},"1":{"docs":{"62":{"tf":1.4142135623730951}},"df":1,"2":{"docs":{},"df":0,"3":{"docs":{},"df":0,"4":{"docs":{"87":{"tf":1.0}},"df":1}}},"5":{"docs":{},"df":0,"1":{"docs":{},"df":0,"8":{"docs":{"68":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"7":{"docs":{},"df":0,"0":{"docs":{"68":{"tf":1.0}},"df":1}}}},"2":{"docs":{"62":{"tf":1.0}},"df":1,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{"87":{"tf":1.0}},"df":1,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{"188":{"tf":2.0},"61":{"tf":1.0},"62":{"tf":1.4142135623730951},"67":{"tf":1.0}},"df":4}}}},"_":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{"66":{"tf":1.4142135623730951},"87":{"tf":1.4142135623730951}},"df":2}}},"1":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{"133":{"tf":1.0}},"df":1}}},"2":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{"133":{"tf":1.4142135623730951}},"df":1}}}},"1":{"docs":{},"df":0,"2":{"docs":{},"df":0,"3":{"docs":{},"df":0,"4":{"docs":{"87":{"tf":1.0}},"df":1}}}},"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"f":{"docs":{"87":{"tf":1.0}},"df":1}}}}}}}},"4":{"docs":{},"df":0,"e":{"docs":{"62":{"tf":1.0}},"df":1}},"5":{"docs":{},"df":0,"6":{"docs":{"62":{"tf":1.0}},"df":1}}},"3":{"docs":{"62":{"tf":1.0}},"df":1,"7":{"docs":{},"df":0,"4":{"docs":{},"df":0,"b":{"docs":{"53":{"tf":1.0},"68":{"tf":1.0}},"df":2}}},"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"f":{"docs":{"83":{"tf":1.0}},"df":1,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{"83":{"tf":1.0}},"df":1}}}},"_":{"docs":{},"df":0,"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"e":{"docs":{"83":{"tf":1.0}},"df":1}}}}}}}}},"4":{"docs":{"62":{"tf":1.7320508075688772},"83":{"tf":1.0}},"df":2,"0":{"docs":{},"df":0,"0":{"docs":{"61":{"tf":1.0},"62":{"tf":1.0},"68":{"tf":1.4142135623730951}},"df":3,"0":{"docs":{},"df":0,"_":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{"87":{"tf":1.0}},"df":1}}}},"1":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{"101":{"tf":1.0}},"df":1}}}}}}},"5":{"docs":{"62":{"tf":1.0}},"df":1}},"1":{"docs":{},"df":0,"4":{"docs":{"68":{"tf":1.0}},"df":1}},"5":{"docs":{},"df":0,"c":{"docs":{"62":{"tf":1.0}},"df":1}}},"5":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"2":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{"62":{"tf":1.0}},"df":1}}}}}}},"6":{"docs":{},"df":0,"1":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{"83":{"tf":1.0}},"df":1}}}}}},"c":{"docs":{"68":{"tf":1.0}},"df":1}}},"7":{"docs":{},"df":0,"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"d":{"docs":{},"df":0,"3":{"docs":{},"df":0,"3":{"docs":{},"df":0,"5":{"docs":{},"df":0,"9":{"docs":{},"df":0,"8":{"docs":{},"df":0,"4":{"docs":{},"df":0,"9":{"docs":{},"df":0,"0":{"docs":{"166":{"tf":1.0}},"df":1},"2":{"docs":{"166":{"tf":1.0}},"df":1},"3":{"docs":{"166":{"tf":1.0}},"df":1}}}}}}}}},"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"9":{"docs":{},"df":0,"0":{"docs":{},"df":0,"9":{"docs":{},"df":0,"a":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{"166":{"tf":1.0}},"df":1},"2":{"docs":{"166":{"tf":1.0}},"df":1},"4":{"docs":{"166":{"tf":1.0}},"df":1}}}},"b":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{"166":{"tf":1.0}},"df":1},"2":{"docs":{"166":{"tf":1.0}},"df":1},"4":{"docs":{"166":{"tf":1.0}},"df":1}}}}}}},"b":{"docs":{},"df":0,"3":{"docs":{},"df":0,"5":{"docs":{},"df":0,"1":{"docs":{},"df":0,"1":{"docs":{},"df":0,"d":{"docs":{},"df":0,"0":{"docs":{"166":{"tf":1.0}},"df":1},"2":{"docs":{"166":{"tf":1.0}},"df":1},"4":{"docs":{"166":{"tf":1.0}},"df":1}}}}}}}}},"f":{"docs":{},"df":0,"d":{"docs":{},"df":0,"0":{"docs":{},"df":0,"d":{"docs":{},"df":0,"8":{"docs":{},"df":0,"4":{"docs":{},"df":0,"c":{"docs":{},"df":0,"6":{"docs":{},"df":0,"0":{"docs":{"166":{"tf":1.0}},"df":1},"2":{"docs":{"166":{"tf":1.0}},"df":1},"4":{"docs":{"166":{"tf":1.0}},"df":1}}}}}}}}}}}},"8":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{"188":{"tf":1.4142135623730951},"68":{"tf":1.4142135623730951}},"df":2}}},"4":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{"188":{"tf":1.4142135623730951},"68":{"tf":1.4142135623730951}},"df":2}},"9":{"docs":{},"df":0,"0":{"docs":{"68":{"tf":1.0}},"df":1}}},"_":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{"88":{"tf":1.0}},"df":1}}}}}},"1":{"docs":{},"df":0,"1":{"docs":{},"df":0,"c":{"docs":{},"df":0,"0":{"docs":{"188":{"tf":1.0}},"df":1}}},"4":{"docs":{},"df":0,"4":{"docs":{},"df":0,"e":{"docs":{"68":{"tf":1.0}},"df":1}}},"9":{"docs":{},"df":0,"1":{"docs":{},"df":0,"8":{"docs":{"68":{"tf":1.0}},"df":1}}}},"2":{"docs":{},"df":0,"2":{"docs":{},"df":0,"7":{"docs":{},"df":0,"0":{"docs":{"68":{"tf":1.0}},"df":1}}},"7":{"docs":{},"df":0,"8":{"docs":{},"df":0,"0":{"docs":{"188":{"tf":1.0}},"df":1}}}}}}},"e":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"_":{"docs":{},"df":0,"e":{"docs":{},"df":0,"0":{"docs":{},"df":0,"1":{"docs":{},"df":0,"0":{"docs":{"91":{"tf":1.0},"92":{"tf":1.4142135623730951},"93":{"tf":1.0}},"df":3}}}}}}}}},"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"e":{"docs":{"83":{"tf":1.0}},"df":1},"f":{"docs":{"87":{"tf":1.0}},"df":1,"_":{"docs":{},"df":0,"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"f":{"docs":{"87":{"tf":1.0}},"df":1}}}}}}}}},"{":{"docs":{},"df":0,":":{"docs":{},"df":0,"0":{"docs":{},"df":0,"8":{"docs":{},"df":0,"x":{"docs":{"93":{"tf":1.0}},"df":1}}}}}}},"1":{"docs":{"107":{"tf":2.23606797749979},"108":{"tf":2.23606797749979},"109":{"tf":1.0},"11":{"tf":1.0},"122":{"tf":1.0},"123":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":1.0},"126":{"tf":1.0},"133":{"tf":1.4142135623730951},"166":{"tf":1.4142135623730951},"188":{"tf":1.0},"189":{"tf":1.0},"44":{"tf":1.4142135623730951},"61":{"tf":1.4142135623730951},"62":{"tf":1.4142135623730951},"64":{"tf":1.4142135623730951},"67":{"tf":1.4142135623730951},"68":{"tf":1.7320508075688772},"72":{"tf":1.0},"77":{"tf":1.4142135623730951},"79":{"tf":1.0},"80":{"tf":1.0},"81":{"tf":1.0},"84":{"tf":1.0}},"df":25,".":{"docs":{},"df":0,"2":{"docs":{},"df":0,"9":{"docs":{},"df":0,".":{"docs":{},"df":0,"0":{"docs":{"190":{"tf":1.0}},"df":1}}}},"3":{"docs":{},"df":0,"0":{"docs":{"168":{"tf":1.0}},"df":1},"1":{"docs":{"159":{"tf":1.0},"19":{"tf":1.4142135623730951},"38":{"tf":1.0}},"df":3,".":{"docs":{},"df":0,"1":{"docs":{"38":{"tf":1.0}},"df":1}}}},"4":{"docs":{"12":{"tf":1.0}},"df":1},"5":{"docs":{},"df":0,"v":{"docs":{"107":{"tf":1.4142135623730951},"108":{"tf":1.4142135623730951}},"df":2}},"c":{"docs":{},"df":0,"f":{"docs":{},"df":0,"g":{"docs":{"53":{"tf":1.4142135623730951}},"df":1}}}},"0":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0},"11":{"tf":1.0},"64":{"tf":1.0}},"df":4,"0":{"docs":{"53":{"tf":1.0},"62":{"tf":1.0},"68":{"tf":1.0}},"df":3,"0":{"docs":{"53":{"tf":1.7320508075688772},"68":{"tf":1.7320508075688772}},"df":2}},"2":{"docs":{},"df":0,"4":{"docs":{"159":{"tf":1.0},"188":{"tf":1.4142135623730951},"61":{"tf":1.0},"62":{"tf":1.0},"67":{"tf":1.0}},"df":5}},"3":{"docs":{},"df":0,"8":{"docs":{},"df":0,"0":{"docs":{"68":{"tf":1.0}},"df":1}}}},"1":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0},"64":{"tf":1.0},"68":{"tf":1.4142135623730951}},"df":4,"0":{"docs":{},"df":0,"0":{"docs":{"188":{"tf":1.0}},"df":1}},"5":{"docs":{},"df":0,"2":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"_":{"docs":{},"df":0,"u":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{},"df":0,".":{"docs":{},"df":0,"b":{"docs":{},"df":0,"p":{"docs":{"76":{"tf":1.0}},"df":1}}}}}}}}}}},"8":{"docs":{},"df":0,"4":{"docs":{"18":{"tf":1.0}},"df":1}}},"2":{"docs":{"38":{"tf":1.0},"64":{"tf":1.0},"81":{"tf":1.0}},"df":3,"3":{"docs":{},"df":0,"4":{"docs":{},"df":0,"5":{"docs":{},"df":0,"6":{"docs":{},"df":0,"7":{"docs":{},"df":0,"8":{"docs":{},"df":0,"9":{"docs":{"81":{"tf":1.0}},"df":1}}}}}}},"5":{"docs":{"88":{"tf":1.0}},"df":1},"8":{"docs":{"159":{"tf":1.0}},"df":1},":":{"docs":{},"df":0,"3":{"docs":{},"df":0,"4":{"docs":{"44":{"tf":1.0}},"df":1}}}},"3":{"docs":{"44":{"tf":1.0},"64":{"tf":2.0},"68":{"tf":1.4142135623730951}},"df":3,".":{"docs":{},"df":0,"1":{"docs":{},"df":0,"\\\\":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"\\\\":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"50":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}},"4":{"docs":{"64":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"0":{"docs":{},"df":0,"4":{"docs":{"43":{"tf":1.0}},"df":1}}},"5":{"docs":{},"df":0,"7":{"docs":{},"df":0,"0":{"docs":{"62":{"tf":1.0}},"df":1}}}},"5":{"docs":{"64":{"tf":1.0}},"df":1,"3":{"docs":{},"df":0,"2":{"docs":{},"df":0,"0":{"docs":{},"df":0,"4":{"docs":{"62":{"tf":1.0}},"df":1}}}}},"6":{"docs":{"163":{"tf":1.0},"64":{"tf":1.0}},"df":2,".":{"docs":{},"df":0,"0":{"docs":{},"df":0,"4":{"docs":{"43":{"tf":1.0}},"df":1}}}},"7":{"docs":{"44":{"tf":1.0},"68":{"tf":1.0}},"df":2,"0":{"docs":{},"df":0,"8":{"docs":{"188":{"tf":1.0}},"df":1}}},"8":{"docs":{"189":{"tf":1.0},"190":{"tf":1.0},"38":{"tf":1.0},"62":{"tf":1.0}},"df":4,".":{"docs":{},"df":0,"0":{"docs":{},"df":0,"4":{"docs":{"43":{"tf":1.0}},"df":1}}},"9":{"docs":{"44":{"tf":1.0}},"df":1,"2":{"docs":{"62":{"tf":1.0}},"df":1}}},"9":{"docs":{"62":{"tf":1.4142135623730951},"68":{"tf":1.0}},"df":2,"4":{"docs":{"189":{"tf":1.0}},"df":1},"7":{"docs":{},"df":0,"0":{"docs":{"86":{"tf":1.0}},"df":1}},"8":{"docs":{},"df":0,"0":{"docs":{"86":{"tf":1.0}},"df":1}}},"g":{"docs":{},"df":0,"b":{"docs":{"12":{"tf":1.0}},"df":1}}},"2":{"docs":{"104":{"tf":1.0},"109":{"tf":1.0},"189":{"tf":2.23606797749979},"190":{"tf":1.0},"62":{"tf":1.0},"79":{"tf":1.0}},"df":6,"\'":{"docs":{"62":{"tf":1.0}},"df":1},".":{"docs":{},"df":0,".":{"docs":{},"df":0,"3":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0}},"df":2}},"9":{"docs":{},"df":0,"1":{"docs":{},"df":0,"3":{"docs":{},"df":0,"8":{"docs":{},"df":0,"7":{"docs":{},"df":0,"9":{"docs":{"68":{"tf":1.0}},"df":1}}}},"9":{"docs":{},"df":0,"8":{"docs":{},"df":0,"8":{"docs":{},"df":0,"1":{"docs":{"53":{"tf":1.0}},"df":1}}}}}}},"0":{"docs":{},"df":0,"1":{"docs":{},"df":0,"7":{"docs":{"53":{"tf":1.0}},"df":1},"8":{"docs":{"189":{"tf":1.0},"190":{"tf":1.0},"38":{"tf":1.0},"4":{"tf":1.4142135623730951},"49":{"tf":1.0},"58":{"tf":1.0},"78":{"tf":1.0}},"df":7},"9":{"docs":{"39":{"tf":1.0}},"df":1}},"3":{"docs":{},"df":0,"5":{"docs":{"62":{"tf":1.0}},"df":1}}},"1":{"docs":{},"df":0,"1":{"docs":{},"df":0,"2":{"docs":{"72":{"tf":1.0}},"df":1}}},"2":{"docs":{},"df":0,"5":{"docs":{"190":{"tf":1.4142135623730951}},"df":1}},"4":{"docs":{"11":{"tf":1.0}},"df":1,"8":{"docs":{},"df":0,"9":{"docs":{"68":{"tf":1.0}},"df":1}}},"5":{"docs":{"190":{"tf":1.0}},"df":1,"6":{"docs":{"11":{"tf":1.0},"66":{"tf":1.4142135623730951},"72":{"tf":1.0}},"df":3,"k":{"docs":{"61":{"tf":1.0},"67":{"tf":1.0}},"df":2}}},"6":{"docs":{"189":{"tf":1.0}},"df":1,"4":{"docs":{"72":{"tf":1.0}},"df":1}},"7":{"docs":{"43":{"tf":1.0}},"df":1,"5":{"docs":{"190":{"tf":1.4142135623730951}},"df":1}},"9":{"docs":{},"df":0,"5":{"docs":{},"df":0,"8":{"docs":{"62":{"tf":1.0}},"df":1}}}},"3":{"docs":{"109":{"tf":1.0},"12":{"tf":1.0},"189":{"tf":2.23606797749979},"190":{"tf":1.0},"79":{"tf":1.0}},"df":5,".":{"docs":{},"df":0,"0":{"docs":{},"df":0,".":{"docs":{},"df":0,"0":{"docs":{"19":{"tf":1.0}},"df":1}}},"3":{"docs":{"11":{"tf":1.0}},"df":1}},"2":{"docs":{"168":{"tf":1.0},"52":{"tf":1.0},"62":{"tf":1.0},"66":{"tf":1.0},"72":{"tf":1.4142135623730951},"87":{"tf":1.0},"88":{"tf":1.4142135623730951},"90":{"tf":2.0},"91":{"tf":1.0}},"df":9,"/":{"docs":{},"df":0,"6":{"docs":{},"df":0,"4":{"docs":{"12":{"tf":1.0}},"df":1}}}},"3":{"docs":{},"df":0,"3":{"docs":{},"df":0,"3":{"docs":{"64":{"tf":2.0},"68":{"tf":2.0}},"df":2}}},"4":{"docs":{},"df":0,"6":{"docs":{},"df":0,"0":{"docs":{"68":{"tf":1.0}},"df":1}},"9":{"docs":{},"df":0,"0":{"docs":{"188":{"tf":1.0}},"df":1}}}},"4":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0},"12":{"tf":1.0},"188":{"tf":1.4142135623730951},"5":{"tf":1.0},"53":{"tf":1.0},"68":{"tf":1.0},"72":{"tf":1.0},"79":{"tf":1.0}},"df":9,".":{"docs":{},"df":0,"8":{"docs":{},"df":0,"2":{"docs":{},"df":0,"8":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{"191":{"tf":1.4142135623730951}},"df":1}}}}}},"0":{"docs":{"62":{"tf":1.4142135623730951},"66":{"tf":1.0}},"df":2,"0":{"docs":{"62":{"tf":1.0}},"df":1},"4":{"docs":{"62":{"tf":1.0}},"df":1},"6":{"docs":{"62":{"tf":1.0}},"df":1},"9":{"docs":{},"df":0,"6":{"docs":{"166":{"tf":1.0}},"df":1}},"a":{"docs":{"62":{"tf":1.0}},"df":1},"k":{"docs":{"67":{"tf":1.0}},"df":1}},"5":{"docs":{"62":{"tf":1.0}},"df":1},"6":{"docs":{"62":{"tf":1.4142135623730951}},"df":1},"7":{"docs":{},"df":0,"3":{"docs":{"64":{"tf":1.0}},"df":1}},"8":{"docs":{"11":{"tf":1.0}},"df":1},"9":{"docs":{},"df":0,"2":{"docs":{},"df":0,"9":{"docs":{"62":{"tf":1.0}},"df":1}}},"c":{"docs":{"62":{"tf":1.0}},"df":1}},"5":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0}},"df":2,"1":{"docs":{},"df":0,"2":{"docs":{"87":{"tf":1.0}},"df":1}},"2":{"docs":{"62":{"tf":1.4142135623730951}},"df":1,"8":{"docs":{"72":{"tf":1.0}},"df":1}},"6":{"docs":{},"df":0,"7":{"docs":{"62":{"tf":1.0}},"df":1}}},"6":{"docs":{"188":{"tf":1.0},"53":{"tf":1.0},"64":{"tf":1.0},"68":{"tf":1.0}},"df":4,".":{"docs":{},"df":0,"2":{"docs":{"11":{"tf":1.0}},"df":1}},"1":{"docs":{"169":{"tf":1.0}},"df":1},"2":{"docs":{"170":{"tf":1.0}},"df":1},"4":{"docs":{"52":{"tf":1.0},"87":{"tf":1.0}},"df":2,"k":{"docs":{"61":{"tf":1.0}},"df":1}},"5":{"docs":{},"df":0,"6":{"docs":{"62":{"tf":1.0}},"df":1},"7":{"docs":{"62":{"tf":1.0}},"df":1},"8":{"docs":{"62":{"tf":1.0}},"df":1},"9":{"docs":{"62":{"tf":1.0}},"df":1},"a":{"docs":{"62":{"tf":1.0}},"df":1},"c":{"docs":{"62":{"tf":1.0}},"df":1}},"6":{"docs":{},"df":0,"0":{"docs":{"62":{"tf":1.0}},"df":1},"2":{"docs":{"62":{"tf":1.0}},"df":1},"3":{"docs":{"62":{"tf":1.0}},"df":1}}},"7":{"docs":{"49":{"tf":1.0},"64":{"tf":1.0}},"df":2,".":{"docs":{},"df":0,"1":{"docs":{},"df":0,"0":{"docs":{"19":{"tf":1.0}},"df":1},"1":{"docs":{"19":{"tf":1.0}},"df":1},"2":{"docs":{"19":{"tf":1.4142135623730951}},"df":1}}},"2":{"docs":{"11":{"tf":1.0}},"df":1},"4":{"docs":{},"df":0,"6":{"docs":{},"df":0,"8":{"docs":{"68":{"tf":1.0}},"df":1}}},"5":{"docs":{"190":{"tf":1.0}},"df":1},"f":{"docs":{"62":{"tf":1.0}},"df":1}},"8":{"docs":{"11":{"tf":1.0},"12":{"tf":1.0},"134":{"tf":1.4142135623730951},"64":{"tf":1.0},"88":{"tf":1.0}},"df":5,".":{"docs":{},"df":0,"1":{"docs":{"19":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"0":{"docs":{},"df":0,".":{"docs":{},"df":0,"2":{"docs":{},"df":0,"0":{"docs":{},"df":0,"1":{"docs":{},"df":0,"8":{"docs":{},"df":0,"0":{"docs":{},"df":0,"3":{"docs":{},"df":0,"1":{"docs":{},"df":0,"5":{"docs":{"49":{"tf":1.0}},"df":1}}}}}}}}}}}}},"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"9":{"docs":{},"df":0,"4":{"docs":{},"df":0,"2":{"docs":{"83":{"tf":1.0}},"df":1},"6":{"docs":{"83":{"tf":1.0}},"df":1},"a":{"docs":{"83":{"tf":1.0}},"df":1},"c":{"docs":{"83":{"tf":1.0}},"df":1}}}}},"m":{"docs":{},"df":0,"h":{"docs":{},"df":0,"z":{"docs":{"72":{"tf":1.0}},"df":1}}}},"6":{"docs":{},"df":0,"7":{"docs":{"62":{"tf":1.0}},"df":1}}},"9":{"docs":{"64":{"tf":1.0},"81":{"tf":1.4142135623730951}},"df":2,"0":{"docs":{},"df":0,"6":{"docs":{},"df":0,"0":{"docs":{"188":{"tf":1.0}},"df":1}}},"2":{"docs":{"62":{"tf":1.0}},"df":1},"5":{"docs":{},"df":0,"0":{"docs":{"53":{"tf":1.7320508075688772},"68":{"tf":1.7320508075688772}},"df":2}}},"_":{"docs":{"101":{"tf":1.4142135623730951},"109":{"tf":1.4142135623730951},"111":{"tf":2.0},"133":{"tf":1.4142135623730951},"134":{"tf":1.0},"191":{"tf":1.0},"60":{"tf":1.4142135623730951},"63":{"tf":1.4142135623730951},"64":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0},"76":{"tf":1.0},"77":{"tf":1.7320508075688772},"78":{"tf":2.0},"79":{"tf":1.0},"81":{"tf":1.0},"83":{"tf":1.0},"98":{"tf":1.0}},"df":18,"_":{"docs":{},"df":0,"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"p":{"docs":{"62":{"tf":1.0}},"df":1}}},"p":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"62":{"tf":1.0}},"df":1}}}}}}}}},"c":{"docs":{"125":{"tf":1.4142135623730951}},"df":1},"p":{"docs":{"155":{"tf":1.7320508075688772}},"df":1},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"61":{"tf":1.0}},"df":1}}}}}}}}},"e":{"docs":{},"df":0,"x":{"docs":{},"df":0,"t":{"docs":{"61":{"tf":1.0}},"df":1}}}}},"y":{"docs":{"79":{"tf":1.0}},"df":1}},"a":{"docs":{},"df":0,"/":{"docs":{},"df":0,"b":{"docs":{"44":{"tf":1.0}},"df":1}},"5":{"docs":{},"df":0,"3":{"docs":{"12":{"tf":1.0}},"df":1}},"b":{"docs":{},"df":0,"b":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"v":{"docs":{},"df":0,"i":{"docs":{"192":{"tf":1.0}},"df":1}}}}},"i":{"docs":{"171":{"tf":1.4142135623730951},"173":{"tf":1.0},"181":{"tf":1.0},"183":{"tf":1.7320508075688772},"62":{"tf":1.0}},"df":5,"l":{"docs":{"111":{"tf":1.0},"128":{"tf":1.0}},"df":2}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"78":{"tf":2.0}},"df":1}},"v":{"docs":{"100":{"tf":1.0},"107":{"tf":1.4142135623730951},"116":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":1.0},"126":{"tf":1.0},"159":{"tf":1.0},"165":{"tf":1.0},"173":{"tf":1.0},"175":{"tf":1.0},"5":{"tf":1.0},"61":{"tf":1.0},"62":{"tf":1.0},"64":{"tf":1.0},"68":{"tf":1.0},"72":{"tf":1.0},"77":{"tf":1.0},"91":{"tf":1.0},"92":{"tf":1.0}},"df":19}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{"80":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"11":{"tf":1.0}},"df":1}}}},"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"100":{"tf":1.0},"111":{"tf":1.0},"112":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.7320508075688772},"120":{"tf":1.0},"125":{"tf":1.4142135623730951},"126":{"tf":1.4142135623730951},"127":{"tf":1.0},"141":{"tf":1.0},"15":{"tf":1.0},"153":{"tf":1.0},"165":{"tf":1.0},"190":{"tf":1.4142135623730951},"195":{"tf":1.0},"197":{"tf":1.0},"70":{"tf":1.0}},"df":17}}}}}}},"c":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"11":{"tf":1.0},"118":{"tf":1.0}},"df":2}}}}}}},"p":{"docs":{},"df":0,"t":{"docs":{"126":{"tf":1.0},"93":{"tf":1.0}},"df":2}},"s":{"docs":{},"df":0,"s":{"docs":{"101":{"tf":1.4142135623730951},"103":{"tf":1.0},"11":{"tf":1.4142135623730951},"112":{"tf":1.0},"114":{"tf":1.0},"117":{"tf":1.0},"121":{"tf":1.0},"122":{"tf":2.0},"123":{"tf":1.7320508075688772},"125":{"tf":1.7320508075688772},"126":{"tf":2.0},"127":{"tf":2.449489742783178},"128":{"tf":1.4142135623730951},"142":{"tf":1.0},"144":{"tf":1.0},"147":{"tf":1.7320508075688772},"15":{"tf":1.0},"163":{"tf":1.7320508075688772},"164":{"tf":1.7320508075688772},"165":{"tf":2.23606797749979},"195":{"tf":1.0},"197":{"tf":1.4142135623730951},"36":{"tf":1.0},"69":{"tf":1.7320508075688772},"70":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.7320508075688772},"73":{"tf":1.0},"74":{"tf":1.0},"79":{"tf":1.0},"80":{"tf":1.4142135623730951},"87":{"tf":1.4142135623730951},"92":{"tf":2.0},"95":{"tf":1.4142135623730951}},"df":34,"o":{"docs":{},"df":0,"r":{"docs":{"72":{"tf":1.0}},"df":1}}}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{"78":{"tf":1.0}},"df":1}},"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"127":{"tf":1.0},"84":{"tf":1.0}},"df":2}}}},"u":{"docs":{},"df":0,"r":{"docs":{"62":{"tf":1.0}},"df":1}}},"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"e":{"docs":{},"df":0,"v":{"docs":{"119":{"tf":1.0},"137":{"tf":1.0}},"df":2}}}},"q":{"docs":{},"df":0,"u":{"docs":{},"df":0,"i":{"docs":{},"df":0,"r":{"docs":{"126":{"tf":1.4142135623730951}},"df":1}}}},"t":{"docs":{"112":{"tf":1.0},"142":{"tf":1.0},"87":{"tf":1.0}},"df":3,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"100":{"tf":1.0},"105":{"tf":1.0},"87":{"tf":1.0}},"df":3}},"v":{"docs":{"68":{"tf":1.0},"76":{"tf":1.0},"87":{"tf":1.0}},"df":3}},"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"11":{"tf":1.0},"111":{"tf":1.4142135623730951},"112":{"tf":1.0},"122":{"tf":1.0},"125":{"tf":1.0},"127":{"tf":1.0},"137":{"tf":1.0},"147":{"tf":1.0},"160":{"tf":1.0},"187":{"tf":1.0},"63":{"tf":1.0},"82":{"tf":1.0},"84":{"tf":1.0},"86":{"tf":1.4142135623730951},"87":{"tf":1.0},"92":{"tf":1.4142135623730951}},"df":16},"t":{"docs":{"118":{"tf":1.0}},"df":1}}}}},"d":{"docs":{"61":{"tf":1.0},"8":{"tf":1.0}},"df":2,"a":{"docs":{"4":{"tf":1.0}},"df":1,"p":{"docs":{},"df":0,"t":{"docs":{"117":{"tf":1.4142135623730951},"119":{"tf":1.0},"53":{"tf":1.0},"68":{"tf":1.0}},"df":4,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"_":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"r":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"y":{"docs":{"53":{"tf":1.0},"68":{"tf":1.0}},"df":2}}}}}}}}}}}}}}}}},"d":{"docs":{"124":{"tf":1.0},"131":{"tf":1.0},"150":{"tf":1.0},"155":{"tf":1.0},"161":{"tf":1.0},"21":{"tf":1.0},"38":{"tf":2.8284271247461903},"39":{"tf":1.0},"49":{"tf":1.0},"6":{"tf":1.0},"61":{"tf":1.4142135623730951},"63":{"tf":1.0},"64":{"tf":1.0},"67":{"tf":1.0}},"df":14,"i":{"docs":{},"df":0,"t":{"docs":{"115":{"tf":1.4142135623730951},"128":{"tf":1.0},"141":{"tf":1.0},"150":{"tf":1.0},"155":{"tf":1.4142135623730951},"158":{"tf":1.0},"160":{"tf":1.0},"20":{"tf":1.0},"56":{"tf":1.0}},"df":9,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"100":{"tf":1.0},"102":{"tf":1.0},"105":{"tf":1.0},"137":{"tf":1.0},"155":{"tf":1.0},"158":{"tf":1.0},"24":{"tf":1.0}},"df":7}}}}},"r":{"docs":{"188":{"tf":1.4142135623730951},"62":{"tf":1.0}},"df":2,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"157":{"tf":1.0},"166":{"tf":1.4142135623730951},"62":{"tf":1.0},"66":{"tf":2.0},"68":{"tf":1.7320508075688772},"80":{"tf":1.0},"86":{"tf":1.0},"87":{"tf":3.4641016151377544},"88":{"tf":1.7320508075688772}},"df":9}}}}},"j":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"20":{"tf":1.0},"61":{"tf":1.0}},"df":2}}}},"v":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{"191":{"tf":1.0},"30":{"tf":1.0},"35":{"tf":1.0},"68":{"tf":1.0},"84":{"tf":1.0}},"df":5},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{"1":{"tf":1.0},"128":{"tf":1.0},"21":{"tf":1.0}},"df":3}}}}}}},"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"123":{"tf":1.0},"124":{"tf":1.0},"186":{"tf":1.0}},"df":3}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{"189":{"tf":1.0},"36":{"tf":1.0}},"df":2}}}}},"g":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"188":{"tf":1.0},"44":{"tf":1.0},"64":{"tf":1.0},"67":{"tf":1.0},"74":{"tf":1.0},"80":{"tf":1.0}},"df":6,"s":{"docs":{},"df":0,"t":{"docs":{"161":{"tf":1.0}},"df":1}}}}},"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"14":{"tf":1.0},"16":{"tf":1.0},"37":{"tf":1.0},"70":{"tf":1.0}},"df":4}}}}},"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"175":{"tf":1.0}},"df":1}}}},"i":{"docs":{},"df":0,"m":{"docs":{"140":{"tf":1.0},"144":{"tf":1.0},"26":{"tf":1.0},"70":{"tf":1.0}},"df":4}},"k":{"docs":{},"df":0,"a":{"docs":{"132":{"tf":1.4142135623730951},"187":{"tf":1.0},"82":{"tf":1.0}},"df":3}},"l":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"b":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{"191":{"tf":1.0}},"df":1}}}}},"i":{"docs":{},"df":0,"a":{"docs":{"168":{"tf":1.0}},"df":1},"g":{"docs":{},"df":0,"n":{"docs":{"133":{"tf":2.0},"142":{"tf":1.0},"166":{"tf":3.1622776601683795},"29":{"tf":1.0}},"df":4,"(":{"docs":{},"df":0,"4":{"docs":{"61":{"tf":1.4142135623730951}},"df":1}},"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"k":{"docs":{"133":{"tf":1.4142135623730951}},"df":1}}}}}}},"k":{"docs":{"36":{"tf":1.0}},"df":1}},"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{"122":{"tf":1.4142135623730951},"132":{"tf":2.0},"133":{"tf":3.872983346207417},"134":{"tf":2.23606797749979},"135":{"tf":1.0},"136":{"tf":2.0},"137":{"tf":2.23606797749979},"138":{"tf":1.4142135623730951},"139":{"tf":2.0},"14":{"tf":1.4142135623730951},"168":{"tf":1.0},"17":{"tf":2.0},"61":{"tf":1.0},"62":{"tf":1.4142135623730951},"78":{"tf":1.0}},"df":15,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"133":{"tf":1.0}},"df":1}}}}}},":":{"docs":{},"df":0,":":{"docs":{},"df":0,"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{"168":{"tf":1.0}},"df":1}}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,".":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{"136":{"tf":1.0},"138":{"tf":1.0}},"df":2}}}}},":":{"docs":{},"df":0,":":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{"133":{"tf":1.0}},"df":1}}}}}}}}}},"_":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"_":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"l":{"docs":{"133":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}},"w":{"docs":{"102":{"tf":1.4142135623730951},"105":{"tf":1.0},"107":{"tf":1.4142135623730951},"114":{"tf":1.0},"118":{"tf":1.0},"125":{"tf":1.0},"126":{"tf":1.4142135623730951},"154":{"tf":1.0},"158":{"tf":1.0},"164":{"tf":1.0},"180":{"tf":1.0},"26":{"tf":1.0},"28":{"tf":1.0},"31":{"tf":1.0},"32":{"tf":1.0},"84":{"tf":1.0},"86":{"tf":1.4142135623730951},"91":{"tf":1.0},"99":{"tf":1.4142135623730951}},"df":19,"(":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{"146":{"tf":1.0},"153":{"tf":1.0},"154":{"tf":1.0},"155":{"tf":1.0},"158":{"tf":1.0},"159":{"tf":1.0}},"df":6}}}}}}}},"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{"11":{"tf":1.0},"126":{"tf":1.0},"14":{"tf":1.0},"174":{"tf":1.0},"19":{"tf":1.0}},"df":5,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{"159":{"tf":1.0},"188":{"tf":1.0},"24":{"tf":1.0}},"df":3}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{"104":{"tf":1.0},"126":{"tf":1.0},"147":{"tf":1.0},"156":{"tf":1.0},"175":{"tf":1.0},"64":{"tf":1.0},"69":{"tf":1.0},"77":{"tf":1.0},"96":{"tf":1.0}},"df":9}}}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"n":{"docs":{"103":{"tf":1.0},"124":{"tf":1.0},"128":{"tf":1.0},"157":{"tf":1.0},"68":{"tf":1.0},"76":{"tf":1.0}},"df":6}}},"h":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{"100":{"tf":1.0},"104":{"tf":1.0},"107":{"tf":1.0},"112":{"tf":1.0},"160":{"tf":1.0},"161":{"tf":1.0},"87":{"tf":1.0},"99":{"tf":1.0}},"df":8}}}}}},"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"y":{"docs":{"122":{"tf":1.0},"126":{"tf":1.0},"128":{"tf":1.4142135623730951},"136":{"tf":1.0},"14":{"tf":1.0},"147":{"tf":1.0},"164":{"tf":1.0},"166":{"tf":1.0},"168":{"tf":1.0},"173":{"tf":1.0},"62":{"tf":1.0},"84":{"tf":1.4142135623730951},"95":{"tf":1.0},"98":{"tf":1.0}},"df":14}}}},"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"87":{"tf":1.0}},"df":1}}}}},"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"g":{"docs":{"115":{"tf":1.0}},"df":1}},"y":{"docs":{},"df":0,"s":{"docs":{"184":{"tf":1.0}},"df":1,"i":{"docs":{"111":{"tf":1.0},"137":{"tf":1.0},"30":{"tf":1.0}},"df":3}},"z":{"docs":{"137":{"tf":1.0},"32":{"tf":1.0}},"df":2}}}},"d":{"docs":{},"df":0,"/":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"184":{"tf":1.0}},"df":1}}}},"o":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{"105":{"tf":1.0},"108":{"tf":1.0},"125":{"tf":1.0},"126":{"tf":1.0},"130":{"tf":1.0},"158":{"tf":1.0},"4":{"tf":1.0},"60":{"tf":1.0},"64":{"tf":1.4142135623730951},"66":{"tf":1.0},"68":{"tf":1.4142135623730951},"84":{"tf":1.0},"87":{"tf":1.0}},"df":13}}},"s":{"docs":{},"df":0,"w":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"61":{"tf":1.0}},"df":1}}}},"t":{"docs":{},"df":0,"i":{"docs":{"163":{"tf":1.0}},"df":1}},"y":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{"127":{"tf":1.0},"136":{"tf":1.0},"16":{"tf":1.0},"5":{"tf":1.0},"64":{"tf":1.0},"78":{"tf":1.0}},"df":6}},"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"y":{"docs":{"123":{"tf":1.0},"68":{"tf":1.0}},"df":2}},"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"125":{"tf":1.0},"92":{"tf":1.0}},"df":2}}}}}},"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"9":{"tf":1.0}},"df":1}},"r":{"docs":{},"df":0,"t":{"docs":{"133":{"tf":1.0},"179":{"tf":1.0}},"df":2}}},"i":{"docs":{"103":{"tf":1.0},"115":{"tf":1.4142135623730951},"128":{"tf":1.4142135623730951},"134":{"tf":1.0},"139":{"tf":1.7320508075688772},"141":{"tf":1.0},"155":{"tf":1.4142135623730951},"16":{"tf":1.7320508075688772},"171":{"tf":1.0},"179":{"tf":1.0},"183":{"tf":1.0},"184":{"tf":1.0},"53":{"tf":1.4142135623730951},"68":{"tf":1.4142135623730951},"69":{"tf":1.0},"71":{"tf":1.4142135623730951},"76":{"tf":2.0},"77":{"tf":1.0},"86":{"tf":1.0},"88":{"tf":1.0},"93":{"tf":1.4142135623730951}},"df":21},"p":{"docs":{"100":{"tf":1.0},"188":{"tf":2.23606797749979},"55":{"tf":1.7320508075688772},"57":{"tf":1.7320508075688772},"58":{"tf":2.0},"59":{"tf":1.4142135623730951},"62":{"tf":2.6457513110645907},"63":{"tf":1.0},"67":{"tf":1.7320508075688772},"83":{"tf":1.0}},"df":10,"a":{"docs":{},"df":0,"r":{"docs":{"137":{"tf":1.0}},"df":1}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"78":{"tf":1.0}},"df":1}},"n":{"docs":{},"df":0,"d":{"docs":{"190":{"tf":1.0},"44":{"tf":1.0}},"df":2}}},"l":{"docs":{},"df":0,"i":{"docs":{"103":{"tf":1.4142135623730951},"11":{"tf":1.0},"12":{"tf":1.0},"158":{"tf":1.0},"63":{"tf":1.0},"86":{"tf":1.0}},"df":6,"c":{"docs":{"102":{"tf":1.4142135623730951},"114":{"tf":1.0},"115":{"tf":2.0},"119":{"tf":1.4142135623730951},"125":{"tf":1.7320508075688772},"130":{"tf":1.0},"138":{"tf":1.4142135623730951},"148":{"tf":1.0},"151":{"tf":1.0},"154":{"tf":1.0},"183":{"tf":1.0},"188":{"tf":1.0},"33":{"tf":1.0},"4":{"tf":1.0},"56":{"tf":1.7320508075688772},"78":{"tf":1.4142135623730951}},"df":16}}},"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"114":{"tf":1.4142135623730951},"115":{"tf":1.0},"121":{"tf":1.0},"123":{"tf":1.0},"129":{"tf":1.0},"92":{"tf":1.0},"93":{"tf":1.0}},"df":7}}},"p":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"101":{"tf":1.0},"142":{"tf":1.0},"160":{"tf":1.0},"69":{"tf":1.0},"76":{"tf":1.0},"80":{"tf":1.0},"9":{"tf":1.0}},"df":7}}}}}},"t":{"docs":{"43":{"tf":1.4142135623730951}},"df":1}},"r":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"160":{"tf":1.0},"87":{"tf":1.0}},"df":2}}}}}}},"c":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{"104":{"tf":1.0}},"df":1}},"h":{"docs":{"43":{"tf":1.0}},"df":1,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"129":{"tf":1.0},"158":{"tf":1.0},"19":{"tf":1.0},"2":{"tf":1.7320508075688772},"21":{"tf":1.0},"30":{"tf":1.0},"38":{"tf":2.8284271247461903},"61":{"tf":1.4142135623730951},"69":{"tf":1.0},"7":{"tf":1.4142135623730951},"70":{"tf":1.0},"84":{"tf":1.0}},"df":12}}}}}},"v":{"docs":{"175":{"tf":1.7320508075688772},"178":{"tf":1.0}},"df":2}}}},"e":{"docs":{},"df":0,"a":{"docs":{"130":{"tf":1.0}},"df":1}},"g":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"15":{"tf":1.0},"173":{"tf":1.4142135623730951},"187":{"tf":1.0},"62":{"tf":1.0},"72":{"tf":1.4142135623730951},"74":{"tf":1.4142135623730951},"75":{"tf":1.7320508075688772},"82":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0},"93":{"tf":1.0}},"df":11}}}}}},"m":{"docs":{"11":{"tf":1.0},"19":{"tf":1.7320508075688772},"190":{"tf":1.0},"199":{"tf":1.0},"2":{"tf":1.0},"21":{"tf":1.0},"22":{"tf":1.0},"30":{"tf":1.0},"35":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.0},"43":{"tf":3.3166247903554},"46":{"tf":1.0},"47":{"tf":1.0},"49":{"tf":1.7320508075688772},"62":{"tf":1.7320508075688772},"63":{"tf":2.23606797749979},"64":{"tf":2.0},"66":{"tf":1.4142135623730951},"68":{"tf":2.449489742783178},"7":{"tf":1.0},"71":{"tf":1.0},"77":{"tf":2.23606797749979},"78":{"tf":1.0},"79":{"tf":1.0},"81":{"tf":1.0},"83":{"tf":1.0},"90":{"tf":1.4142135623730951}},"df":28,".":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"62":{"tf":1.4142135623730951}},"df":1}}}}}}}}},"v":{"docs":{},"df":0,"6":{"docs":{"38":{"tf":1.0}},"df":1},"7":{"docs":{"38":{"tf":1.7320508075688772}},"df":1},"8":{"docs":{"38":{"tf":1.7320508075688772}},"df":1}}},"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"124":{"tf":1.0},"125":{"tf":1.0},"127":{"tf":1.0},"69":{"tf":1.0},"96":{"tf":1.0}},"df":5}}}},"r":{"docs":{"163":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"163":{"tf":1.0}},"df":1}}},"[":{"docs":{},"df":0,"1":{"docs":{},"df":0,"6":{"docs":{"163":{"tf":1.0}},"df":1}}},"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{"11":{"tf":1.0},"91":{"tf":1.0}},"df":2}},"y":{"docs":{"118":{"tf":1.0},"157":{"tf":1.0},"159":{"tf":1.0},"163":{"tf":2.0},"189":{"tf":1.0},"79":{"tf":1.0}},"df":6,"_":{"docs":{},"df":0,"s":{"docs":{"159":{"tf":1.7320508075688772}},"df":1}}}}}},"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{"127":{"tf":1.0},"190":{"tf":1.0}},"df":2}}}},"k":{"docs":{"5":{"tf":1.0}},"df":1},"m":{"docs":{"68":{"tf":1.0}},"df":1,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"b":{"docs":{},"df":0,"k":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{"133":{"tf":1.0}},"df":1}}}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"112":{"tf":1.0},"62":{"tf":1.0},"72":{"tf":1.0},"88":{"tf":1.0}},"df":4}}},"r":{"docs":{},"df":0,"t":{"docs":{"77":{"tf":1.0}},"df":1,"!":{"docs":{},"df":0,"(":{"docs":{},"df":0,"x":{"docs":{},"df":0,"s":{"docs":{},"df":0,".":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"p":{"docs":{"133":{"tf":1.0}},"df":1}}}}}}}},"_":{"docs":{},"df":0,"e":{"docs":{},"df":0,"q":{"docs":{},"df":0,"!":{"docs":{},"df":0,"(":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{"77":{"tf":1.0}},"df":1}}},"x":{"docs":{},"df":0,"s":{"docs":{},"df":0,".":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"p":{"docs":{"134":{"tf":1.0}},"df":1}}}}}}}}}}}}}},"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"n":{"docs":{"153":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{"120":{"tf":1.0},"123":{"tf":1.0},"125":{"tf":1.0}},"df":3}}},"u":{"docs":{},"df":0,"m":{"docs":{"124":{"tf":1.0},"127":{"tf":1.4142135623730951},"131":{"tf":1.0},"190":{"tf":1.0},"2":{"tf":1.0},"61":{"tf":1.0},"7":{"tf":1.0},"72":{"tf":1.0},"80":{"tf":1.0}},"df":9,"p":{"docs":{},"df":0,"t":{"docs":{"16":{"tf":1.0},"3":{"tf":1.0}},"df":2}}}}},"y":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{"128":{"tf":1.4142135623730951},"129":{"tf":1.0}},"df":2,"h":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"128":{"tf":1.0},"129":{"tf":1.0},"200":{"tf":1.0},"201":{"tf":1.0},"80":{"tf":1.0}},"df":5}}}}}}}},"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"122":{"tf":1.0},"124":{"tf":2.8284271247461903},"125":{"tf":1.0},"131":{"tf":1.4142135623730951},"16":{"tf":1.0},"165":{"tf":1.0},"80":{"tf":1.0}},"df":7,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{"124":{"tf":1.4142135623730951}},"df":1,"i":{"docs":{},"df":0,"z":{"docs":{},"df":0,"e":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{},"df":0,"(":{"docs":{},"df":0,"0":{"docs":{"124":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{"124":{"tf":1.0},"126":{"tf":1.0},"192":{"tf":1.0},"78":{"tf":1.0},"80":{"tf":1.0},"84":{"tf":1.0}},"df":6}}}},"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"133":{"tf":1.4142135623730951},"158":{"tf":1.0},"16":{"tf":1.0},"160":{"tf":1.0},"166":{"tf":1.0},"173":{"tf":1.0},"60":{"tf":1.0},"61":{"tf":1.0},"80":{"tf":1.7320508075688772},"82":{"tf":1.4142135623730951},"84":{"tf":2.23606797749979}},"df":11}}}},"s":{"docs":{},"df":0,"{":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{},"df":0,"p":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"u":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"}":{"docs":{},"df":0,"=":{"docs":{},"df":0,"=":{"docs":{},"df":0,"\\"":{"docs":{},"df":0,"3":{"docs":{},"df":0,"7":{"docs":{},"df":0,"4":{"docs":{},"df":0,"8":{"docs":{"44":{"tf":1.0}},"df":1},"b":{"docs":{"44":{"tf":1.0}},"df":1}}}}}}}}}}}}}}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"}":{"docs":{},"df":0,"=":{"docs":{},"df":0,"=":{"docs":{},"df":0,"\\"":{"docs":{},"df":0,"0":{"docs":{},"df":0,"4":{"docs":{},"df":0,"8":{"docs":{},"df":0,"3":{"docs":{"44":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}}}}}}}}},"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"58":{"tf":1.7320508075688772},"93":{"tf":1.4142135623730951}},"df":2}}},"o":{"docs":{"53":{"tf":1.0},"68":{"tf":1.0},"72":{"tf":1.4142135623730951},"84":{"tf":1.0}},"df":4,"m":{"docs":{"184":{"tf":1.0}},"df":1,"a":{"docs":{},"df":0,"t":{"docs":{"124":{"tf":1.0},"125":{"tf":1.0},"158":{"tf":1.0},"160":{"tf":1.0},"161":{"tf":1.0},"162":{"tf":1.0},"165":{"tf":1.4142135623730951},"174":{"tf":1.0},"61":{"tf":1.0},"68":{"tf":1.0},"76":{"tf":1.0},"88":{"tf":1.0},"92":{"tf":1.0}},"df":13}}}}}},"v":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"115":{"tf":1.0},"119":{"tf":1.0},"124":{"tf":1.0},"132":{"tf":1.0},"137":{"tf":1.0},"14":{"tf":1.0},"15":{"tf":1.0},"159":{"tf":1.0},"165":{"tf":1.0},"168":{"tf":1.0},"17":{"tf":1.7320508075688772},"191":{"tf":1.7320508075688772},"53":{"tf":1.0},"68":{"tf":1.0},"73":{"tf":1.0},"74":{"tf":1.0},"84":{"tf":1.4142135623730951},"9":{"tf":1.0}},"df":18}}},"o":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{"123":{"tf":1.0},"125":{"tf":1.0},"126":{"tf":1.0},"150":{"tf":1.0},"157":{"tf":1.0},"158":{"tf":1.0},"55":{"tf":1.0},"86":{"tf":1.0}},"df":8}}}},"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"129":{"tf":1.0}},"df":1}},"r":{"docs":{"160":{"tf":1.0}},"df":1},"y":{"docs":{"112":{"tf":1.0},"121":{"tf":1.0}},"df":2}}}},"b":{"docs":{"104":{"tf":1.7320508075688772},"12":{"tf":1.0},"154":{"tf":1.0},"21":{"tf":1.0},"62":{"tf":2.0},"68":{"tf":1.0},"83":{"tf":1.0}},"df":7,"6":{"docs":{},"df":0,"c":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{},"df":0,"d":{"docs":{},"df":0,"a":{"docs":{},"df":0,"9":{"docs":{},"df":0,"b":{"docs":{"38":{"tf":1.0}},"df":1}}}}}}}},"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"122":{"tf":1.4142135623730951},"146":{"tf":1.0},"7":{"tf":1.0},"75":{"tf":1.0}},"df":4,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"21":{"tf":1.0}},"df":1}}},"g":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"3":{"tf":1.4142135623730951}},"df":1}}}}}}}},"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"w":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{"38":{"tf":1.0}},"df":1}}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{"0":{"tf":1.0},"102":{"tf":1.0},"14":{"tf":1.4142135623730951},"187":{"tf":1.0},"20":{"tf":1.0},"5":{"tf":1.4142135623730951}},"df":6,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"56":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"124":{"tf":1.0}},"df":1}}}}},"s":{"docs":{},"df":0,"e":{"docs":{"130":{"tf":1.0},"147":{"tf":1.0},"158":{"tf":1.0},"191":{"tf":1.4142135623730951},"33":{"tf":1.0},"69":{"tf":1.4142135623730951},"7":{"tf":1.4142135623730951},"71":{"tf":1.0}},"df":8},"i":{"docs":{"136":{"tf":1.0}},"df":1,"c":{"docs":{"105":{"tf":1.0},"54":{"tf":1.0},"7":{"tf":1.0},"72":{"tf":1.0},"94":{"tf":1.0}},"df":5}}},"t":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"129":{"tf":1.0}},"df":1}}},"l":{"docs":{"133":{"tf":1.0}},"df":1}}},"u":{"docs":{},"df":0,"d":{"docs":{"69":{"tf":1.0},"76":{"tf":1.7320508075688772}},"df":2}}},"e":{"docs":{"123":{"tf":1.0},"138":{"tf":1.0},"147":{"tf":1.0},"187":{"tf":1.0},"188":{"tf":1.0},"82":{"tf":1.7320508075688772},"87":{"tf":1.0}},"df":7,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"102":{"tf":1.0},"131":{"tf":1.0},"184":{"tf":1.0},"80":{"tf":1.0}},"df":4}}},"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"104":{"tf":1.0},"108":{"tf":1.0},"110":{"tf":1.0},"122":{"tf":1.0},"127":{"tf":1.0},"14":{"tf":1.0},"15":{"tf":1.4142135623730951},"17":{"tf":1.0},"172":{"tf":1.0},"176":{"tf":1.0},"178":{"tf":1.0},"49":{"tf":1.0},"64":{"tf":1.4142135623730951},"66":{"tf":1.0},"67":{"tf":1.4142135623730951},"68":{"tf":2.0},"76":{"tf":1.0},"78":{"tf":1.0},"83":{"tf":1.0}},"df":19}}},"g":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"66":{"tf":1.0}},"df":1}}},"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"v":{"docs":{"164":{"tf":1.0},"28":{"tf":1.0}},"df":2,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"107":{"tf":1.0},"111":{"tf":1.0},"60":{"tf":1.0},"77":{"tf":1.0},"78":{"tf":3.0},"79":{"tf":1.4142135623730951},"80":{"tf":1.7320508075688772},"83":{"tf":1.0}},"df":8},"u":{"docs":{},"df":0,"r":{"docs":{"122":{"tf":1.4142135623730951},"126":{"tf":1.0},"166":{"tf":1.0}},"df":3}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"164":{"tf":1.0}},"df":1}}}},"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"e":{"docs":{},"df":0,"v":{"docs":{"3":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"w":{"docs":{"125":{"tf":1.0},"188":{"tf":1.0},"19":{"tf":1.4142135623730951},"190":{"tf":1.0},"38":{"tf":1.0},"44":{"tf":1.0},"53":{"tf":1.0},"61":{"tf":1.4142135623730951},"77":{"tf":1.0}},"df":9}}},"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"58":{"tf":1.0}},"df":1}},"e":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"115":{"tf":1.0}},"df":1}}}}},"s":{"docs":{},"df":0,"t":{"docs":{"124":{"tf":1.0},"179":{"tf":1.0},"2":{"tf":1.4142135623730951},"38":{"tf":1.0}},"df":4}},"t":{"docs":{},"df":0,"a":{"docs":{"19":{"tf":1.0}},"df":1},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"109":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":1.0},"158":{"tf":1.0},"165":{"tf":1.0},"188":{"tf":1.0},"192":{"tf":1.0},"5":{"tf":1.0}},"df":8}}},"w":{"docs":{},"df":0,"e":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"115":{"tf":1.0},"117":{"tf":1.0},"119":{"tf":1.0},"120":{"tf":1.0},"121":{"tf":1.0},"125":{"tf":1.4142135623730951},"126":{"tf":1.0},"130":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0},"137":{"tf":1.0},"165":{"tf":1.0},"166":{"tf":1.4142135623730951},"168":{"tf":1.4142135623730951},"181":{"tf":1.0},"24":{"tf":1.0},"27":{"tf":1.0},"32":{"tf":1.0},"7":{"tf":1.4142135623730951},"70":{"tf":1.0},"87":{"tf":1.0}},"df":21}}}}},"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"188":{"tf":1.0}},"df":1}}},"y":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"131":{"tf":1.0},"35":{"tf":1.0},"79":{"tf":1.0}},"df":3}}}}},"i":{"docs":{},"df":0,"g":{"docs":{"119":{"tf":1.0},"189":{"tf":1.0},"62":{"tf":1.0}},"df":3,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"187":{"tf":1.0}},"df":1}}}}},"n":{"docs":{"188":{"tf":1.4142135623730951},"58":{"tf":1.0},"62":{"tf":2.23606797749979},"63":{"tf":1.0},"83":{"tf":1.0}},"df":5,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"161":{"tf":1.0},"173":{"tf":1.0},"178":{"tf":1.0},"186":{"tf":1.0},"187":{"tf":2.0},"189":{"tf":1.0},"190":{"tf":1.7320508075688772},"21":{"tf":1.0},"30":{"tf":1.0},"50":{"tf":1.7320508075688772},"62":{"tf":2.8284271247461903},"63":{"tf":2.0},"67":{"tf":1.0},"78":{"tf":1.0}},"df":14}}},"d":{"docs":{"119":{"tf":1.0},"174":{"tf":1.0},"191":{"tf":1.0}},"df":3,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"174":{"tf":2.23606797749979},"176":{"tf":1.0}},"df":2}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"s":{"docs":{},"df":0,".":{"docs":{},"df":0,"h":{"docs":{"174":{"tf":1.4142135623730951}},"df":1},"r":{"docs":{"174":{"tf":1.0}},"df":1}}}}}}},"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"19":{"tf":1.0},"21":{"tf":1.4142135623730951},"39":{"tf":1.0},"62":{"tf":1.0},"67":{"tf":1.0}},"df":5}}}}},"t":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0},"114":{"tf":1.0},"12":{"tf":1.4142135623730951},"168":{"tf":1.0},"52":{"tf":1.4142135623730951},"53":{"tf":1.0},"60":{"tf":1.4142135623730951},"68":{"tf":1.0},"72":{"tf":1.7320508075688772},"75":{"tf":1.0},"83":{"tf":1.4142135623730951},"87":{"tf":1.0},"88":{"tf":1.7320508075688772},"90":{"tf":2.0},"91":{"tf":1.0}},"df":16,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"109":{"tf":1.0}},"df":1}}}}}}}}}}}}}}},"l":{"docs":{"62":{"tf":1.4142135623730951}},"df":1,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"k":{"docs":{"69":{"tf":1.0},"70":{"tf":1.0}},"df":2}}},"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"125":{"tf":2.0},"126":{"tf":1.7320508075688772},"157":{"tf":1.0},"158":{"tf":1.7320508075688772},"165":{"tf":1.0},"53":{"tf":1.0},"64":{"tf":1.0},"68":{"tf":1.0},"77":{"tf":1.0},"80":{"tf":1.4142135623730951},"81":{"tf":1.0}},"df":11}}},"u":{"docs":{},"df":0,"e":{"docs":{"77":{"tf":1.7320508075688772}},"df":1,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{"70":{"tf":1.0}},"df":1}}}}}}}},"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{"11":{"tf":2.23606797749979},"193":{"tf":1.4142135623730951},"34":{"tf":1.0},"38":{"tf":1.0},"44":{"tf":1.4142135623730951},"53":{"tf":2.0},"63":{"tf":1.0},"66":{"tf":1.0},"68":{"tf":2.0},"69":{"tf":1.7320508075688772},"7":{"tf":1.7320508075688772},"70":{"tf":2.8284271247461903},"81":{"tf":1.0},"86":{"tf":1.0}},"df":14}}},"d":{"docs":{},"df":0,"i":{"docs":{"173":{"tf":1.0}},"df":1}},"i":{"docs":{},"df":0,"l":{"docs":{"112":{"tf":1.0}},"df":1}},"o":{"docs":{},"df":0,"k":{"docs":{"0":{"tf":1.4142135623730951},"125":{"tf":1.0},"129":{"tf":1.0},"131":{"tf":1.0},"163":{"tf":1.0},"167":{"tf":1.0},"19":{"tf":1.0},"2":{"tf":1.7320508075688772},"22":{"tf":1.0},"3":{"tf":1.7320508075688772},"38":{"tf":1.0},"4":{"tf":1.0},"5":{"tf":2.23606797749979},"6":{"tf":1.0},"60":{"tf":1.0},"7":{"tf":1.7320508075688772},"70":{"tf":1.7320508075688772},"77":{"tf":1.0},"8":{"tf":2.0},"84":{"tf":1.0},"9":{"tf":2.23606797749979}},"df":21,"s":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"5":{"tf":1.0}},"df":1}}}}}},"l":{"docs":{"100":{"tf":1.4142135623730951},"102":{"tf":1.0},"107":{"tf":2.0},"108":{"tf":1.7320508075688772},"109":{"tf":1.4142135623730951},"165":{"tf":1.4142135623730951}},"df":6},"t":{"docs":{"64":{"tf":1.0},"67":{"tf":1.0}},"df":2,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"16":{"tf":1.0},"17":{"tf":1.0}},"df":2}}}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"16":{"tf":1.0}},"df":1}}}}}}},"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"101":{"tf":1.0},"127":{"tf":2.0},"190":{"tf":1.0},"76":{"tf":1.4142135623730951},"96":{"tf":1.4142135623730951},"98":{"tf":1.0},"99":{"tf":1.0}},"df":7,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"127":{"tf":1.0}},"df":1}}}}}}}},"t":{"docs":{},"df":0,"h":{"docs":{"115":{"tf":1.0},"122":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":1.4142135623730951},"127":{"tf":1.0},"138":{"tf":1.0},"155":{"tf":1.0},"168":{"tf":1.0},"175":{"tf":1.0},"186":{"tf":1.0},"189":{"tf":1.0},"2":{"tf":1.0},"21":{"tf":1.0},"34":{"tf":1.0},"68":{"tf":1.0},"84":{"tf":1.0},"9":{"tf":1.0}},"df":17},"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"126":{"tf":1.0},"70":{"tf":1.0}},"df":2}}}},"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"118":{"tf":1.0},"155":{"tf":1.0},"163":{"tf":1.4142135623730951},"78":{"tf":1.0},"79":{"tf":1.4142135623730951}},"df":5,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"133":{"tf":1.0},"151":{"tf":1.0}},"df":2}}}}}},"x":{"docs":{},"df":0,"<":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"<":{"docs":{},"df":0,"_":{"docs":{"134":{"tf":1.0}},"df":1}}}}}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"190":{"tf":1.0}},"df":1}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{"127":{"tf":1.0},"63":{"tf":1.0},"64":{"tf":1.7320508075688772},"68":{"tf":2.0}},"df":4,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"187":{"tf":1.0},"28":{"tf":1.0},"30":{"tf":1.0},"31":{"tf":1.0},"32":{"tf":1.0},"35":{"tf":1.0},"53":{"tf":1.7320508075688772},"64":{"tf":2.0},"68":{"tf":2.449489742783178},"78":{"tf":1.0},"82":{"tf":1.0}},"df":11,"s":{"docs":{},"df":0,"/":{"docs":{},"df":0,"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"28":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}},"w":{"docs":{"46":{"tf":2.0}},"df":1}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{"125":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"193":{"tf":1.0},"195":{"tf":1.0},"197":{"tf":1.0}},"df":3}}}},"w":{"docs":{},"df":0,"s":{"docs":{"59":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"r":{"docs":{"20":{"tf":1.0}},"df":1}}}}}},"s":{"docs":{},"df":0,"s":{"docs":{"188":{"tf":1.4142135623730951},"61":{"tf":1.0},"62":{"tf":1.4142135623730951}},"df":3}},"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"17":{"tf":1.0}},"df":1}}}}}}},"u":{"docs":{"44":{"tf":1.4142135623730951},"69":{"tf":1.0},"86":{"tf":2.0}},"df":3,"f":{"docs":{"159":{"tf":1.0}},"df":1,"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"164":{"tf":1.0}},"df":1}}}},"g":{"docs":{"120":{"tf":1.0},"28":{"tf":1.0},"53":{"tf":1.0},"62":{"tf":1.0},"68":{"tf":1.0},"8":{"tf":1.0}},"df":6},"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"d":{"docs":{"138":{"tf":1.0},"161":{"tf":3.3166247903554},"162":{"tf":1.7320508075688772},"169":{"tf":1.0},"171":{"tf":1.0},"175":{"tf":1.0},"176":{"tf":1.0},"177":{"tf":1.7320508075688772},"178":{"tf":1.7320508075688772},"179":{"tf":1.4142135623730951},"183":{"tf":1.0},"184":{"tf":1.4142135623730951},"187":{"tf":1.4142135623730951},"189":{"tf":1.0},"39":{"tf":1.0},"54":{"tf":1.0},"61":{"tf":2.23606797749979},"63":{"tf":1.4142135623730951},"67":{"tf":2.449489742783178},"68":{"tf":1.0},"7":{"tf":1.0},"78":{"tf":1.4142135623730951}},"df":22,".":{"docs":{},"df":0,"r":{"docs":{"161":{"tf":1.0},"175":{"tf":1.0},"176":{"tf":1.4142135623730951},"177":{"tf":1.0},"178":{"tf":1.4142135623730951}},"df":5}},"e":{"docs":{},"df":0,"r":{"docs":{"104":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"y":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"p":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{},"df":0,"x":{"docs":{},"df":0,"(":{"docs":{},"df":0,"\\"":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{"174":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}},"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"174":{"tf":1.0}},"df":1}}}}}}}}}}},"t":{"docs":{"161":{"tf":1.0},"176":{"tf":1.4142135623730951},"26":{"tf":1.0},"30":{"tf":1.0},"78":{"tf":1.7320508075688772}},"df":5}}},"m":{"docs":{},"df":0,"p":{"docs":{"133":{"tf":1.7320508075688772}},"df":1,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{"133":{"tf":2.23606797749979}},"df":1}}}}}}}}}}}}}},"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"72":{"tf":1.0},"92":{"tf":1.0}},"df":2}}},"r":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"26":{"tf":1.0}},"df":1}}}},"s":{"docs":{},"df":0,"e":{"docs":{"119":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"59":{"tf":1.0},"69":{"tf":1.0}},"df":2}}}}},"x":{"docs":{"62":{"tf":1.4142135623730951}},"df":1},"y":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{"11":{"tf":1.0},"166":{"tf":2.0},"189":{"tf":1.0},"61":{"tf":1.0},"62":{"tf":2.23606797749979},"67":{"tf":1.0}},"df":6,"s":{"docs":{},"df":0,"/":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"68":{"tf":1.4142135623730951}},"df":1}}}}}}}}}},"c":{"docs":{"142":{"tf":2.8284271247461903},"154":{"tf":1.0},"156":{"tf":1.4142135623730951},"157":{"tf":1.0},"158":{"tf":1.0},"160":{"tf":1.7320508075688772},"163":{"tf":1.7320508075688772},"164":{"tf":1.0},"165":{"tf":1.4142135623730951},"166":{"tf":2.0},"167":{"tf":1.7320508075688772},"168":{"tf":2.23606797749979},"171":{"tf":3.0},"172":{"tf":2.0},"173":{"tf":4.123105625617661},"174":{"tf":1.4142135623730951},"175":{"tf":1.7320508075688772},"178":{"tf":1.0},"179":{"tf":1.7320508075688772},"181":{"tf":2.0},"183":{"tf":1.4142135623730951},"184":{"tf":2.23606797749979},"187":{"tf":1.0},"190":{"tf":1.4142135623730951},"2":{"tf":1.0},"39":{"tf":1.0},"4":{"tf":1.4142135623730951},"44":{"tf":1.0},"64":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.0},"75":{"tf":1.0},"88":{"tf":1.0},"91":{"tf":1.7320508075688772},"92":{"tf":1.0}},"df":35,"/":{"docs":{},"df":0,"c":{"docs":{"172":{"tf":1.0},"174":{"tf":1.0},"175":{"tf":1.4142135623730951},"176":{"tf":1.0}},"df":4}},":":{"docs":{},"df":0,"\\\\":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"s":{"docs":{},"df":0,"\\\\":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"\\\\":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"p":{"docs":{},"df":0,"d":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"\\\\":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"\\\\":{"docs":{},"df":0,"x":{"docs":{},"df":0,"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{},"df":0,"s":{"docs":{},"df":0,"\\\\":{"docs":{},"df":0,"@":{"docs":{},"df":0,"x":{"docs":{},"df":0,"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"50":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"_":{"docs":{},"df":0,"n":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{"168":{"tf":1.4142135623730951}},"df":1}}},"u":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"168":{"tf":1.4142135623730951}},"df":1}}}},"v":{"docs":{},"df":0,"o":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{"168":{"tf":1.0}},"df":1}}}}},"a":{"docs":{"124":{"tf":1.4142135623730951}},"df":1,"b":{"docs":{},"df":0,"l":{"docs":{"53":{"tf":1.0}},"df":1}},"c":{"docs":{},"df":0,"h":{"docs":{"124":{"tf":1.0}},"df":1}},"l":{"docs":{},"df":0,"c":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{"191":{"tf":1.0},"76":{"tf":1.0}},"df":2}}},"i":{"docs":{},"df":0,"b":{"docs":{"91":{"tf":1.0},"92":{"tf":1.0},"93":{"tf":1.0}},"df":3,"r":{"docs":{"90":{"tf":1.0}},"df":1}}},"l":{"docs":{"100":{"tf":1.0},"101":{"tf":1.0},"105":{"tf":1.4142135623730951},"112":{"tf":1.4142135623730951},"114":{"tf":1.4142135623730951},"122":{"tf":1.0},"125":{"tf":1.0},"127":{"tf":1.4142135623730951},"130":{"tf":1.0},"131":{"tf":1.0},"137":{"tf":1.0},"146":{"tf":1.0},"157":{"tf":1.0},"160":{"tf":1.0},"164":{"tf":1.0},"168":{"tf":1.0},"170":{"tf":1.0},"173":{"tf":1.0},"174":{"tf":1.0},"175":{"tf":1.0},"176":{"tf":1.0},"177":{"tf":1.0},"184":{"tf":1.7320508075688772},"187":{"tf":1.0},"190":{"tf":1.0},"57":{"tf":1.0},"61":{"tf":1.4142135623730951},"64":{"tf":1.4142135623730951},"67":{"tf":1.4142135623730951},"68":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0},"74":{"tf":1.0},"75":{"tf":1.0},"76":{"tf":1.4142135623730951},"80":{"tf":2.0},"81":{"tf":1.0},"84":{"tf":1.4142135623730951},"88":{"tf":1.0},"93":{"tf":1.0},"99":{"tf":1.7320508075688772}},"df":41}},"n":{"docs":{},"df":0,"\'":{"docs":{},"df":0,"t":{"docs":{"109":{"tf":1.7320508075688772}},"df":1}},"’":{"docs":{},"df":0,"t":{"docs":{"123":{"tf":1.0},"127":{"tf":1.0},"189":{"tf":1.0},"71":{"tf":1.0}},"df":4}}},"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"114":{"tf":1.0},"115":{"tf":1.4142135623730951},"119":{"tf":1.0},"30":{"tf":1.0}},"df":4}},"c":{"docs":{"132":{"tf":1.0},"134":{"tf":2.449489742783178},"135":{"tf":1.0},"136":{"tf":1.0},"137":{"tf":2.23606797749979},"138":{"tf":1.7320508075688772},"139":{"tf":1.0}},"df":7}}},"r":{"docs":{},"df":0,"d":{"docs":{"86":{"tf":1.4142135623730951}},"df":1},"e":{"docs":{"11":{"tf":1.0},"122":{"tf":1.0},"125":{"tf":1.0},"127":{"tf":1.0},"138":{"tf":1.0},"15":{"tf":1.0},"160":{"tf":1.0},"161":{"tf":1.0},"164":{"tf":1.0},"165":{"tf":1.0},"173":{"tf":1.0}},"df":11,"f":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"123":{"tf":1.0},"125":{"tf":1.4142135623730951}},"df":2}}}}}},"g":{"docs":{},"df":0,"o":{"docs":{"158":{"tf":1.4142135623730951},"160":{"tf":1.0},"161":{"tf":2.0},"162":{"tf":1.7320508075688772},"169":{"tf":1.0},"178":{"tf":1.0},"179":{"tf":1.4142135623730951},"180":{"tf":1.4142135623730951},"184":{"tf":1.0},"187":{"tf":1.0},"188":{"tf":1.7320508075688772},"189":{"tf":1.0},"19":{"tf":1.4142135623730951},"20":{"tf":1.7320508075688772},"21":{"tf":1.4142135623730951},"39":{"tf":1.4142135623730951},"40":{"tf":1.7320508075688772},"57":{"tf":2.0},"61":{"tf":1.7320508075688772},"62":{"tf":2.8284271247461903},"63":{"tf":2.6457513110645907},"67":{"tf":2.8284271247461903},"68":{"tf":2.23606797749979},"77":{"tf":2.0},"78":{"tf":1.4142135623730951},"79":{"tf":1.0},"81":{"tf":1.0},"83":{"tf":1.0},"9":{"tf":1.0}},"df":29,".":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"l":{"docs":{"133":{"tf":1.0},"158":{"tf":1.0},"188":{"tf":1.0},"190":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.0},"77":{"tf":1.0},"81":{"tf":1.0}},"df":8}}}}},"/":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,".":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"l":{"docs":{"190":{"tf":1.4142135623730951},"61":{"tf":1.7320508075688772},"63":{"tf":1.4142135623730951},"67":{"tf":1.4142135623730951},"68":{"tf":1.4142135623730951},"77":{"tf":1.0}},"df":6}}}}}}}}}}}}}},"r":{"docs":{},"df":0,"i":{"docs":{"86":{"tf":1.4142135623730951}},"df":1}}},"s":{"docs":{},"df":0,"e":{"docs":{"103":{"tf":1.0},"113":{"tf":1.0},"12":{"tf":1.0},"124":{"tf":1.0},"131":{"tf":1.0},"134":{"tf":1.0},"136":{"tf":1.4142135623730951},"138":{"tf":1.0},"146":{"tf":1.0},"157":{"tf":1.0},"160":{"tf":1.4142135623730951},"161":{"tf":1.0},"162":{"tf":1.0},"164":{"tf":1.0},"169":{"tf":1.0},"170":{"tf":1.0},"178":{"tf":1.0},"188":{"tf":1.0},"2":{"tf":1.0},"22":{"tf":1.0},"60":{"tf":1.0},"61":{"tf":1.0},"63":{"tf":1.0},"79":{"tf":1.0},"87":{"tf":1.0},"99":{"tf":1.0}},"df":26}},"t":{"docs":{"67":{"tf":1.0},"68":{"tf":1.4142135623730951}},"df":2,"c":{"docs":{},"df":0,"h":{"docs":{"3":{"tf":1.4142135623730951},"63":{"tf":1.0},"82":{"tf":1.0}},"df":3}},"e":{"docs":{},"df":0,"r":{"docs":{"3":{"tf":1.0}},"df":1}}},"u":{"docs":{},"df":0,"s":{"docs":{"107":{"tf":1.0},"122":{"tf":1.7320508075688772},"124":{"tf":1.0},"136":{"tf":1.0},"78":{"tf":1.4142135623730951},"83":{"tf":1.0},"84":{"tf":1.0}},"df":7},"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"11":{"tf":1.0}},"df":1}}}}}},"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"184":{"tf":1.0}},"df":1}}}}}}},"c":{"docs":{"175":{"tf":1.0},"176":{"tf":1.0},"178":{"tf":1.4142135623730951},"9":{"tf":1.4142135623730951}},"df":4,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"d":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{"178":{"tf":1.0}},"df":1}}}}}}}}}}}}},"d":{"docs":{"57":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.0},"63":{"tf":1.0},"67":{"tf":1.0}},"df":5,"y":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"b":{"docs":{"180":{"tf":1.0}},"df":1}}}}},"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{"126":{"tf":1.7320508075688772},"127":{"tf":1.0}},"df":2}},"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"53":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"103":{"tf":1.0},"107":{"tf":1.0},"111":{"tf":1.0},"115":{"tf":1.4142135623730951},"118":{"tf":1.0},"166":{"tf":1.0},"87":{"tf":1.0},"88":{"tf":1.0}},"df":8}}}}}},"f":{"docs":{},"df":0,"g":{"docs":{"158":{"tf":1.0}},"df":1,"(":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"g":{"docs":{},"df":0,"_":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"78":{"tf":1.0}},"df":1}}}}}}}}}}}},"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"=":{"docs":{},"df":0,"\\"":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{},"df":0,"r":{"docs":{"158":{"tf":1.4142135623730951}},"df":1}}},"i":{"docs":{},"df":0,"i":{"docs":{},"df":0,"r":{"docs":{"158":{"tf":1.0}},"df":1}}},"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"159":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}},"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"t":{"docs":{},"df":0,"(":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"g":{"docs":{},"df":0,"_":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"78":{"tf":1.0}},"df":1}}}}}}}}}}}},"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"=":{"docs":{},"df":0,"\\"":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"159":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}},"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"163":{"tf":1.4142135623730951}},"df":1}},"n":{"docs":{},"df":0,"c":{"docs":{"64":{"tf":1.0}},"df":1},"g":{"docs":{"102":{"tf":1.4142135623730951},"103":{"tf":1.0},"122":{"tf":1.0},"137":{"tf":1.0},"155":{"tf":1.0},"161":{"tf":1.0},"165":{"tf":1.0},"166":{"tf":1.7320508075688772},"190":{"tf":1.4142135623730951},"67":{"tf":1.4142135623730951},"75":{"tf":1.0},"78":{"tf":1.0},"79":{"tf":1.0},"9":{"tf":1.4142135623730951},"95":{"tf":1.0}},"df":15}},"p":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"103":{"tf":1.0},"108":{"tf":1.0},"140":{"tf":1.0},"156":{"tf":1.0},"158":{"tf":1.0},"165":{"tf":1.0},"166":{"tf":1.0},"60":{"tf":1.0},"64":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":2.0},"87":{"tf":1.0}},"df":14}}}},"r":{"docs":{"168":{"tf":1.7320508075688772},"173":{"tf":1.4142135623730951}},"df":2,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"155":{"tf":1.0},"186":{"tf":1.0},"20":{"tf":1.0},"66":{"tf":1.0}},"df":4}}}}}}}}}},"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"103":{"tf":2.0},"108":{"tf":1.4142135623730951},"110":{"tf":1.0},"115":{"tf":1.0},"125":{"tf":1.0},"127":{"tf":1.0},"128":{"tf":1.0},"129":{"tf":1.0},"136":{"tf":1.0},"163":{"tf":1.4142135623730951},"166":{"tf":1.0},"3":{"tf":1.0},"44":{"tf":1.0},"53":{"tf":1.7320508075688772},"62":{"tf":1.0},"63":{"tf":1.0},"68":{"tf":1.7320508075688772},"72":{"tf":1.0},"77":{"tf":1.0},"78":{"tf":1.0},"83":{"tf":1.0},"93":{"tf":1.0}},"df":22,"e":{"docs":{},"df":0,"r":{"docs":{"101":{"tf":1.0},"96":{"tf":1.4142135623730951},"98":{"tf":1.0},"99":{"tf":1.0}},"df":4}},"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"141":{"tf":1.0}},"df":1}}}}}}},"i":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{"130":{"tf":1.0},"170":{"tf":1.0}},"df":2}}},"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{"6":{"tf":1.0}},"df":1}}},"p":{"docs":{"11":{"tf":1.7320508075688772},"144":{"tf":1.4142135623730951},"155":{"tf":1.4142135623730951},"50":{"tf":1.0},"53":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.0},"72":{"tf":1.0},"73":{"tf":1.4142135623730951},"74":{"tf":1.4142135623730951},"76":{"tf":1.0},"84":{"tf":1.0},"86":{"tf":2.0},"90":{"tf":1.0}},"df":15}},"o":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"119":{"tf":1.0},"124":{"tf":1.0},"34":{"tf":1.0}},"df":3}},"o":{"docs":{},"df":0,"s":{"docs":{"135":{"tf":1.0},"38":{"tf":1.0}},"df":2}},"s":{"docs":{},"df":0,"e":{"docs":{"79":{"tf":1.0}},"df":1,"n":{"docs":{"55":{"tf":1.0},"78":{"tf":1.0}},"df":2}}}},"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"k":{"docs":{"86":{"tf":1.0},"88":{"tf":1.0}},"df":2}}}},"i":{"docs":{},"df":0,"r":{"docs":{},"df":0,"c":{"docs":{},"df":0,"u":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"196":{"tf":1.0},"86":{"tf":1.0}},"df":2}},"m":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"164":{"tf":1.0}},"df":1}}}}}}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{"190":{"tf":1.0}},"df":1}},"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{"173":{"tf":1.0}},"df":1}}}},"s":{"docs":{},"df":0,"s":{"docs":{"12":{"tf":1.0},"5":{"tf":1.0},"62":{"tf":1.0},"97":{"tf":1.0}},"df":4,"i":{"docs":{},"df":0,"f":{"docs":{"12":{"tf":1.0}},"df":1}}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{"67":{"tf":1.0}},"df":1},"r":{"docs":{"127":{"tf":1.0},"147":{"tf":1.0},"78":{"tf":1.0},"8":{"tf":1.0},"84":{"tf":1.4142135623730951}},"df":5}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"92":{"tf":1.0}},"df":1}}}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"59":{"tf":1.4142135623730951}},"df":1}},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"64":{"tf":1.4142135623730951},"68":{"tf":1.0}},"df":2}}}},"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"11":{"tf":1.0},"53":{"tf":1.0},"68":{"tf":1.0},"76":{"tf":2.6457513110645907},"81":{"tf":1.0}},"df":5}},"n":{"docs":{},"df":0,"e":{"docs":{"58":{"tf":1.4142135623730951},"59":{"tf":1.0},"63":{"tf":1.0}},"df":3}},"s":{"docs":{},"df":0,"e":{"docs":{"13":{"tf":1.0},"139":{"tf":1.0},"64":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0}},"df":5,"r":{"docs":{"60":{"tf":1.0}},"df":1},"s":{"docs":{},"df":0,"t":{"docs":{"158":{"tf":1.0}},"df":1}}},"u":{"docs":{},"df":0,"r":{"docs":{"190":{"tf":1.0},"72":{"tf":1.0},"74":{"tf":1.0},"75":{"tf":1.4142135623730951}},"df":4}}}}},"m":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"s":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"127":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}},"a":{"docs":{},"df":0,"k":{"docs":{},"df":0,"e":{"docs":{"169":{"tf":1.0},"175":{"tf":1.0}},"df":2}}},"s":{"docs":{},"df":0,"i":{"docs":{"191":{"tf":1.0},"199":{"tf":1.0}},"df":2}}},"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{"100":{"tf":1.0},"101":{"tf":1.0},"102":{"tf":1.0},"108":{"tf":1.4142135623730951},"109":{"tf":1.0},"110":{"tf":1.0},"112":{"tf":1.7320508075688772},"12":{"tf":1.0},"120":{"tf":1.0},"122":{"tf":1.4142135623730951},"123":{"tf":2.0},"125":{"tf":2.449489742783178},"126":{"tf":1.4142135623730951},"127":{"tf":1.4142135623730951},"13":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.0},"151":{"tf":1.0},"157":{"tf":1.0},"158":{"tf":2.449489742783178},"16":{"tf":1.4142135623730951},"160":{"tf":1.4142135623730951},"163":{"tf":1.0},"165":{"tf":1.7320508075688772},"166":{"tf":1.0},"168":{"tf":1.4142135623730951},"17":{"tf":1.4142135623730951},"171":{"tf":1.4142135623730951},"172":{"tf":1.4142135623730951},"173":{"tf":1.4142135623730951},"174":{"tf":2.0},"175":{"tf":2.8284271247461903},"176":{"tf":1.0},"179":{"tf":1.0},"181":{"tf":1.0},"182":{"tf":1.0},"183":{"tf":1.0},"184":{"tf":1.0},"187":{"tf":1.4142135623730951},"188":{"tf":1.4142135623730951},"191":{"tf":1.4142135623730951},"24":{"tf":1.0},"26":{"tf":1.0},"28":{"tf":1.4142135623730951},"29":{"tf":1.0},"31":{"tf":1.0},"32":{"tf":1.0},"60":{"tf":1.4142135623730951},"63":{"tf":1.0},"64":{"tf":1.7320508075688772},"69":{"tf":1.4142135623730951},"72":{"tf":2.0},"78":{"tf":1.0},"80":{"tf":1.0},"9":{"tf":1.0},"92":{"tf":1.4142135623730951},"93":{"tf":2.0},"94":{"tf":1.0},"96":{"tf":1.0},"99":{"tf":1.0}},"df":60,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"188":{"tf":1.0}},"df":1}}}}},"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"132":{"tf":2.23606797749979},"133":{"tf":1.7320508075688772},"134":{"tf":3.4641016151377544},"135":{"tf":1.4142135623730951},"136":{"tf":2.449489742783178},"137":{"tf":3.1622776601683795},"138":{"tf":2.23606797749979},"139":{"tf":1.0},"140":{"tf":1.0},"156":{"tf":1.0},"169":{"tf":1.0},"17":{"tf":1.4142135623730951},"170":{"tf":1.0},"21":{"tf":1.0},"86":{"tf":1.0},"91":{"tf":1.0}},"df":16}}}}},"m":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"113":{"tf":1.0},"158":{"tf":1.0},"166":{"tf":1.4142135623730951},"169":{"tf":1.0},"171":{"tf":1.0},"175":{"tf":1.0},"33":{"tf":1.0}},"df":7}}},"e":{"docs":{"117":{"tf":1.0},"120":{"tf":1.0},"128":{"tf":1.0},"90":{"tf":1.0}},"df":4},"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"4":{"tf":1.4142135623730951},"5":{"tf":1.0}},"df":2}}}},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"15":{"tf":1.0},"177":{"tf":1.0},"21":{"tf":1.0},"42":{"tf":1.0},"43":{"tf":1.7320508075688772},"44":{"tf":1.4142135623730951},"53":{"tf":2.23606797749979},"61":{"tf":1.0},"62":{"tf":1.4142135623730951},"63":{"tf":2.23606797749979},"64":{"tf":2.449489742783178},"68":{"tf":3.0}},"df":12}}},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"63":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.4142135623730951}},"df":3}}},"i":{"docs":{},"df":0,"t":{"docs":{"161":{"tf":1.0},"53":{"tf":1.0}},"df":2}},"o":{"docs":{},"df":0,"n":{"docs":{"114":{"tf":1.0},"126":{"tf":1.4142135623730951},"129":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.0},"132":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":1.0},"15":{"tf":1.0},"157":{"tf":1.4142135623730951},"160":{"tf":1.0},"161":{"tf":1.0},"166":{"tf":1.0},"169":{"tf":1.0},"4":{"tf":1.0},"69":{"tf":1.7320508075688772},"71":{"tf":1.0},"78":{"tf":1.0},"9":{"tf":1.0},"90":{"tf":1.0}},"df":20,"l":{"docs":{},"df":0,"i":{"docs":{"165":{"tf":1.0},"175":{"tf":1.0},"66":{"tf":1.0},"78":{"tf":1.0}},"df":4}}}},"u":{"docs":{},"df":0,"n":{"docs":{"115":{"tf":1.0},"196":{"tf":1.0},"27":{"tf":1.0},"32":{"tf":1.0},"35":{"tf":1.0},"69":{"tf":1.0},"86":{"tf":1.0}},"df":7}}},"p":{"docs":{},"df":0,"a":{"docs":{"72":{"tf":1.0}},"df":1,"r":{"docs":{"103":{"tf":1.0},"124":{"tf":1.0},"132":{"tf":1.0},"53":{"tf":1.0},"68":{"tf":1.0}},"df":5},"s":{"docs":{},"df":0,"s":{"docs":{"11":{"tf":1.0},"70":{"tf":1.0}},"df":2}},"t":{"docs":{"166":{"tf":1.0},"173":{"tf":1.4142135623730951},"174":{"tf":1.0},"20":{"tf":1.0},"30":{"tf":1.0},"35":{"tf":1.0}},"df":6}},"i":{"docs":{},"df":0,"l":{"docs":{"102":{"tf":1.4142135623730951},"103":{"tf":1.7320508075688772},"110":{"tf":1.4142135623730951},"111":{"tf":1.0},"112":{"tf":1.0},"114":{"tf":1.0},"122":{"tf":1.0},"124":{"tf":1.4142135623730951},"125":{"tf":1.7320508075688772},"132":{"tf":1.0},"151":{"tf":2.23606797749979},"154":{"tf":1.0},"157":{"tf":1.4142135623730951},"158":{"tf":2.449489742783178},"159":{"tf":1.0},"160":{"tf":1.0},"162":{"tf":1.0},"163":{"tf":1.0},"165":{"tf":1.0},"166":{"tf":1.4142135623730951},"168":{"tf":1.0},"171":{"tf":1.0},"173":{"tf":1.0},"174":{"tf":1.7320508075688772},"175":{"tf":2.449489742783178},"176":{"tf":1.4142135623730951},"177":{"tf":1.4142135623730951},"178":{"tf":1.7320508075688772},"179":{"tf":1.0},"181":{"tf":1.0},"182":{"tf":1.4142135623730951},"183":{"tf":1.0},"19":{"tf":1.0},"38":{"tf":2.0},"4":{"tf":1.0},"50":{"tf":1.0},"60":{"tf":1.0},"61":{"tf":2.23606797749979},"63":{"tf":2.0},"64":{"tf":1.0},"67":{"tf":1.7320508075688772},"72":{"tf":1.0},"78":{"tf":1.7320508075688772},"80":{"tf":1.0},"84":{"tf":1.4142135623730951},"91":{"tf":1.7320508075688772},"92":{"tf":1.7320508075688772},"93":{"tf":1.0}},"df":48,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"\\"":{"docs":{},"df":0,"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{"178":{"tf":1.0}},"df":1}}}}},"/":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"k":{"docs":{"175":{"tf":1.0}},"df":1}}}}}}}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"62":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"62":{"tf":1.0}},"df":1}}}},"t":{"docs":{"112":{"tf":1.0},"133":{"tf":1.0},"160":{"tf":1.4142135623730951},"87":{"tf":1.0}},"df":4},"x":{"docs":{"113":{"tf":1.0},"115":{"tf":1.4142135623730951},"126":{"tf":1.0},"131":{"tf":1.0},"160":{"tf":1.0},"168":{"tf":1.0},"177":{"tf":1.0},"178":{"tf":1.0},"191":{"tf":1.0},"27":{"tf":1.0},"30":{"tf":1.0}},"df":11}},"i":{"docs":{},"df":0,"c":{"docs":{"124":{"tf":1.0},"151":{"tf":1.0}},"df":2}}},"o":{"docs":{},"df":0,"n":{"docs":{"113":{"tf":1.0},"115":{"tf":1.0},"118":{"tf":1.0},"119":{"tf":1.0},"129":{"tf":1.0},"158":{"tf":1.0},"192":{"tf":1.0},"21":{"tf":1.0},"39":{"tf":1.0},"86":{"tf":1.0},"87":{"tf":1.0}},"df":11}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{"5":{"tf":1.0}},"df":1}}}}}},"u":{"docs":{},"df":0,"t":{"docs":{"151":{"tf":1.0},"157":{"tf":1.0},"32":{"tf":1.0},"86":{"tf":1.0}},"df":4}}}},"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{"1":{"tf":1.0},"103":{"tf":1.0},"104":{"tf":1.0},"173":{"tf":1.0},"4":{"tf":1.0},"7":{"tf":1.0}},"df":6}},"r":{"docs":{},"df":0,"n":{"docs":{"38":{"tf":1.0},"80":{"tf":1.0}},"df":2}}},"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{"120":{"tf":1.4142135623730951},"121":{"tf":1.7320508075688772},"125":{"tf":1.0},"126":{"tf":1.0},"127":{"tf":1.0},"128":{"tf":1.0},"129":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.0},"165":{"tf":1.4142135623730951},"80":{"tf":1.7320508075688772}},"df":11}}}},"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"122":{"tf":1.4142135623730951},"133":{"tf":1.0},"136":{"tf":1.7320508075688772},"158":{"tf":2.0},"189":{"tf":1.0},"28":{"tf":1.0},"31":{"tf":1.0}},"df":7}}},"f":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{"125":{"tf":1.0}},"df":1},"g":{"docs":{"40":{"tf":1.0},"63":{"tf":2.0},"64":{"tf":1.0},"68":{"tf":1.0},"77":{"tf":1.0}},"df":5,"u":{"docs":{},"df":0,"r":{"docs":{"103":{"tf":2.0},"104":{"tf":1.4142135623730951},"106":{"tf":2.6457513110645907},"107":{"tf":1.7320508075688772},"108":{"tf":1.4142135623730951},"109":{"tf":1.0},"117":{"tf":1.0},"119":{"tf":1.0},"127":{"tf":2.0},"155":{"tf":1.0},"161":{"tf":1.0},"17":{"tf":1.0},"193":{"tf":1.0},"26":{"tf":1.0},"27":{"tf":1.0},"31":{"tf":1.0},"53":{"tf":1.4142135623730951},"61":{"tf":1.0},"66":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.0},"76":{"tf":1.7320508075688772},"81":{"tf":1.4142135623730951},"88":{"tf":2.0}},"df":24,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"g":{"docs":{},"df":0,"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"(":{"docs":{},"df":0,"g":{"docs":{},"df":0,"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{"127":{"tf":1.0}},"df":1}}}}}}}}}},"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"127":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}},"r":{"docs":{},"df":0,"m":{"docs":{"62":{"tf":1.0},"79":{"tf":1.0}},"df":2}}},"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"119":{"tf":1.0}},"df":1}}}}},"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"11":{"tf":1.0},"118":{"tf":1.0},"119":{"tf":1.0},"29":{"tf":1.0},"30":{"tf":1.0},"31":{"tf":1.0},"53":{"tf":1.0},"64":{"tf":1.7320508075688772},"68":{"tf":2.23606797749979}},"df":9,"o":{"docs":{},"df":0,"r":{"docs":{"53":{"tf":1.0}},"df":1}}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"q":{"docs":{},"df":0,"u":{"docs":{"124":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"d":{"docs":{"115":{"tf":1.0},"122":{"tf":1.0},"125":{"tf":1.0},"126":{"tf":1.0},"160":{"tf":1.0},"164":{"tf":1.0},"165":{"tf":1.0}},"df":7,"e":{"docs":{},"df":0,"r":{"docs":{"75":{"tf":1.0}},"df":1}}},"s":{"docs":{},"df":0,"t":{"docs":{"117":{"tf":1.0},"121":{"tf":1.0},"171":{"tf":1.0},"174":{"tf":1.0},"179":{"tf":1.0}},"df":5}}},"o":{"docs":{},"df":0,"l":{"docs":{"53":{"tf":1.0},"64":{"tf":1.0},"68":{"tf":1.0},"77":{"tf":1.0},"79":{"tf":1.0},"81":{"tf":1.4142135623730951},"83":{"tf":1.0}},"df":7}},"t":{"docs":{"100":{"tf":1.0},"101":{"tf":1.0},"125":{"tf":1.0},"159":{"tf":2.0},"164":{"tf":1.0},"165":{"tf":1.4142135623730951},"168":{"tf":1.0},"83":{"tf":1.0}},"df":8,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"112":{"tf":1.0},"138":{"tf":1.0},"151":{"tf":1.0},"158":{"tf":1.0},"159":{"tf":1.0},"62":{"tf":1.0}},"df":6}}},"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"b":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{"191":{"tf":1.0}},"df":1}}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"76":{"tf":1.0}},"df":1,"t":{"docs":{"110":{"tf":1.0},"114":{"tf":1.0},"126":{"tf":1.0}},"df":3}}}},"u":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"115":{"tf":1.0},"126":{"tf":1.0},"146":{"tf":1.0}},"df":3,"o":{"docs":{},"df":0,"r":{"docs":{"142":{"tf":1.0},"69":{"tf":1.0}},"df":2}}}}}}},"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{"11":{"tf":1.0},"160":{"tf":1.0}},"df":2}},"m":{"docs":{"105":{"tf":1.0},"110":{"tf":1.0},"146":{"tf":1.0},"172":{"tf":1.0},"76":{"tf":1.0}},"df":5}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"100":{"tf":1.4142135623730951},"11":{"tf":1.0},"111":{"tf":1.4142135623730951},"112":{"tf":1.0},"125":{"tf":1.4142135623730951},"127":{"tf":1.4142135623730951},"133":{"tf":1.0},"144":{"tf":1.0},"166":{"tf":1.0},"37":{"tf":1.0},"53":{"tf":1.0},"56":{"tf":1.4142135623730951},"62":{"tf":2.449489742783178},"63":{"tf":1.0},"66":{"tf":1.0},"7":{"tf":1.0},"78":{"tf":1.0},"86":{"tf":1.0},"9":{"tf":1.4142135623730951},"99":{"tf":1.4142135623730951}},"df":20}}},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"126":{"tf":1.0},"44":{"tf":1.0},"53":{"tf":1.0},"61":{"tf":1.0},"74":{"tf":1.0},"75":{"tf":1.4142135623730951},"8":{"tf":1.0}},"df":7}},"x":{"docs":{},"df":0,"t":{"docs":{"120":{"tf":1.0},"123":{"tf":1.0},"125":{"tf":1.0},"126":{"tf":1.0},"165":{"tf":1.0},"5":{"tf":1.0},"60":{"tf":1.0},"80":{"tf":1.0}},"df":8}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"u":{"docs":{"122":{"tf":1.0},"28":{"tf":1.0},"64":{"tf":2.0},"68":{"tf":2.0}},"df":4}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"107":{"tf":1.0},"108":{"tf":1.4142135623730951},"115":{"tf":1.4142135623730951}},"df":3}},"s":{"docs":{},"df":0,"t":{"docs":{"134":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"l":{"docs":{"103":{"tf":1.0},"107":{"tf":1.0},"130":{"tf":1.0},"158":{"tf":1.0},"160":{"tf":1.0},"166":{"tf":1.0},"24":{"tf":1.0},"28":{"tf":1.0},"44":{"tf":1.0},"53":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":2.23606797749979},"71":{"tf":1.0},"72":{"tf":1.0},"84":{"tf":1.7320508075688772},"86":{"tf":1.7320508075688772},"87":{"tf":1.0},"90":{"tf":1.0}},"df":18,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"’":{"docs":{"69":{"tf":1.0}},"df":1}}}}}}}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"i":{"docs":{"107":{"tf":1.0},"109":{"tf":1.0},"34":{"tf":1.0},"60":{"tf":1.0},"77":{"tf":1.4142135623730951}},"df":5},"t":{"docs":{"142":{"tf":1.0},"30":{"tf":1.0},"84":{"tf":1.0}},"df":3}},"r":{"docs":{},"df":0,"s":{"docs":{"115":{"tf":1.0},"174":{"tf":1.0}},"df":2},"t":{"docs":{"110":{"tf":1.0},"127":{"tf":1.0},"165":{"tf":1.0},"168":{"tf":1.0},"175":{"tf":1.0}},"df":5}}}}},"o":{"docs":{},"df":0,"k":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"k":{"docs":{"2":{"tf":1.0}},"df":1}}}}},"l":{"docs":{"175":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"h":{"docs":{"173":{"tf":1.0}},"df":1}},"_":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"s":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{"173":{"tf":1.0}},"df":1}}}}}}}}}},"f":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"173":{"tf":1.7320508075688772}},"df":1,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"(":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"173":{"tf":1.0}},"df":1}}}}}}}}}}}}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"173":{"tf":3.0}},"df":1}}}}}}},"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"130":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"8":{"tf":1.0},"80":{"tf":1.0}},"df":2}}}}},"p":{"docs":{},"df":0,"i":{"docs":{"112":{"tf":1.0},"126":{"tf":1.7320508075688772},"127":{"tf":1.0},"146":{"tf":1.4142135623730951},"177":{"tf":1.0}},"df":5}},"r":{"docs":{},"df":0,"e":{"docs":{"100":{"tf":1.0},"11":{"tf":1.0},"12":{"tf":1.0},"120":{"tf":1.4142135623730951},"123":{"tf":2.0},"124":{"tf":1.4142135623730951},"125":{"tf":1.4142135623730951},"129":{"tf":1.0},"131":{"tf":2.23606797749979},"132":{"tf":1.0},"133":{"tf":1.0},"16":{"tf":1.0},"174":{"tf":1.0},"35":{"tf":1.0},"60":{"tf":1.0},"63":{"tf":1.0},"64":{"tf":1.0},"66":{"tf":2.0},"67":{"tf":1.0},"69":{"tf":1.4142135623730951},"78":{"tf":1.0},"80":{"tf":2.0},"90":{"tf":1.4142135623730951}},"df":23,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"{":{"docs":{},"df":0,"g":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{"133":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}},"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"126":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"127":{"tf":1.4142135623730951}},"df":1}}}}}},"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"125":{"tf":1.0},"133":{"tf":1.0}},"df":2}}}}}}}}}}}}}}},"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{"168":{"tf":1.0}},"df":1}},"m":{"docs":{},"df":0,"t":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"81":{"tf":1.0},"83":{"tf":1.0}},"df":2}}}}}}}}},"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"z":{"docs":{},"df":0,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"o":{"docs":{},"df":0,"f":{"docs":{"111":{"tf":1.0}},"df":1}}}}}}}}}}}},"n":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"n":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{},"df":0,"x":{"docs":{},"df":0,"4":{"docs":{},"df":0,"0":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"64":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"o":{"docs":{},"df":0,"p":{"docs":{},"df":0,"s":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"127":{"tf":1.0}},"df":1}}}}}}}}}}}}},"p":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{"133":{"tf":1.0},"83":{"tf":1.0}},"df":2,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"v":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"165":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"92":{"tf":1.0}},"df":1}}},"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"165":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}},"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"v":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"165":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"165":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"df":0,"y":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"{":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{"124":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"120":{"tf":1.0},"2":{"tf":1.0},"28":{"tf":1.0},"75":{"tf":1.0},"76":{"tf":1.0},"88":{"tf":1.0},"93":{"tf":1.0}},"df":7,"l":{"docs":{},"df":0,"i":{"docs":{"103":{"tf":1.0},"119":{"tf":1.0},"53":{"tf":1.4142135623730951},"68":{"tf":1.0},"96":{"tf":1.0}},"df":5}}}},"s":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"168":{"tf":1.0},"184":{"tf":1.0},"61":{"tf":1.0},"78":{"tf":1.0},"87":{"tf":1.0},"91":{"tf":1.0}},"df":6}}}}}},"u":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{"191":{"tf":1.0},"67":{"tf":1.0},"77":{"tf":1.0}},"df":3}}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"x":{"docs":{"100":{"tf":1.7320508075688772},"11":{"tf":1.0},"12":{"tf":1.0},"124":{"tf":2.0},"17":{"tf":1.0},"188":{"tf":1.4142135623730951},"19":{"tf":1.0},"190":{"tf":1.0},"2":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":3.0},"43":{"tf":1.7320508075688772},"55":{"tf":1.0},"56":{"tf":1.0},"59":{"tf":1.7320508075688772},"60":{"tf":1.0},"61":{"tf":3.0},"63":{"tf":2.449489742783178},"64":{"tf":1.4142135623730951},"66":{"tf":2.0},"67":{"tf":3.1622776601683795},"68":{"tf":1.0},"69":{"tf":1.7320508075688772},"7":{"tf":1.0},"71":{"tf":1.4142135623730951},"72":{"tf":1.4142135623730951},"77":{"tf":1.4142135623730951},"78":{"tf":1.0},"79":{"tf":1.0},"80":{"tf":1.0},"81":{"tf":2.23606797749979},"82":{"tf":1.4142135623730951},"84":{"tf":1.4142135623730951},"87":{"tf":1.0},"90":{"tf":1.4142135623730951}},"df":35,"_":{"docs":{},"df":0,"m":{"docs":{"100":{"tf":1.4142135623730951},"126":{"tf":1.0}},"df":2,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"m":{"docs":{"133":{"tf":1.0}},"df":1,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"w":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{"127":{"tf":1.0}},"df":1}}}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{"125":{"tf":1.0},"133":{"tf":1.0}},"df":2,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"f":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{"123":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"|":{"docs":{},"df":0,"_":{"docs":{"123":{"tf":1.0}},"df":1}}}}}}},"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"x":{"docs":{"126":{"tf":1.0},"131":{"tf":1.0}},"df":2}}}}},"{":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"127":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}}}},"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"100":{"tf":1.0}},"df":1,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"s":{"docs":{},"df":0,"y":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"s":{"docs":{},"df":0,"y":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"k":{"docs":{},"df":0,"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"c":{"docs":{"81":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}},"{":{"docs":{},"df":0,"s":{"docs":{},"df":0,"y":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"71":{"tf":1.0}},"df":1}}}}}}},"s":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"72":{"tf":1.0},"81":{"tf":1.0}},"df":2}}}}}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"100":{"tf":1.0}},"df":1}}}}}}}}}}},"_":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"191":{"tf":1.0},"60":{"tf":1.0},"64":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0},"76":{"tf":1.0},"77":{"tf":1.7320508075688772},"79":{"tf":1.0}},"df":8}}}}},"{":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"81":{"tf":1.0},"83":{"tf":1.0}},"df":2}}}}}}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"h":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"81":{"tf":1.0}},"df":1,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"g":{"docs":{"77":{"tf":1.4142135623730951}},"df":1}}}}},"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{"83":{"tf":1.0}},"df":1}},"p":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"l":{"docs":{},"df":0,"n":{"docs":{"77":{"tf":1.0}},"df":1}}}}}}}},"{":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"g":{"docs":{"191":{"tf":1.0},"64":{"tf":1.0}},"df":2}}}}}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"df":0,"t":{"docs":{"110":{"tf":1.0},"111":{"tf":1.0},"112":{"tf":1.0},"123":{"tf":1.0},"153":{"tf":1.0},"187":{"tf":1.0},"189":{"tf":1.4142135623730951},"190":{"tf":1.0},"33":{"tf":1.0},"76":{"tf":1.0},"99":{"tf":1.0}},"df":11}},"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"d":{"docs":{},"df":0,"n":{"docs":{},"df":0,"’":{"docs":{},"df":0,"t":{"docs":{"126":{"tf":1.0}},"df":1}}}}},"n":{"docs":{},"df":0,"t":{"docs":{"122":{"tf":1.4142135623730951},"128":{"tf":1.0},"72":{"tf":1.0},"80":{"tf":1.7320508075688772},"81":{"tf":2.449489742783178},"84":{"tf":1.7320508075688772}},"df":6,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"o":{"docs":{},"df":0,"k":{"docs":{"81":{"tf":1.0}},"df":1}}}},"e":{"docs":{},"df":0,"r":{"docs":{"122":{"tf":2.8284271247461903},"123":{"tf":2.8284271247461903},"124":{"tf":2.23606797749979},"125":{"tf":2.8284271247461903},"126":{"tf":2.6457513110645907},"64":{"tf":1.0},"83":{"tf":1.4142135623730951}},"df":7,".":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"(":{"docs":{},"df":0,"c":{"docs":{},"df":0,"s":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"(":{"docs":{},"df":0,"0":{"docs":{"126":{"tf":1.0}},"df":1},"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,".":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"(":{"docs":{},"df":0,"c":{"docs":{},"df":0,"s":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"126":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"_":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"d":{"docs":{},"df":0,"(":{"docs":{},"df":0,"1":{"docs":{"124":{"tf":1.0}},"df":1}}}}}}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"(":{"docs":{},"df":0,"c":{"docs":{"125":{"tf":1.0}},"df":1}}}}}}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"(":{"docs":{},"df":0,"c":{"docs":{"125":{"tf":1.0}},"df":1}}}}}}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"0":{"docs":{"124":{"tf":1.0}},"df":1}}}}}}}}}}}},"p":{"docs":{},"df":0,"l":{"docs":{"92":{"tf":1.0}},"df":1}},"r":{"docs":{},"df":0,"s":{"docs":{"115":{"tf":1.0},"91":{"tf":1.0}},"df":2}}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"131":{"tf":1.0},"157":{"tf":1.0},"172":{"tf":1.0},"24":{"tf":1.0},"60":{"tf":1.0},"67":{"tf":1.0},"70":{"tf":1.0},"84":{"tf":1.0}},"df":8}}}},"p":{"docs":{"127":{"tf":1.4142135623730951},"72":{"tf":1.0},"76":{"tf":1.0}},"df":3,"u":{"docs":{"112":{"tf":1.0},"130":{"tf":1.0},"35":{"tf":1.0},"63":{"tf":2.449489742783178},"64":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.0},"7":{"tf":1.0},"79":{"tf":1.0},"81":{"tf":1.4142135623730951},"86":{"tf":1.4142135623730951}},"df":11}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{"31":{"tf":1.0},"46":{"tf":1.0},"78":{"tf":1.0},"83":{"tf":1.0}},"df":4}},"t":{"docs":{},"df":0,"e":{"docs":{"100":{"tf":1.0},"115":{"tf":1.0},"126":{"tf":1.0},"127":{"tf":1.7320508075688772},"132":{"tf":1.4142135623730951},"133":{"tf":2.0},"14":{"tf":1.4142135623730951},"142":{"tf":2.449489742783178},"144":{"tf":1.4142135623730951},"147":{"tf":2.0},"148":{"tf":1.0},"151":{"tf":1.0},"158":{"tf":2.449489742783178},"16":{"tf":2.449489742783178},"160":{"tf":1.7320508075688772},"161":{"tf":1.0},"164":{"tf":1.0},"165":{"tf":1.0},"168":{"tf":1.4142135623730951},"17":{"tf":1.4142135623730951},"174":{"tf":1.0},"175":{"tf":1.0},"176":{"tf":1.0},"177":{"tf":1.7320508075688772},"178":{"tf":1.4142135623730951},"179":{"tf":1.0},"180":{"tf":1.7320508075688772},"188":{"tf":2.6457513110645907},"191":{"tf":1.4142135623730951},"193":{"tf":1.4142135623730951},"195":{"tf":1.7320508075688772},"197":{"tf":1.7320508075688772},"60":{"tf":2.0},"67":{"tf":1.0},"69":{"tf":3.3166247903554},"70":{"tf":2.6457513110645907},"71":{"tf":1.0},"72":{"tf":1.4142135623730951},"76":{"tf":1.4142135623730951},"77":{"tf":1.4142135623730951},"78":{"tf":3.0},"80":{"tf":1.0},"82":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.7320508075688772},"92":{"tf":1.0}},"df":46,"s":{"docs":{},"df":0,".":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{"133":{"tf":1.0},"78":{"tf":1.0}},"df":2}}}}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"100":{"tf":1.0},"104":{"tf":1.4142135623730951},"105":{"tf":1.4142135623730951},"117":{"tf":1.0},"122":{"tf":1.4142135623730951},"126":{"tf":1.0},"146":{"tf":1.0},"179":{"tf":1.0},"180":{"tf":1.7320508075688772},"184":{"tf":1.0},"188":{"tf":1.0},"20":{"tf":1.0},"44":{"tf":1.4142135623730951},"56":{"tf":1.0},"57":{"tf":1.4142135623730951},"61":{"tf":1.0},"63":{"tf":1.0},"67":{"tf":1.4142135623730951},"68":{"tf":1.0},"76":{"tf":1.0},"93":{"tf":1.0}},"df":21,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"105":{"tf":1.0}},"df":1}},"v":{"docs":{"9":{"tf":1.0}},"df":1}}}},"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"9":{"tf":1.0}},"df":1}}}},"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"123":{"tf":3.0},"125":{"tf":2.23606797749979},"126":{"tf":2.0},"127":{"tf":1.7320508075688772},"128":{"tf":1.0},"131":{"tf":1.0},"133":{"tf":1.0},"64":{"tf":1.0},"78":{"tf":1.0}},"df":9,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"125":{"tf":2.0}},"df":1}}}}}}}}}},"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"162":{"tf":1.0},"38":{"tf":1.0},"4":{"tf":1.0},"61":{"tf":1.4142135623730951},"67":{"tf":1.0}},"df":5}}},"u":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"32":{"tf":1.0}},"df":1}}}},"d":{"docs":{},"df":0,"e":{"docs":{"71":{"tf":1.0}},"df":1}}},"w":{"docs":{"44":{"tf":1.0}},"df":1}},"s":{"docs":{"125":{"tf":2.0},"173":{"tf":1.7320508075688772}},"df":2,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"125":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}},"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"102":{"tf":1.0}},"df":1}}}},"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"125":{"tf":2.8284271247461903}},"df":1,"(":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{},"df":0,"(":{"docs":{},"df":0,"0":{"docs":{"125":{"tf":1.0}},"df":1}}}}}}},"<":{"docs":{},"df":0,"u":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{"125":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}},"r":{"docs":{"91":{"tf":1.0},"92":{"tf":1.0},"93":{"tf":1.0}},"df":3},"t":{"docs":{},"df":0,"r":{"docs":{"168":{"tf":1.0}},"df":1,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{"168":{"tf":1.0}},"df":1}}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{"168":{"tf":1.0}},"df":1}}}}}},"t":{"docs":{"76":{"tf":1.0}},"df":1,"i":{"docs":{"168":{"tf":1.0},"173":{"tf":1.0},"174":{"tf":1.4142135623730951}},"df":3},"o":{"docs":{},"df":0,"r":{"docs":{"142":{"tf":1.0}},"df":1}},"y":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"c":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"173":{"tf":1.4142135623730951}},"df":1}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"173":{"tf":2.449489742783178}},"df":1}}}}}}},"p":{"docs":{},"df":0,"e":{"docs":{"174":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"b":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"139":{"tf":1.0}},"df":1}}}}}}},"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{"3":{"tf":1.0}},"df":1}}},"l":{"docs":{"20":{"tf":1.0},"59":{"tf":1.0}},"df":2},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"104":{"tf":1.0},"110":{"tf":1.0},"126":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.0},"158":{"tf":1.0},"170":{"tf":1.0},"189":{"tf":1.0},"2":{"tf":1.0},"62":{"tf":1.0},"75":{"tf":1.0},"78":{"tf":1.0},"90":{"tf":1.0}},"df":13}}}}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"118":{"tf":1.4142135623730951},"27":{"tf":1.0},"61":{"tf":1.4142135623730951},"63":{"tf":1.0},"68":{"tf":1.0},"76":{"tf":1.0},"82":{"tf":1.0}},"df":7,"i":{"docs":{},"df":0,"s":{"docs":{"161":{"tf":1.0}},"df":1}}}}}}},"v":{"docs":{},"df":0,"r":{"docs":{"91":{"tf":1.0},"92":{"tf":1.0},"93":{"tf":1.0}},"df":3}},"x":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{"100":{"tf":1.0}},"df":1}}}},"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"100":{"tf":1.0}},"df":1}}}}}}},"y":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{"72":{"tf":1.4142135623730951}},"df":1}}}},"d":{"docs":{"154":{"tf":1.0},"83":{"tf":1.0}},"df":2,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"122":{"tf":1.0}},"df":1}}}},"s":{"docs":{},"df":0,"h":{"docs":{"144":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"a":{"docs":{"103":{"tf":1.0},"111":{"tf":1.4142135623730951},"112":{"tf":1.0},"121":{"tf":1.0},"122":{"tf":1.4142135623730951},"123":{"tf":1.0},"126":{"tf":1.0},"132":{"tf":1.0},"163":{"tf":1.0},"164":{"tf":1.0},"168":{"tf":1.0},"172":{"tf":1.4142135623730951},"173":{"tf":1.7320508075688772},"183":{"tf":1.0},"188":{"tf":1.4142135623730951},"61":{"tf":1.0},"62":{"tf":1.7320508075688772},"66":{"tf":1.0},"69":{"tf":1.4142135623730951},"80":{"tf":1.4142135623730951},"86":{"tf":1.4142135623730951},"87":{"tf":1.0},"88":{"tf":2.0},"90":{"tf":1.0}},"df":24,"s":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"87":{"tf":1.0},"88":{"tf":1.0}},"df":2}}}}},"t":{"docs":{},"df":0,"y":{"docs":{},"df":0,"p":{"docs":{"173":{"tf":1.4142135623730951},"174":{"tf":1.0}},"df":2}}}},"e":{"docs":{"161":{"tf":1.0},"38":{"tf":1.0}},"df":2}},"y":{"docs":{"5":{"tf":1.0},"93":{"tf":1.0}},"df":2}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"158":{"tf":1.0},"190":{"tf":1.0}},"df":2,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"126":{"tf":1.4142135623730951},"128":{"tf":1.0}},"df":2}}}}},"l":{"docs":{"120":{"tf":1.0},"136":{"tf":1.4142135623730951},"137":{"tf":1.0},"19":{"tf":1.4142135623730951}},"df":4,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{"133":{"tf":1.0}},"df":1,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"133":{"tf":1.0}},"df":1}}}}}}}}}}},"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{"43":{"tf":1.0}},"df":1}}},"u":{"docs":{},"df":0,"g":{"docs":{"136":{"tf":1.0},"187":{"tf":2.23606797749979},"188":{"tf":1.4142135623730951},"19":{"tf":1.0},"24":{"tf":2.0},"26":{"tf":1.4142135623730951},"27":{"tf":1.4142135623730951},"28":{"tf":1.4142135623730951},"29":{"tf":1.4142135623730951},"30":{"tf":2.0},"31":{"tf":2.0},"32":{"tf":1.4142135623730951},"33":{"tf":1.0},"34":{"tf":1.7320508075688772},"36":{"tf":1.0},"4":{"tf":1.4142135623730951},"43":{"tf":1.7320508075688772},"54":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.0},"64":{"tf":3.0},"68":{"tf":2.6457513110645907},"77":{"tf":1.7320508075688772},"78":{"tf":1.0},"81":{"tf":1.0},"91":{"tf":1.0}},"df":26,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"e":{"docs":{},"df":0,"x":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"67":{"tf":1.0},"77":{"tf":1.0}},"df":2,"(":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"g":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"e":{"docs":{},"df":0,"x":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"f":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"77":{"tf":1.0}},"df":1}}}}}},"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"c":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"191":{"tf":1.0},"64":{"tf":1.0},"67":{"tf":1.0},"77":{"tf":1.0},"81":{"tf":1.0}},"df":5}}}}}}}}}}}}}}}}}}}}}}}}}},"_":{"docs":{"62":{"tf":1.0}},"df":1,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"b":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"v":{"docs":{"62":{"tf":1.0}},"df":1}}}}}},"f":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"62":{"tf":1.0}},"df":1}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"f":{"docs":{},"df":0,"o":{"docs":{"62":{"tf":1.0}},"df":1}}}},"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"62":{"tf":1.0}},"df":1}},"o":{"docs":{},"df":0,"c":{"docs":{"62":{"tf":1.0}},"df":1}}},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"f":{"docs":{},"df":0,"o":{"docs":{"62":{"tf":1.0}},"df":1}}}}}}},"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"b":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"62":{"tf":1.0}},"df":1}}},"t":{"docs":{},"df":0,"y":{"docs":{},"df":0,"p":{"docs":{"62":{"tf":1.0}},"df":1}}}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{"62":{"tf":1.0}},"df":1}}}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{"62":{"tf":1.0}},"df":1}}}},"g":{"docs":{"11":{"tf":1.0},"187":{"tf":1.0},"188":{"tf":1.7320508075688772},"24":{"tf":1.4142135623730951},"26":{"tf":1.0},"27":{"tf":1.0},"28":{"tf":1.7320508075688772},"35":{"tf":1.0},"50":{"tf":1.0},"53":{"tf":1.0},"64":{"tf":1.4142135623730951},"68":{"tf":1.0},"77":{"tf":1.0},"81":{"tf":1.0},"83":{"tf":1.0}},"df":15},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"f":{"docs":{},"df":0,"o":{"docs":{"187":{"tf":1.4142135623730951},"63":{"tf":1.0}},"df":2}}}},"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"68":{"tf":1.0}},"df":1}}}}}}}}}}},"c":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{"137":{"tf":1.0},"190":{"tf":1.0},"99":{"tf":1.0}},"df":3}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"133":{"tf":1.7320508075688772},"134":{"tf":1.4142135623730951},"158":{"tf":1.4142135623730951},"184":{"tf":1.0},"78":{"tf":1.0},"80":{"tf":1.4142135623730951},"84":{"tf":1.4142135623730951}},"df":7}}},"o":{"docs":{},"df":0,"d":{"docs":{"63":{"tf":1.4142135623730951}},"df":1}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{"160":{"tf":1.0}},"df":1}}}}},"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"153":{"tf":1.0},"168":{"tf":1.0},"70":{"tf":1.0}},"df":3}}},"e":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"113":{"tf":1.4142135623730951}},"df":1}}}},"f":{"docs":{},"df":0,"a":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{"142":{"tf":1.0},"151":{"tf":1.0},"164":{"tf":1.0},"166":{"tf":1.0},"17":{"tf":1.0},"173":{"tf":1.0},"183":{"tf":1.0},"187":{"tf":1.4142135623730951},"189":{"tf":1.0},"38":{"tf":1.0},"61":{"tf":1.4142135623730951},"63":{"tf":1.4142135623730951},"67":{"tf":1.0},"74":{"tf":1.0},"81":{"tf":1.0},"82":{"tf":1.7320508075688772},"92":{"tf":1.0}},"df":17,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"l":{"docs":{"68":{"tf":1.0},"82":{"tf":2.0}},"df":2,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"(":{"docs":{},"df":0,"i":{"docs":{},"df":0,"r":{"docs":{},"df":0,"q":{"docs":{},"df":0,"n":{"docs":{"82":{"tf":1.0}},"df":1}}}}},"_":{"docs":{"62":{"tf":1.4142135623730951}},"df":1}}}}}}}},"p":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"62":{"tf":1.0}},"df":1}}}}}}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{"100":{"tf":1.4142135623730951},"107":{"tf":1.4142135623730951},"112":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.0},"115":{"tf":1.7320508075688772},"133":{"tf":1.0},"160":{"tf":1.0},"172":{"tf":1.4142135623730951},"173":{"tf":2.6457513110645907},"174":{"tf":1.0},"188":{"tf":1.0},"60":{"tf":1.0},"61":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.4142135623730951},"71":{"tf":1.0},"72":{"tf":1.0},"73":{"tf":1.0},"74":{"tf":1.0},"76":{"tf":1.4142135623730951},"78":{"tf":1.4142135623730951},"83":{"tf":1.0},"84":{"tf":1.0}},"df":24,"i":{"docs":{},"df":0,"t":{"docs":{"109":{"tf":1.0},"132":{"tf":1.0},"168":{"tf":1.0},"172":{"tf":1.7320508075688772},"173":{"tf":1.7320508075688772},"80":{"tf":1.0}},"df":6}}}},"m":{"docs":{},"df":0,"t":{"docs":{"63":{"tf":2.449489742783178}},"df":1,"/":{"docs":{},"df":0,"q":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{"63":{"tf":1.0}},"df":1}}}}},"_":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"t":{"docs":{"63":{"tf":1.0}},"df":1}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"h":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"63":{"tf":1.0}},"df":1}}}}}}}}}}}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"y":{"docs":{"71":{"tf":1.0},"72":{"tf":1.0}},"df":2}}},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"l":{"docs":{"68":{"tf":1.4142135623730951}},"df":1}}}},"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{"84":{"tf":1.0}},"df":1}}}}}},"n":{"docs":{},"df":0,"y":{"docs":{},"df":0,"(":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{"81":{"tf":1.0}},"df":1}}}}}}}}}}}}},"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"12":{"tf":1.0},"123":{"tf":1.0},"124":{"tf":1.0},"13":{"tf":1.0},"133":{"tf":1.0},"134":{"tf":1.0},"138":{"tf":1.4142135623730951},"155":{"tf":1.0},"158":{"tf":1.4142135623730951},"161":{"tf":1.0},"168":{"tf":1.4142135623730951},"176":{"tf":1.0},"178":{"tf":1.4142135623730951},"179":{"tf":1.0},"183":{"tf":1.0},"184":{"tf":1.4142135623730951},"188":{"tf":2.8284271247461903},"193":{"tf":1.0},"197":{"tf":1.0},"63":{"tf":1.4142135623730951},"64":{"tf":1.0},"68":{"tf":2.0},"77":{"tf":2.0},"78":{"tf":1.0},"81":{"tf":1.0}},"df":25}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{"190":{"tf":1.0}},"df":1,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"127":{"tf":1.0}},"df":1}}}},"e":{"docs":{},"df":0,"r":{"docs":{"80":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{"164":{"tf":1.4142135623730951},"173":{"tf":1.0}},"df":2}}}}}}},"i":{"docs":{},"df":0,"v":{"docs":{"125":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"g":{"docs":{"104":{"tf":1.0}},"df":1}}}}}}}}}},"s":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"61":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"b":{"docs":{"104":{"tf":1.0},"124":{"tf":1.0},"175":{"tf":1.0},"199":{"tf":1.0}},"df":4},"p":{"docs":{},"df":0,"t":{"docs":{"190":{"tf":1.0},"193":{"tf":1.0},"195":{"tf":1.0},"197":{"tf":1.0},"199":{"tf":1.0},"5":{"tf":1.0},"84":{"tf":1.0},"90":{"tf":1.0}},"df":8}}}}},"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"n":{"docs":{"103":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.4142135623730951},"110":{"tf":1.0},"125":{"tf":1.0},"131":{"tf":1.0},"140":{"tf":1.0},"26":{"tf":1.0},"33":{"tf":1.0},"87":{"tf":1.0},"97":{"tf":1.0}},"df":11}},"r":{"docs":{"119":{"tf":1.0},"14":{"tf":1.0},"77":{"tf":1.0},"84":{"tf":1.4142135623730951}},"df":4}},"k":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"p":{"docs":{"4":{"tf":1.0},"86":{"tf":1.4142135623730951},"87":{"tf":1.0}},"df":3}}}},"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"142":{"tf":1.0}},"df":1}}}}}}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"68":{"tf":1.0}},"df":1}},"i":{"docs":{},"df":0,"l":{"docs":{"11":{"tf":1.0},"114":{"tf":1.0},"124":{"tf":1.4142135623730951},"131":{"tf":1.0},"158":{"tf":1.0},"160":{"tf":1.0},"163":{"tf":1.0},"165":{"tf":1.0},"166":{"tf":1.0},"193":{"tf":1.0},"195":{"tf":1.0},"196":{"tf":1.0},"197":{"tf":1.0},"2":{"tf":1.0},"29":{"tf":1.0},"5":{"tf":1.0},"60":{"tf":1.0},"7":{"tf":1.4142135623730951},"70":{"tf":1.0},"84":{"tf":1.0}},"df":20}}},"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"137":{"tf":1.0},"28":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.0},"87":{"tf":1.0}},"df":5}},"r":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"138":{"tf":1.0},"72":{"tf":1.0},"84":{"tf":1.0}},"df":3}}}}}},"v":{"docs":{"187":{"tf":1.4142135623730951},"40":{"tf":1.0},"50":{"tf":1.0},"78":{"tf":1.4142135623730951}},"df":4,"/":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"/":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"b":{"docs":{},"df":0,"/":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"1":{"docs":{},"df":0,"/":{"docs":{},"df":0,"0":{"docs":{},"df":0,"1":{"docs":{},"df":0,"8":{"docs":{"44":{"tf":1.7320508075688772}},"df":1}}}}}}},"<":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,">":{"docs":{},"df":0,"/":{"docs":{},"df":0,"<":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"44":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}},"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"p":{"docs":{"108":{"tf":1.0},"112":{"tf":1.0},"130":{"tf":1.0},"139":{"tf":1.4142135623730951},"156":{"tf":1.0},"187":{"tf":1.4142135623730951},"195":{"tf":1.0},"2":{"tf":2.0},"26":{"tf":1.0},"28":{"tf":1.7320508075688772},"29":{"tf":1.0},"30":{"tf":1.0},"31":{"tf":1.0},"32":{"tf":1.4142135623730951},"33":{"tf":1.0},"34":{"tf":1.7320508075688772},"35":{"tf":1.4142135623730951},"36":{"tf":1.4142135623730951},"4":{"tf":1.0},"5":{"tf":1.0},"55":{"tf":1.0},"64":{"tf":1.0},"65":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":1.4142135623730951},"72":{"tf":1.0},"8":{"tf":1.0},"86":{"tf":1.7320508075688772},"93":{"tf":1.0},"94":{"tf":1.0}},"df":31}}}},"i":{"docs":{},"df":0,"c":{"docs":{"100":{"tf":1.4142135623730951},"114":{"tf":1.0},"118":{"tf":1.4142135623730951},"127":{"tf":1.4142135623730951},"187":{"tf":1.4142135623730951},"19":{"tf":1.0},"199":{"tf":1.0},"24":{"tf":1.4142135623730951},"32":{"tf":1.4142135623730951},"35":{"tf":1.0},"44":{"tf":1.7320508075688772},"61":{"tf":1.4142135623730951},"62":{"tf":1.0},"63":{"tf":1.0},"64":{"tf":1.0},"66":{"tf":1.7320508075688772},"68":{"tf":1.7320508075688772},"77":{"tf":1.4142135623730951},"81":{"tf":1.0},"82":{"tf":1.0},"84":{"tf":2.0},"86":{"tf":1.0}},"df":22}}}},"i":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{},"df":0,"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{"32":{"tf":1.0},"83":{"tf":1.0}},"df":2}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"9":{"tf":1.0}},"df":1}}}}},"d":{"docs":{},"df":0,"n":{"docs":{},"df":0,"’":{"docs":{},"df":0,"t":{"docs":{"108":{"tf":1.0},"123":{"tf":1.0},"53":{"tf":1.0}},"df":3}}}},"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"101":{"tf":1.0},"114":{"tf":1.4142135623730951},"119":{"tf":2.449489742783178},"12":{"tf":1.4142135623730951},"120":{"tf":1.7320508075688772},"134":{"tf":1.0},"138":{"tf":1.0},"155":{"tf":1.4142135623730951},"156":{"tf":1.0},"157":{"tf":1.0},"158":{"tf":1.0},"180":{"tf":1.4142135623730951},"181":{"tf":1.0},"182":{"tf":1.0},"186":{"tf":1.0},"19":{"tf":1.4142135623730951},"190":{"tf":1.0},"192":{"tf":1.0},"53":{"tf":1.4142135623730951},"60":{"tf":1.0},"61":{"tf":1.0},"62":{"tf":1.4142135623730951},"68":{"tf":1.7320508075688772},"7":{"tf":1.7320508075688772},"80":{"tf":1.0},"84":{"tf":1.0}},"df":26}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{"120":{"tf":1.0},"127":{"tf":1.0},"161":{"tf":1.0},"178":{"tf":1.0}},"df":4}}}}}}},"g":{"docs":{"7":{"tf":1.0}},"df":1,"i":{"docs":{},"df":0,"t":{"docs":{"115":{"tf":1.0},"127":{"tf":1.0},"151":{"tf":1.0}},"df":3}}},"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{"93":{"tf":1.0}},"df":1}}},"p":{"docs":{"69":{"tf":1.0}},"df":1},"r":{"docs":{"109":{"tf":1.4142135623730951}},"df":1,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"104":{"tf":1.0},"107":{"tf":1.7320508075688772},"108":{"tf":2.6457513110645907},"109":{"tf":3.0},"112":{"tf":1.4142135623730951},"114":{"tf":1.0},"34":{"tf":1.0},"88":{"tf":1.0}},"df":8,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,".":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"109":{"tf":1.0}},"df":1}}}}},"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"109":{"tf":1.0}},"df":1}}}}}}}}}},"l":{"docs":{},"df":0,"i":{"docs":{"124":{"tf":1.0},"126":{"tf":1.0},"133":{"tf":1.0},"160":{"tf":1.0},"163":{"tf":1.0},"164":{"tf":1.0},"165":{"tf":1.0},"175":{"tf":1.0},"176":{"tf":1.0},"197":{"tf":1.0},"26":{"tf":1.0},"69":{"tf":1.0},"80":{"tf":1.0},"84":{"tf":1.0},"86":{"tf":1.0}},"df":15},"y":{"docs":{},"df":0,"—":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{"84":{"tf":1.0}},"df":1}}}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"177":{"tf":1.0},"178":{"tf":1.0},"56":{"tf":1.0},"64":{"tf":1.0}},"df":4}}}}}}},"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"106":{"tf":1.7320508075688772},"107":{"tf":1.4142135623730951},"108":{"tf":1.4142135623730951},"109":{"tf":1.4142135623730951},"123":{"tf":1.4142135623730951},"124":{"tf":1.4142135623730951},"187":{"tf":1.0},"189":{"tf":1.0},"69":{"tf":1.0}},"df":9}},"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"62":{"tf":1.7320508075688772},"83":{"tf":1.4142135623730951}},"df":2}}}}},"t":{"docs":{},"df":0,"r":{"docs":{"126":{"tf":1.0}},"df":1}}}},"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"44":{"tf":1.0},"5":{"tf":1.0},"53":{"tf":1.7320508075688772},"68":{"tf":1.7320508075688772},"69":{"tf":1.0},"70":{"tf":1.4142135623730951},"81":{"tf":1.0}},"df":7}}}}},"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"165":{"tf":1.0},"186":{"tf":1.0}},"df":2}}}},"k":{"docs":{"38":{"tf":1.0},"62":{"tf":1.0}},"df":2},"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"y":{"docs":{"118":{"tf":1.4142135623730951},"64":{"tf":1.0},"68":{"tf":1.0},"86":{"tf":1.0}},"df":4}}}},"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"163":{"tf":1.0}},"df":1}},"g":{"docs":{},"df":0,"u":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{"144":{"tf":1.0}},"df":1}}}}}}},"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"133":{"tf":1.0},"175":{"tf":1.0},"42":{"tf":1.0},"50":{"tf":1.0},"78":{"tf":1.0},"9":{"tf":1.0}},"df":6}}}},"o":{"docs":{"40":{"tf":1.0}},"df":1}}}},"v":{"docs":{},"df":0,"e":{"docs":{"65":{"tf":1.0}},"df":1,"r":{"docs":{},"df":0,"g":{"docs":{"60":{"tf":1.0}},"df":1},"s":{"docs":{"84":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"76":{"tf":1.0}},"df":1}}}}}},"m":{"docs":{},"df":0,"a":{"docs":{"130":{"tf":1.0},"164":{"tf":1.0}},"df":2}},"n":{"docs":{},"df":0,"f":{"docs":{"43":{"tf":1.0}},"df":1}},"o":{"docs":{"108":{"tf":1.0},"5":{"tf":1.0},"86":{"tf":1.4142135623730951}},"df":3,"c":{"docs":{"100":{"tf":1.0}},"df":1,"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"128":{"tf":1.0},"159":{"tf":1.0},"160":{"tf":1.0},"163":{"tf":1.0},"165":{"tf":1.0},"183":{"tf":1.0},"199":{"tf":1.0},"24":{"tf":1.0},"27":{"tf":1.0},"73":{"tf":1.0},"74":{"tf":1.0},"78":{"tf":1.0}},"df":12}}}}}},"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"n":{"docs":{},"df":0,"’":{"docs":{},"df":0,"t":{"docs":{"107":{"tf":1.0},"187":{"tf":1.0},"2":{"tf":1.0},"70":{"tf":1.0},"72":{"tf":1.0},"83":{"tf":1.0},"92":{"tf":1.0}},"df":7}}}}},"n":{"docs":{},"df":0,"\'":{"docs":{},"df":0,"t":{"docs":{"109":{"tf":1.4142135623730951},"187":{"tf":1.0},"188":{"tf":1.0}},"df":3}},"e":{"docs":{"103":{"tf":1.0},"126":{"tf":1.4142135623730951},"5":{"tf":1.0},"57":{"tf":1.0},"59":{"tf":1.0},"61":{"tf":1.0},"64":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.0}},"df":9},"t":{"docs":{},"df":0,"c":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"109":{"tf":2.8284271247461903}},"df":1}}}},"’":{"docs":{},"df":0,"t":{"docs":{"123":{"tf":1.0},"134":{"tf":1.0},"136":{"tf":1.0},"174":{"tf":1.0},"179":{"tf":1.0},"22":{"tf":1.0},"55":{"tf":1.0},"60":{"tf":1.0},"67":{"tf":1.0},"82":{"tf":1.0},"93":{"tf":1.4142135623730951}},"df":11}}},"u":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"68":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"a":{"docs":{"104":{"tf":1.0}},"df":1,"(":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"104":{"tf":1.0}},"df":1}}}}}}}}}}},"w":{"docs":{},"df":0,"n":{"docs":{"112":{"tf":1.0},"127":{"tf":1.0},"146":{"tf":1.0},"151":{"tf":1.0},"63":{"tf":1.0},"72":{"tf":1.0}},"df":6,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"20":{"tf":1.0},"39":{"tf":1.0},"59":{"tf":1.4142135623730951}},"df":3}}}},"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{"187":{"tf":1.0},"77":{"tf":1.0}},"df":2}}}}},"z":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"187":{"tf":1.0}},"df":1}}}},"p":{"docs":{"127":{"tf":1.7320508075688772}},"df":1,".":{"docs":{},"df":0,"g":{"docs":{},"df":0,"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{"127":{"tf":1.4142135623730951}},"df":1}}}}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"119":{"tf":1.0}},"df":1}}}}},"i":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{"155":{"tf":1.0}},"df":1,"n":{"docs":{"107":{"tf":1.4142135623730951},"108":{"tf":1.4142135623730951},"118":{"tf":1.0},"128":{"tf":1.0}},"df":4},"r":{"docs":{"115":{"tf":1.7320508075688772},"117":{"tf":1.0},"118":{"tf":1.7320508075688772},"165":{"tf":1.4142135623730951},"52":{"tf":1.4142135623730951},"53":{"tf":1.0},"93":{"tf":1.7320508075688772}},"df":7}}}},"o":{"docs":{},"df":0,"p":{"docs":{"126":{"tf":1.0}},"df":1}}},"s":{"docs":{},"df":0,"p":{"docs":{"191":{"tf":1.4142135623730951}},"df":1}},"u":{"docs":{},"df":0,"e":{"docs":{"110":{"tf":1.0},"115":{"tf":1.0},"139":{"tf":1.0},"146":{"tf":1.0},"17":{"tf":1.0},"173":{"tf":1.0},"34":{"tf":1.0},"84":{"tf":1.0}},"df":8},"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"93":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"e":{"docs":{"126":{"tf":1.0},"127":{"tf":1.0},"63":{"tf":1.0},"68":{"tf":1.7320508075688772}},"df":4}}},"y":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"132":{"tf":1.4142135623730951},"14":{"tf":1.0},"17":{"tf":1.0},"180":{"tf":1.0}},"df":4}}}}},"e":{"docs":{"191":{"tf":1.4142135623730951}},"df":1,".":{"docs":{},"df":0,"g":{"docs":{"107":{"tf":1.0},"117":{"tf":2.0},"119":{"tf":1.0},"13":{"tf":1.0},"189":{"tf":1.4142135623730951},"190":{"tf":1.4142135623730951},"2":{"tf":1.0},"40":{"tf":1.0},"53":{"tf":1.0},"66":{"tf":1.7320508075688772},"77":{"tf":1.0},"80":{"tf":1.0}},"df":12}},"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{"162":{"tf":1.0},"38":{"tf":2.23606797749979},"43":{"tf":2.0},"46":{"tf":1.0},"47":{"tf":1.0},"49":{"tf":1.0},"61":{"tf":2.6457513110645907},"63":{"tf":1.4142135623730951},"64":{"tf":1.0},"67":{"tf":1.7320508075688772},"68":{"tf":1.7320508075688772}},"df":11,"/":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"g":{"docs":{},"df":0,"/":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"p":{"docs":{"62":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"x":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"/":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{"64":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}},"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{"63":{"tf":1.7320508075688772}},"df":1}}}}},"m":{"docs":{},"df":0,"y":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"p":{"docs":{"162":{"tf":1.0}},"df":1}}}}}}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"/":{"docs":{},"df":0,"e":{"docs":{},"df":0,"x":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"/":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{"63":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}},"h":{"docs":{},"df":0,"f":{"docs":{"38":{"tf":1.7320508075688772},"61":{"tf":1.0},"67":{"tf":1.7320508075688772}},"df":3,"/":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"g":{"docs":{},"df":0,"/":{"docs":{},"df":0,"e":{"docs":{},"df":0,"x":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"/":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{"68":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}},"c":{"docs":{},"df":0,"h":{"docs":{"114":{"tf":1.0},"120":{"tf":1.0},"122":{"tf":1.4142135623730951},"123":{"tf":1.7320508075688772},"139":{"tf":1.0},"153":{"tf":1.4142135623730951},"158":{"tf":1.4142135623730951},"165":{"tf":1.0},"173":{"tf":1.0},"174":{"tf":1.0},"69":{"tf":1.0},"76":{"tf":1.0},"77":{"tf":1.0},"80":{"tf":1.0},"96":{"tf":1.0},"99":{"tf":1.0}},"df":16}},"r":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"131":{"tf":1.0},"7":{"tf":1.4142135623730951},"72":{"tf":1.0},"87":{"tf":1.0}},"df":4}}}}},"s":{"docs":{},"df":0,"i":{"docs":{"105":{"tf":1.0},"115":{"tf":1.0},"21":{"tf":1.0},"33":{"tf":1.0},"50":{"tf":1.0},"70":{"tf":1.0}},"df":6,"e":{"docs":{},"df":0,"r":{"docs":{"103":{"tf":1.0},"160":{"tf":1.0},"168":{"tf":1.0},"178":{"tf":1.0},"19":{"tf":1.0},"2":{"tf":1.0},"78":{"tf":1.0},"93":{"tf":1.4142135623730951}},"df":8},"s":{"docs":{},"df":0,"t":{"docs":{"177":{"tf":1.0}},"df":1}}},"l":{"docs":{},"df":0,"i":{"docs":{"160":{"tf":1.0}},"df":1}}}}},"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"o":{"docs":{"63":{"tf":1.0},"77":{"tf":1.4142135623730951}},"df":2}},"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"y":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{"129":{"tf":1.0},"192":{"tf":1.0},"29":{"tf":1.0},"63":{"tf":1.0}},"df":4}}}}}}}},"d":{"docs":{},"df":0,"g":{"docs":{"122":{"tf":1.0},"127":{"tf":1.0},"53":{"tf":1.0}},"df":3},"i":{"docs":{},"df":0,"t":{"docs":{"4":{"tf":1.0},"58":{"tf":1.0},"77":{"tf":1.0},"78":{"tf":1.0}},"df":4}},"u":{"docs":{},"df":0,"c":{"docs":{"36":{"tf":1.0}},"df":1}}},"f":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"o":{"docs":{},"df":0,"k":{"docs":{"83":{"tf":1.0}},"df":1}}}},"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"188":{"tf":1.0},"190":{"tf":1.0},"24":{"tf":1.0},"32":{"tf":1.0},"33":{"tf":1.0}},"df":5}}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{"124":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"119":{"tf":1.0}},"df":1}}}}},"g":{"docs":{"155":{"tf":1.0}},"df":1},"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"134":{"tf":2.0},"163":{"tf":1.0}},"df":2}}}}},"f":{"docs":{"62":{"tf":2.23606797749979}},"df":1,"/":{"docs":{},"df":0,"d":{"docs":{},"df":0,"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"f":{"docs":{"30":{"tf":1.0}},"df":1}}}}}},"3":{"docs":{},"df":0,"2":{"docs":{"62":{"tf":1.4142135623730951}},"df":1}}},"i":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"190":{"tf":1.0}},"df":1}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"124":{"tf":1.0},"173":{"tf":1.0}},"df":2}}}}}}},"m":{"docs":{},"df":0,"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{"129":{"tf":2.0}},"df":1}}}},"e":{"docs":{},"df":0,"d":{"docs":{"0":{"tf":1.4142135623730951},"1":{"tf":1.4142135623730951},"103":{"tf":1.0},"114":{"tf":1.7320508075688772},"115":{"tf":1.7320508075688772},"117":{"tf":1.0},"118":{"tf":1.4142135623730951},"12":{"tf":1.4142135623730951},"120":{"tf":1.4142135623730951},"121":{"tf":1.0},"122":{"tf":1.7320508075688772},"125":{"tf":1.0},"126":{"tf":1.0},"127":{"tf":1.0},"130":{"tf":1.4142135623730951},"131":{"tf":1.0},"14":{"tf":1.0},"140":{"tf":1.0},"142":{"tf":1.0},"148":{"tf":1.0},"151":{"tf":1.0},"156":{"tf":1.0},"157":{"tf":1.0},"161":{"tf":1.0},"165":{"tf":1.0},"166":{"tf":1.0},"167":{"tf":1.0},"169":{"tf":1.0},"175":{"tf":1.0},"177":{"tf":1.0},"179":{"tf":1.0},"192":{"tf":1.0},"195":{"tf":1.0},"2":{"tf":1.7320508075688772},"22":{"tf":1.0},"24":{"tf":1.7320508075688772},"26":{"tf":1.4142135623730951},"27":{"tf":1.7320508075688772},"3":{"tf":1.4142135623730951},"30":{"tf":1.0},"31":{"tf":1.4142135623730951},"32":{"tf":1.7320508075688772},"33":{"tf":1.0},"4":{"tf":1.0},"49":{"tf":1.0},"5":{"tf":3.3166247903554},"54":{"tf":1.0},"56":{"tf":1.4142135623730951},"63":{"tf":1.4142135623730951},"64":{"tf":1.7320508075688772},"69":{"tf":1.7320508075688772},"70":{"tf":1.0},"72":{"tf":1.0},"77":{"tf":1.0},"78":{"tf":1.0},"80":{"tf":1.0},"86":{"tf":1.7320508075688772}},"df":57,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"/":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"k":{"docs":{},"df":0,"/":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"/":{"docs":{},"df":0,"2":{"docs":{},"df":0,"6":{"docs":{},"df":0,"9":{"docs":{"62":{"tf":1.0}},"df":1}}}}}}}}}}}}}}},"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"x":{"docs":{"58":{"tf":1.0},"59":{"tf":1.0},"67":{"tf":1.0}},"df":3}}}}}}}}}},"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"5":{"tf":1.0}},"df":1}}}}}}}}}}},"i":{"docs":{},"df":0,"t":{"docs":{"124":{"tf":1.0},"137":{"tf":1.0},"180":{"tf":1.0}},"df":3}},"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"y":{"docs":{"80":{"tf":1.0}},"df":1}}},"t":{"docs":{"125":{"tf":1.0}},"df":1,"i":{"docs":{"127":{"tf":1.0}},"df":1}}},"u":{"docs":{},"df":0,"l":{"docs":{"114":{"tf":1.0},"22":{"tf":1.4142135623730951},"54":{"tf":1.0},"55":{"tf":1.0},"61":{"tf":1.0},"63":{"tf":2.449489742783178}},"df":6}}},"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"106":{"tf":1.4142135623730951},"107":{"tf":1.7320508075688772},"108":{"tf":2.6457513110645907},"109":{"tf":4.0},"112":{"tf":1.7320508075688772},"123":{"tf":1.0},"127":{"tf":1.4142135623730951},"142":{"tf":1.0},"151":{"tf":1.0},"158":{"tf":2.0},"187":{"tf":1.7320508075688772},"28":{"tf":1.0},"32":{"tf":1.0},"63":{"tf":1.0},"68":{"tf":2.23606797749979},"69":{"tf":1.0},"77":{"tf":2.449489742783178},"84":{"tf":1.4142135623730951}},"df":18,"e":{"docs":{},"df":0,"=":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,",":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"=":{"docs":{},"df":0,"n":{"docs":{"63":{"tf":1.7320508075688772},"64":{"tf":1.0},"68":{"tf":1.0}},"df":3}}}}}}}}}}}}}}}},"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{"104":{"tf":1.4142135623730951},"142":{"tf":1.0},"155":{"tf":1.4142135623730951}},"df":3},"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"28":{"tf":1.0}},"df":1}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{"160":{"tf":1.0}},"df":1}}}}}},"d":{"docs":{"108":{"tf":1.0},"115":{"tf":1.0},"133":{"tf":1.4142135623730951},"144":{"tf":1.0},"166":{"tf":1.0},"60":{"tf":1.0},"64":{"tf":1.0},"81":{"tf":1.0},"87":{"tf":1.0}},"df":9,"i":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{"62":{"tf":1.0}},"df":1}},"f":{"docs":{"158":{"tf":1.0}},"df":1}},"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"84":{"tf":1.0}},"df":1}}}}}}},"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"c":{"docs":{"102":{"tf":1.0},"103":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.7320508075688772},"109":{"tf":1.0},"110":{"tf":1.4142135623730951},"127":{"tf":1.0},"128":{"tf":1.0},"164":{"tf":1.0},"87":{"tf":1.0}},"df":10}}}},"g":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"130":{"tf":1.0},"97":{"tf":1.0}},"df":2}}},"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{"189":{"tf":1.0},"54":{"tf":1.0},"63":{"tf":1.0},"8":{"tf":1.0},"93":{"tf":1.0}},"df":5}}}},"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"103":{"tf":1.0},"114":{"tf":1.0},"118":{"tf":1.0},"119":{"tf":1.0},"123":{"tf":1.0},"125":{"tf":1.4142135623730951},"126":{"tf":1.4142135623730951},"127":{"tf":1.7320508075688772},"128":{"tf":1.0},"131":{"tf":1.0},"133":{"tf":1.0},"165":{"tf":1.0},"166":{"tf":1.4142135623730951},"173":{"tf":1.0},"28":{"tf":1.0},"29":{"tf":1.0},"60":{"tf":1.0},"78":{"tf":1.0},"80":{"tf":1.4142135623730951},"84":{"tf":1.0},"92":{"tf":1.0}},"df":21}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"114":{"tf":1.0},"125":{"tf":1.0},"126":{"tf":1.0},"128":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.0},"78":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0}},"df":9}},"i":{"docs":{},"df":0,"r":{"docs":{"100":{"tf":1.0},"110":{"tf":1.0},"124":{"tf":1.0},"126":{"tf":1.0},"160":{"tf":1.0},"71":{"tf":1.0},"92":{"tf":1.0}},"df":7}},"r":{"docs":{},"df":0,"i":{"docs":{"121":{"tf":1.0},"122":{"tf":1.0},"123":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":1.0},"126":{"tf":1.0},"127":{"tf":1.4142135623730951},"133":{"tf":1.0},"134":{"tf":1.0},"191":{"tf":1.0},"60":{"tf":2.23606797749979},"62":{"tf":1.0},"64":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0},"76":{"tf":1.0},"77":{"tf":1.7320508075688772},"79":{"tf":1.0},"81":{"tf":1.0},"83":{"tf":1.0}},"df":22}}},"u":{"docs":{},"df":0,"m":{"docs":{"109":{"tf":1.0},"154":{"tf":1.0}},"df":2,"e":{"docs":{},"df":0,"r":{"docs":{"163":{"tf":1.0}},"df":1}}}},"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"114":{"tf":1.0},"13":{"tf":1.7320508075688772},"14":{"tf":1.4142135623730951},"15":{"tf":1.0},"2":{"tf":1.0},"35":{"tf":1.0},"4":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.0},"80":{"tf":1.0}},"df":10}}}}}},"q":{"docs":{},"df":0,"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"114":{"tf":1.0},"189":{"tf":1.0},"38":{"tf":1.0}},"df":3}},"i":{"docs":{},"df":0,"v":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"165":{"tf":1.0}},"df":1}}}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{"142":{"tf":1.4142135623730951},"155":{"tf":1.4142135623730951},"28":{"tf":1.0}},"df":3,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"(":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"154":{"tf":1.0}},"df":1}}}}}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"(":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"154":{"tf":1.0}},"df":1}}}}}}}}}}},"u":{"docs":{},"df":0,"r":{"docs":{"154":{"tf":1.0}},"df":1}}}},"g":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"125":{"tf":1.0}},"df":1}}}}},"r":{"docs":{"108":{"tf":2.6457513110645907}},"df":1,"o":{"docs":{},"df":0,"r":{"docs":{"103":{"tf":1.0},"110":{"tf":1.0},"115":{"tf":1.0},"122":{"tf":1.0},"126":{"tf":1.0},"133":{"tf":1.0},"139":{"tf":1.4142135623730951},"174":{"tf":1.0},"28":{"tf":1.0},"29":{"tf":1.0},"63":{"tf":1.4142135623730951},"75":{"tf":1.0},"80":{"tf":1.4142135623730951},"84":{"tf":1.0}},"df":14}}}},"s":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{"125":{"tf":1.0},"126":{"tf":1.0},"156":{"tf":1.0},"170":{"tf":1.0},"27":{"tf":1.0}},"df":5}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{"24":{"tf":1.0},"28":{"tf":1.0}},"df":2}}}}}},"t":{"docs":{},"df":0,"c":{"docs":{"112":{"tf":1.0},"117":{"tf":1.0},"13":{"tf":1.0},"132":{"tf":1.0},"168":{"tf":1.0},"17":{"tf":1.0},"175":{"tf":1.0},"184":{"tf":1.0},"4":{"tf":1.0}},"df":9,"/":{"docs":{},"df":0,"u":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"v":{"docs":{},"df":0,"/":{"docs":{},"df":0,"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,".":{"docs":{},"df":0,"d":{"docs":{},"df":0,"/":{"docs":{},"df":0,"7":{"docs":{},"df":0,"0":{"docs":{"44":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}},"v":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"u":{"docs":{"159":{"tf":1.0},"63":{"tf":1.0}},"df":2}}},"e":{"docs":{},"df":0,"n":{"docs":{"114":{"tf":1.0},"123":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":1.0},"126":{"tf":1.4142135623730951},"127":{"tf":1.0},"134":{"tf":1.0},"136":{"tf":1.0},"150":{"tf":1.0},"151":{"tf":1.0},"160":{"tf":1.0},"22":{"tf":1.0},"7":{"tf":1.0},"76":{"tf":1.0},"78":{"tf":1.0},"80":{"tf":1.4142135623730951},"84":{"tf":1.0},"92":{"tf":1.0}},"df":18,"t":{"docs":{"28":{"tf":1.4142135623730951},"80":{"tf":1.4142135623730951}},"df":2,"u":{"docs":{"132":{"tf":1.0},"64":{"tf":1.0}},"df":2}}},"r":{"docs":{},"df":0,"y":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{"3":{"tf":1.0}},"df":1}}}},"o":{"docs":{},"df":0,"n":{"docs":{"1":{"tf":1.0},"186":{"tf":1.0}},"df":2}},"t":{"docs":{},"df":0,"h":{"docs":{"86":{"tf":1.4142135623730951},"98":{"tf":1.0}},"df":2}}}}},"i":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{"166":{"tf":1.0}},"df":1}}}}}},"x":{"docs":{"49":{"tf":1.0}},"df":1,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"133":{"tf":1.0},"93":{"tf":1.0}},"df":2,"l":{"docs":{},"df":0,"i":{"docs":{"139":{"tf":1.0},"53":{"tf":1.0},"72":{"tf":1.0},"78":{"tf":1.0},"91":{"tf":1.0},"96":{"tf":1.0}},"df":6}}}},"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"28":{"tf":1.0},"31":{"tf":1.0}},"df":2}},"p":{"docs":{},"df":0,"l":{"docs":{"102":{"tf":1.0},"103":{"tf":1.4142135623730951},"104":{"tf":1.4142135623730951},"106":{"tf":1.0},"107":{"tf":1.0},"109":{"tf":2.0},"111":{"tf":1.0},"118":{"tf":1.0},"122":{"tf":1.0},"123":{"tf":1.0},"125":{"tf":1.0},"126":{"tf":1.7320508075688772},"127":{"tf":1.4142135623730951},"129":{"tf":1.0},"130":{"tf":1.4142135623730951},"131":{"tf":1.0},"136":{"tf":1.4142135623730951},"138":{"tf":1.4142135623730951},"146":{"tf":1.4142135623730951},"153":{"tf":1.0},"154":{"tf":1.0},"155":{"tf":1.0},"158":{"tf":1.0},"159":{"tf":1.0},"160":{"tf":1.7320508075688772},"161":{"tf":1.0},"164":{"tf":1.0},"165":{"tf":1.0},"168":{"tf":1.0},"169":{"tf":1.0},"170":{"tf":1.0},"173":{"tf":1.0},"178":{"tf":1.0},"188":{"tf":1.4142135623730951},"191":{"tf":1.0},"2":{"tf":1.0},"38":{"tf":1.0},"54":{"tf":1.0},"56":{"tf":1.0},"61":{"tf":1.0},"63":{"tf":2.23606797749979},"64":{"tf":1.4142135623730951},"67":{"tf":1.4142135623730951},"68":{"tf":1.0},"69":{"tf":1.7320508075688772},"7":{"tf":1.4142135623730951},"72":{"tf":1.7320508075688772},"76":{"tf":1.0},"78":{"tf":1.7320508075688772},"79":{"tf":1.4142135623730951},"80":{"tf":1.0},"81":{"tf":1.4142135623730951},"83":{"tf":1.0},"87":{"tf":1.0},"88":{"tf":1.0},"90":{"tf":1.0},"93":{"tf":1.0}},"df":57,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{"93":{"tf":1.0}},"df":1}}}}},"s":{"docs":{},"df":0,"/":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{"64":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.0}},"df":3,"s":{"docs":{},"df":0,":":{"docs":{},"df":0,"1":{"docs":{},"df":0,"1":{"docs":{"68":{"tf":1.0}},"df":1},"3":{"docs":{"68":{"tf":1.0}},"df":1},"5":{"docs":{},"df":0,":":{"docs":{},"df":0,"5":{"docs":{"77":{"tf":1.0}},"df":1}}}}}}}}}}}}}},"\\\\":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{},"df":0,"s":{"docs":{},"df":0,":":{"docs":{},"df":0,"1":{"docs":{},"df":0,"3":{"docs":{"64":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}},"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"111":{"tf":1.0}},"df":1},"p":{"docs":{},"df":0,"t":{"docs":{"181":{"tf":1.0},"68":{"tf":1.0},"72":{"tf":1.0},"80":{"tf":4.795831523312719},"81":{"tf":2.23606797749979},"82":{"tf":3.1622776601683795},"83":{"tf":3.0},"84":{"tf":2.6457513110645907}},"df":8,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"f":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"83":{"tf":2.23606797749979}},"df":1}}}}}}}}},"s":{"docs":{},"df":0,"s":{"docs":{"139":{"tf":1.0}},"df":1}}},"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{"105":{"tf":1.4142135623730951},"5":{"tf":1.0}},"df":2}}}},"l":{"docs":{},"df":0,"u":{"docs":{},"df":0,"d":{"docs":{"14":{"tf":1.0},"188":{"tf":1.0}},"df":2},"s":{"docs":{"123":{"tf":1.0},"126":{"tf":1.7320508075688772},"136":{"tf":1.0},"137":{"tf":1.0}},"df":4}}}},"e":{"docs":{},"df":0,"c":{"docs":{"62":{"tf":1.0}},"df":1,"u":{"docs":{},"df":0,"t":{"docs":{"100":{"tf":1.0},"110":{"tf":1.0},"111":{"tf":1.0},"120":{"tf":1.0},"123":{"tf":1.0},"125":{"tf":1.0},"126":{"tf":1.0},"131":{"tf":1.0},"138":{"tf":1.7320508075688772},"161":{"tf":1.0},"162":{"tf":1.0},"176":{"tf":1.0},"177":{"tf":1.0},"178":{"tf":1.4142135623730951},"186":{"tf":1.0},"187":{"tf":1.0},"189":{"tf":1.0},"24":{"tf":1.0},"28":{"tf":1.4142135623730951},"62":{"tf":1.0},"63":{"tf":1.4142135623730951},"64":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.0},"78":{"tf":1.4142135623730951},"80":{"tf":2.0},"84":{"tf":1.4142135623730951}},"df":27,"o":{"docs":{},"df":0,"r":{"docs":{"128":{"tf":1.0},"129":{"tf":1.0}},"df":2}}}}}},"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"136":{"tf":1.0}},"df":1}}}}},"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"112":{"tf":1.0},"113":{"tf":1.0},"127":{"tf":1.4142135623730951},"141":{"tf":1.0},"164":{"tf":1.0},"166":{"tf":1.0},"169":{"tf":1.0},"172":{"tf":1.0},"175":{"tf":1.4142135623730951},"44":{"tf":1.0},"70":{"tf":1.0},"74":{"tf":1.0},"84":{"tf":1.0},"94":{"tf":1.0}},"df":14}},"t":{"docs":{"191":{"tf":1.0},"63":{"tf":1.4142135623730951},"64":{"tf":1.7320508075688772},"67":{"tf":1.0},"68":{"tf":1.0},"77":{"tf":2.0}},"df":6,"(":{"docs":{},"df":0,"e":{"docs":{},"df":0,"x":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"f":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"77":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}},"p":{"docs":{},"df":0,"(":{"docs":{},"df":0,"f":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"f":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,".":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{"191":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}},"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"117":{"tf":1.0},"159":{"tf":1.0},"160":{"tf":1.7320508075688772}},"df":3}}},"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"151":{"tf":1.0},"159":{"tf":1.0},"182":{"tf":1.0},"86":{"tf":1.0}},"df":4}},"n":{"docs":{},"df":0,"s":{"docs":{"189":{"tf":1.0}},"df":1}},"r":{"docs":{},"df":0,"i":{"docs":{"29":{"tf":1.0},"3":{"tf":1.0},"70":{"tf":1.0}},"df":3,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{"156":{"tf":1.0}},"df":1}}}}}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"160":{"tf":1.0},"173":{"tf":1.0},"19":{"tf":1.0},"2":{"tf":1.0}},"df":4}},"n":{"docs":{"125":{"tf":1.0}},"df":1}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"128":{"tf":1.0},"139":{"tf":1.4142135623730951},"164":{"tf":1.0}},"df":3,"l":{"docs":{},"df":0,"i":{"docs":{"125":{"tf":1.0},"158":{"tf":1.0},"78":{"tf":1.0}},"df":3}}}}}},"o":{"docs":{},"df":0,"r":{"docs":{"130":{"tf":1.0},"132":{"tf":1.0}},"df":2}}},"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{"191":{"tf":1.7320508075688772}},"df":1,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"_":{"docs":{},"df":0,"o":{"docs":{},"df":0,"f":{"docs":{},"df":0,"_":{"docs":{},"df":0,"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"191":{"tf":2.0}},"df":1}}}}}}}}}}}}}}},"r":{"docs":{},"df":0,"t":{"docs":{"160":{"tf":1.0},"182":{"tf":1.0},"84":{"tf":1.4142135623730951}},"df":3}},"s":{"docs":{"107":{"tf":1.0},"136":{"tf":1.0},"137":{"tf":1.0},"153":{"tf":1.0},"16":{"tf":1.0},"171":{"tf":1.0},"70":{"tf":1.0},"76":{"tf":1.0},"84":{"tf":1.0},"86":{"tf":1.0},"88":{"tf":1.0},"96":{"tf":1.0}},"df":12}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"115":{"tf":1.0},"160":{"tf":1.4142135623730951},"78":{"tf":1.0}},"df":3}}}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"44":{"tf":1.0},"68":{"tf":1.0}},"df":2},"s":{"docs":{"142":{"tf":1.0},"150":{"tf":1.0},"24":{"tf":1.0},"26":{"tf":1.0},"27":{"tf":1.0},"29":{"tf":1.4142135623730951},"31":{"tf":1.0},"34":{"tf":1.0}},"df":8}},"r":{"docs":{},"df":0,"n":{"docs":{"11":{"tf":1.0},"118":{"tf":1.0},"119":{"tf":1.0},"133":{"tf":1.0},"173":{"tf":1.4142135623730951},"174":{"tf":1.0},"176":{"tf":1.0},"177":{"tf":1.0},"179":{"tf":1.0},"183":{"tf":1.0},"184":{"tf":1.0},"191":{"tf":1.0},"64":{"tf":1.0},"78":{"tf":1.0}},"df":14}}},"r":{"docs":{},"df":0,"a":{"docs":{"123":{"tf":1.0},"131":{"tf":1.0},"158":{"tf":1.0},"161":{"tf":1.0},"64":{"tf":1.0},"77":{"tf":1.0}},"df":6,"c":{"docs":{},"df":0,"t":{"docs":{"59":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"m":{"docs":{"126":{"tf":1.0},"128":{"tf":1.0},"160":{"tf":1.4142135623730951}},"df":3}}}}}},"f":{"docs":{"155":{"tf":2.0},"53":{"tf":2.449489742783178},"66":{"tf":1.4142135623730951}},"df":3,"3":{"docs":{},"df":0,"2":{"docs":{"191":{"tf":1.0}},"df":1}},"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{"183":{"tf":1.0},"78":{"tf":1.0}},"df":2},"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"24":{"tf":1.0},"32":{"tf":1.0}},"df":2}}}},"t":{"docs":{"122":{"tf":1.0},"125":{"tf":1.0},"126":{"tf":1.0},"173":{"tf":1.0},"53":{"tf":1.0},"68":{"tf":1.0},"78":{"tf":1.0}},"df":7,"o":{"docs":{},"df":0,"r":{"docs":{"101":{"tf":1.4142135623730951},"137":{"tf":1.0}},"df":2}}}},"i":{"docs":{},"df":0,"l":{"docs":{"134":{"tf":1.0},"136":{"tf":1.0},"146":{"tf":1.0},"61":{"tf":1.0},"77":{"tf":1.0}},"df":5,"u":{"docs":{},"df":0,"r":{"docs":{"136":{"tf":1.4142135623730951}},"df":1}}},"r":{"docs":{"92":{"tf":1.0}},"df":1}},"k":{"docs":{},"df":0,"e":{"docs":{"125":{"tf":1.0}},"df":1}},"l":{"docs":{},"df":0,"s":{"docs":{"122":{"tf":1.0},"123":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":1.0},"126":{"tf":1.0},"127":{"tf":1.0},"165":{"tf":2.0},"58":{"tf":1.4142135623730951}},"df":8,"e":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"100":{"tf":1.0}},"df":1}}}}}}}}}}},"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"114":{"tf":1.0},"144":{"tf":1.0},"155":{"tf":1.0},"7":{"tf":1.0}},"df":4,"a":{"docs":{},"df":0,"r":{"docs":{"10":{"tf":1.0},"133":{"tf":1.0},"139":{"tf":1.0},"2":{"tf":1.0},"4":{"tf":1.4142135623730951},"65":{"tf":1.0}},"df":6}}}}}},"q":{"docs":{"167":{"tf":1.0},"5":{"tf":1.0}},"df":2},"r":{"docs":{"69":{"tf":1.0},"72":{"tf":1.0},"86":{"tf":1.0}},"df":3},"s":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"127":{"tf":1.0},"67":{"tf":1.0},"86":{"tf":1.0},"93":{"tf":1.0}},"df":4}}}},"t":{"docs":{"186":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"80":{"tf":1.0}},"df":1}}},"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{"68":{"tf":1.0},"83":{"tf":1.0}},"df":2}}},"v":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"35":{"tf":1.0}},"df":1}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"11":{"tf":1.0},"128":{"tf":1.0},"130":{"tf":1.0},"158":{"tf":3.0},"159":{"tf":1.0},"16":{"tf":1.0},"160":{"tf":1.0},"161":{"tf":1.0},"17":{"tf":1.0},"188":{"tf":1.7320508075688772},"195":{"tf":1.0},"2":{"tf":1.0},"29":{"tf":1.0},"31":{"tf":1.0},"35":{"tf":1.0},"77":{"tf":2.23606797749979},"80":{"tf":1.0},"84":{"tf":1.0}},"df":18,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{"133":{"tf":1.0}},"df":1,"_":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"_":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"l":{"docs":{"133":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}},"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{"43":{"tf":1.0}},"df":1}}}},"e":{"docs":{},"df":0,"d":{"docs":{"174":{"tf":1.0}},"df":1},"l":{"docs":{"13":{"tf":1.0},"139":{"tf":1.0},"187":{"tf":1.0},"94":{"tf":1.0}},"df":4}},"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{"63":{"tf":1.0}},"df":1}}}},"t":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"_":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"d":{"docs":{"124":{"tf":1.0}},"df":1}}}}}}},"w":{"docs":{"12":{"tf":1.0},"165":{"tf":1.0},"37":{"tf":1.0},"42":{"tf":1.0},"61":{"tf":1.0},"63":{"tf":1.0},"68":{"tf":1.0},"87":{"tf":1.0},"98":{"tf":1.0}},"df":9}},"f":{"docs":{},"df":0,"i":{"docs":{"183":{"tf":1.0},"184":{"tf":1.0}},"df":2}},"i":{"docs":{},"df":0,"d":{"docs":{},"df":0,"d":{"docs":{},"df":0,"l":{"docs":{"55":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"d":{"docs":{"107":{"tf":1.0},"150":{"tf":1.0},"154":{"tf":1.0},"166":{"tf":1.0},"72":{"tf":1.4142135623730951},"73":{"tf":1.0},"74":{"tf":1.4142135623730951},"75":{"tf":1.4142135623730951},"83":{"tf":1.0},"91":{"tf":1.7320508075688772}},"df":10}}},"l":{"docs":{},"df":0,"e":{"docs":{"13":{"tf":1.0},"133":{"tf":1.0},"172":{"tf":1.0},"173":{"tf":2.0},"174":{"tf":2.23606797749979},"175":{"tf":1.0},"176":{"tf":1.0},"178":{"tf":1.0},"184":{"tf":1.4142135623730951},"199":{"tf":1.0},"20":{"tf":1.0},"44":{"tf":1.0},"53":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.0},"61":{"tf":1.4142135623730951},"62":{"tf":2.449489742783178},"63":{"tf":1.4142135623730951},"64":{"tf":1.4142135623730951},"67":{"tf":1.4142135623730951},"68":{"tf":2.6457513110645907},"73":{"tf":1.0},"74":{"tf":1.0},"77":{"tf":1.0},"84":{"tf":1.0}},"df":25,"(":{"docs":{},"df":0,"\\"":{"docs":{},"df":0,"s":{"docs":{},"df":0,"r":{"docs":{},"df":0,"c":{"docs":{},"df":0,"/":{"docs":{},"df":0,"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{"178":{"tf":1.0}},"df":1}}}}}}}}}}},":":{"docs":{},"df":0,"/":{"docs":{},"df":0,"/":{"docs":{},"df":0,"/":{"docs":{},"df":0,"t":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"/":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"p":{"docs":{"63":{"tf":1.0}},"df":1}}}}}}}}}}}},"l":{"docs":{"20":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.0}},"df":3}},"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"127":{"tf":2.0},"133":{"tf":1.0},"166":{"tf":1.0},"173":{"tf":1.0},"175":{"tf":1.0},"178":{"tf":1.0},"78":{"tf":1.0}},"df":7}},"d":{"docs":{"162":{"tf":1.0},"163":{"tf":1.0},"188":{"tf":1.0},"3":{"tf":1.0},"38":{"tf":1.0},"5":{"tf":1.4142135623730951},"66":{"tf":1.0},"68":{"tf":1.7320508075688772},"69":{"tf":1.0},"71":{"tf":1.0},"73":{"tf":1.0},"74":{"tf":1.0},"78":{"tf":1.0},"8":{"tf":1.0},"9":{"tf":1.0}},"df":15},"e":{"docs":{"35":{"tf":1.0}},"df":1},"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{"123":{"tf":1.0},"49":{"tf":1.0},"63":{"tf":1.0}},"df":3}}}},"r":{"docs":{"158":{"tf":1.4142135623730951}},"df":1,"e":{"docs":{"122":{"tf":1.0},"123":{"tf":1.4142135623730951},"127":{"tf":1.4142135623730951},"83":{"tf":1.0}},"df":4},"m":{"docs":{},"df":0,"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"114":{"tf":1.0},"115":{"tf":1.0},"16":{"tf":1.0},"17":{"tf":1.0},"84":{"tf":1.0}},"df":5}}}},"s":{"docs":{},"df":0,"t":{"docs":{"103":{"tf":1.0},"133":{"tf":1.0},"134":{"tf":1.0},"172":{"tf":1.0},"53":{"tf":1.0},"57":{"tf":1.0},"61":{"tf":1.0},"64":{"tf":1.4142135623730951},"67":{"tf":1.4142135623730951},"68":{"tf":1.4142135623730951},"76":{"tf":1.0},"77":{"tf":1.0},"88":{"tf":1.0},"92":{"tf":1.0}},"df":14}}},"t":{"docs":{"187":{"tf":1.0},"78":{"tf":1.0}},"df":2},"x":{"docs":{"132":{"tf":1.0},"134":{"tf":1.4142135623730951},"135":{"tf":1.0},"137":{"tf":1.7320508075688772},"138":{"tf":1.0},"177":{"tf":1.0},"32":{"tf":1.0},"8":{"tf":1.0},"92":{"tf":1.0}},"df":9}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{"137":{"tf":1.0},"174":{"tf":1.0},"180":{"tf":1.0},"190":{"tf":1.0},"20":{"tf":1.0},"62":{"tf":1.0},"64":{"tf":1.0},"77":{"tf":1.4142135623730951}},"df":8},"s":{"docs":{},"df":0,"h":{"docs":{"11":{"tf":1.0},"187":{"tf":1.7320508075688772},"188":{"tf":1.0},"189":{"tf":1.0},"28":{"tf":1.0},"35":{"tf":1.0},"54":{"tf":1.0},"61":{"tf":1.0},"62":{"tf":1.4142135623730951},"66":{"tf":2.0},"67":{"tf":1.0},"68":{"tf":1.0},"72":{"tf":1.0},"86":{"tf":1.0},"87":{"tf":1.4142135623730951}},"df":15}},"v":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"117":{"tf":1.0}},"df":1}}}}},"e":{"docs":{},"df":0,"x":{"docs":{},"df":0,"i":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"154":{"tf":1.0},"173":{"tf":1.0},"84":{"tf":1.0}},"df":3}}}}},"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"103":{"tf":1.0},"11":{"tf":1.0},"155":{"tf":1.7320508075688772},"16":{"tf":1.0},"191":{"tf":2.449489742783178},"194":{"tf":1.4142135623730951},"38":{"tf":1.7320508075688772}},"df":7,".":{"docs":{},"df":0,"f":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"191":{"tf":1.0}},"df":1}}}}}}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"f":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"191":{"tf":2.23606797749979}},"df":1,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"191":{"tf":1.0}},"df":1}}}}}}}},".":{"docs":{},"df":0,"e":{"docs":{},"df":0,"x":{"docs":{},"df":0,"p":{"docs":{"191":{"tf":1.0}},"df":1}}},"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"191":{"tf":1.0}},"df":1}},"q":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"191":{"tf":1.0}},"df":1}}}}}}}}}}}}},"f":{"docs":{"191":{"tf":1.0}},"df":1,"(":{"docs":{},"df":0,"f":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"191":{"tf":1.0}},"df":1}}}}}}}}}}},"n":{"docs":{"100":{"tf":1.4142135623730951},"101":{"tf":1.4142135623730951},"102":{"tf":1.4142135623730951},"104":{"tf":2.0},"107":{"tf":2.23606797749979},"108":{"tf":2.23606797749979},"109":{"tf":2.8284271247461903},"112":{"tf":1.0},"121":{"tf":1.0},"122":{"tf":1.4142135623730951},"123":{"tf":1.4142135623730951},"124":{"tf":1.4142135623730951},"125":{"tf":2.0},"126":{"tf":1.4142135623730951},"127":{"tf":2.0},"133":{"tf":2.0},"134":{"tf":1.0},"146":{"tf":1.7320508075688772},"153":{"tf":1.4142135623730951},"154":{"tf":1.7320508075688772},"155":{"tf":3.0},"158":{"tf":1.0},"159":{"tf":2.23606797749979},"165":{"tf":1.4142135623730951},"166":{"tf":2.0},"168":{"tf":1.0},"173":{"tf":1.4142135623730951},"178":{"tf":1.0},"183":{"tf":1.0},"184":{"tf":1.0},"191":{"tf":1.4142135623730951},"60":{"tf":1.4142135623730951},"64":{"tf":1.4142135623730951},"67":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0},"76":{"tf":1.0},"77":{"tf":1.7320508075688772},"79":{"tf":1.0},"80":{"tf":1.4142135623730951},"81":{"tf":1.4142135623730951},"82":{"tf":1.4142135623730951},"83":{"tf":1.4142135623730951},"84":{"tf":1.4142135623730951},"92":{"tf":1.4142135623730951},"93":{"tf":2.449489742783178},"98":{"tf":1.0},"99":{"tf":1.4142135623730951}},"df":48,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"e":{"docs":{},"df":0,"x":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"f":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"83":{"tf":1.0}},"df":1}}}}}}}}}}}}},"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"f":{"docs":{},"df":0,"o":{"docs":{"78":{"tf":1.0}},"df":1}}}}}}}}}}},"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"155":{"tf":2.0}},"df":1}}}}}}}}}},"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"u":{"docs":{"129":{"tf":1.0},"55":{"tf":1.0}},"df":2,"s":{"docs":{"26":{"tf":1.0}},"df":1}}},"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"106":{"tf":2.0},"107":{"tf":1.0},"127":{"tf":1.0},"142":{"tf":1.0},"151":{"tf":1.0},"155":{"tf":1.0},"168":{"tf":1.0},"170":{"tf":1.0},"174":{"tf":1.0},"19":{"tf":1.0},"191":{"tf":1.0},"22":{"tf":1.0},"38":{"tf":1.4142135623730951},"41":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.0},"52":{"tf":1.0},"53":{"tf":1.7320508075688772},"61":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.4142135623730951},"69":{"tf":1.4142135623730951},"7":{"tf":1.0},"78":{"tf":1.0},"8":{"tf":1.4142135623730951},"80":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0},"9":{"tf":1.0},"93":{"tf":1.0}},"df":30}}}},"o":{"docs":{"104":{"tf":2.449489742783178},"105":{"tf":1.7320508075688772},"166":{"tf":3.0}},"df":3,"(":{"docs":{},"df":0,"n":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{"168":{"tf":1.0}},"df":1}}}},"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{"104":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{},"df":0,"(":{"docs":{},"df":0,"1":{"docs":{},"df":0,"0":{"docs":{"104":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}},"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"d":{"docs":{"104":{"tf":2.0},"105":{"tf":1.7320508075688772}},"df":2}}}}},"t":{"docs":{},"df":0,"p":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"154":{"tf":1.0},"188":{"tf":1.0}},"df":2}}}}}}},"r":{"docs":{},"df":0,"(":{"docs":{},"df":0,"i":{"docs":{},"df":0,"=":{"docs":{},"df":0,"0":{"docs":{"163":{"tf":1.0}},"df":1}}}},"c":{"docs":{"160":{"tf":1.0}},"df":1},"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"174":{"tf":1.0},"67":{"tf":1.0}},"df":2}}},"m":{"docs":{"120":{"tf":1.0},"183":{"tf":1.0},"78":{"tf":1.0}},"df":3,"a":{"docs":{},"df":0,"l":{"docs":{"158":{"tf":1.0}},"df":1},"t":{"docs":{"199":{"tf":1.0},"62":{"tf":1.0}},"df":2,"!":{"docs":{},"df":0,"(":{"docs":{},"df":0,"\\"":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{"93":{"tf":1.0}},"df":1}}}}}}}},"e":{"docs":{},"df":0,"r":{"docs":{"160":{"tf":1.0},"168":{"tf":1.0}},"df":2}}},"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{"92":{"tf":1.0}},"df":1}}},"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{"64":{"tf":1.0}},"df":1}}}}},"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"132":{"tf":1.4142135623730951},"176":{"tf":1.0},"19":{"tf":1.0},"26":{"tf":1.0},"68":{"tf":1.0}},"df":5}},"r":{"docs":{"191":{"tf":2.0},"90":{"tf":1.0},"91":{"tf":1.0},"92":{"tf":1.0}},"df":4}}},"p":{"docs":{},"df":0,"u":{"docs":{"61":{"tf":1.4142135623730951},"63":{"tf":1.0},"66":{"tf":1.4142135623730951},"67":{"tf":1.4142135623730951}},"df":4}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"137":{"tf":1.0}},"df":1}}}}},"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"k":{"docs":{"128":{"tf":1.4142135623730951}},"df":1}}}}}}},"e":{"docs":{},"df":0,"e":{"docs":{"132":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":2.0},"9":{"tf":1.0}},"df":4,"(":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"146":{"tf":1.0}},"df":1}}}}},"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{"130":{"tf":1.0},"170":{"tf":1.0}},"df":2}}},"z":{"docs":{"64":{"tf":1.0}},"df":1}},"q":{"docs":{},"df":0,"u":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{"11":{"tf":1.0},"122":{"tf":1.0},"76":{"tf":1.0},"88":{"tf":1.0}},"df":4},"t":{"docs":{"5":{"tf":1.0}},"df":1}}}}},"s":{"docs":{},"df":0,"h":{"docs":{"67":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"179":{"tf":1.0},"187":{"tf":1.0},"188":{"tf":1.4142135623730951},"195":{"tf":1.0},"69":{"tf":1.0}},"df":5}}}}},"g":{"docs":{},"df":0,"h":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"94":{"tf":1.0}},"df":1}}}}}},"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"7":{"tf":1.0},"99":{"tf":1.0}},"df":2}}}},"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{"124":{"tf":1.0},"151":{"tf":1.0},"158":{"tf":1.0},"160":{"tf":1.0},"176":{"tf":1.0},"191":{"tf":1.0},"192":{"tf":1.0},"61":{"tf":1.0},"63":{"tf":1.0}},"df":9,"i":{"docs":{"22":{"tf":1.0}},"df":1}}},"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"102":{"tf":1.0},"109":{"tf":1.4142135623730951},"110":{"tf":1.0},"112":{"tf":1.0},"117":{"tf":1.0},"118":{"tf":1.0},"119":{"tf":1.0},"127":{"tf":1.4142135623730951},"128":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.0},"15":{"tf":1.0},"150":{"tf":1.4142135623730951},"151":{"tf":2.0},"157":{"tf":1.0},"159":{"tf":1.4142135623730951},"160":{"tf":1.7320508075688772},"163":{"tf":1.0},"165":{"tf":1.7320508075688772},"168":{"tf":1.4142135623730951},"170":{"tf":1.0},"172":{"tf":1.0},"173":{"tf":2.23606797749979},"182":{"tf":1.0},"183":{"tf":1.4142135623730951},"184":{"tf":2.23606797749979},"187":{"tf":1.0},"190":{"tf":1.7320508075688772},"191":{"tf":1.7320508075688772},"28":{"tf":1.0},"36":{"tf":1.0},"56":{"tf":1.0},"60":{"tf":1.4142135623730951},"64":{"tf":1.7320508075688772},"68":{"tf":1.0},"69":{"tf":1.4142135623730951},"7":{"tf":1.0},"70":{"tf":1.7320508075688772},"71":{"tf":1.4142135623730951},"72":{"tf":2.0},"73":{"tf":1.4142135623730951},"74":{"tf":1.4142135623730951},"75":{"tf":1.7320508075688772},"76":{"tf":2.23606797749979},"77":{"tf":1.0},"78":{"tf":1.7320508075688772},"80":{"tf":2.23606797749979},"82":{"tf":1.4142135623730951},"84":{"tf":1.4142135623730951},"88":{"tf":1.0},"93":{"tf":1.0}},"df":52}}}}}},"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"11":{"tf":1.4142135623730951},"160":{"tf":1.0},"165":{"tf":1.0},"166":{"tf":1.0},"69":{"tf":1.0}},"df":5,"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"137":{"tf":1.0}},"df":1}}}}}}}},"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"159":{"tf":1.0},"166":{"tf":1.0}},"df":2}}}}},"g":{"docs":{},"df":0,"_":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{"127":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"(":{"docs":{},"df":0,"c":{"docs":{},"df":0,"s":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{},"df":0,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"127":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{"127":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"174":{"tf":1.4142135623730951}},"df":1}}}}},"c":{"docs":{},"df":0,"c":{"docs":{"47":{"tf":1.0}},"df":1}},"d":{"docs":{},"df":0,"b":{"docs":{"187":{"tf":1.0},"19":{"tf":1.0},"24":{"tf":1.0},"27":{"tf":1.0},"31":{"tf":1.7320508075688772},"43":{"tf":3.1622776601683795},"46":{"tf":1.4142135623730951},"47":{"tf":1.0},"49":{"tf":1.4142135623730951},"64":{"tf":3.872983346207417},"68":{"tf":5.196152422706632},"77":{"tf":1.4142135623730951}},"df":12}},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"102":{"tf":1.0},"107":{"tf":1.4142135623730951},"108":{"tf":1.0},"109":{"tf":1.0},"11":{"tf":1.0},"112":{"tf":1.0},"113":{"tf":1.4142135623730951},"12":{"tf":1.0},"123":{"tf":1.0},"125":{"tf":1.4142135623730951},"126":{"tf":1.0},"127":{"tf":1.4142135623730951},"131":{"tf":1.0},"136":{"tf":1.0},"147":{"tf":1.0},"151":{"tf":1.0},"158":{"tf":1.0},"160":{"tf":1.0},"161":{"tf":1.4142135623730951},"17":{"tf":1.0},"172":{"tf":1.0},"174":{"tf":2.0},"176":{"tf":1.0},"178":{"tf":1.0},"184":{"tf":1.0},"188":{"tf":1.4142135623730951},"19":{"tf":1.0},"197":{"tf":1.0},"2":{"tf":1.0},"20":{"tf":1.0},"40":{"tf":1.7320508075688772},"56":{"tf":1.0},"57":{"tf":2.0},"6":{"tf":1.0},"62":{"tf":1.0},"67":{"tf":1.4142135623730951},"68":{"tf":1.0},"7":{"tf":1.0},"72":{"tf":1.7320508075688772},"76":{"tf":1.0},"84":{"tf":2.6457513110645907},"87":{"tf":1.0}},"df":42}}},"t":{"docs":{"93":{"tf":1.0}},"df":1,"_":{"docs":{},"df":0,"g":{"docs":{},"df":0,"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{"109":{"tf":1.0}},"df":1}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0}},"df":2}}}}}}}}}}}}}}}}}},"s":{"docs":{},"df":0,"y":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"92":{"tf":1.4142135623730951}},"df":1}}}}}}},"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{"92":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"93":{"tf":1.0}},"df":1}}}}}}}}}}},"f":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{"44":{"tf":1.4142135623730951}},"df":1}}}}}},"h":{"docs":{},"df":0,"z":{"docs":{"12":{"tf":1.0}},"df":1}},"i":{"docs":{},"df":0,"t":{"docs":{"161":{"tf":1.0},"19":{"tf":1.0},"20":{"tf":1.0},"49":{"tf":1.0},"57":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.0},"63":{"tf":1.0},"67":{"tf":1.0}},"df":9,"@":{"docs":{},"df":0,"g":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"u":{"docs":{},"df":0,"b":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,":":{"docs":{},"df":0,"k":{"docs":{},"df":0,"n":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"l":{"docs":{"63":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}},"v":{"docs":{},"df":0,"e":{"docs":{"114":{"tf":1.0},"124":{"tf":1.0},"126":{"tf":1.0},"127":{"tf":2.0},"128":{"tf":1.0},"160":{"tf":1.0},"163":{"tf":1.0},"190":{"tf":1.0},"69":{"tf":1.4142135623730951},"73":{"tf":1.0},"74":{"tf":1.0},"9":{"tf":1.0}},"df":12,"n":{"docs":{"107":{"tf":1.0},"125":{"tf":1.0},"127":{"tf":1.0},"128":{"tf":1.0},"164":{"tf":1.0},"189":{"tf":1.0},"69":{"tf":1.0},"78":{"tf":1.0},"96":{"tf":1.0}},"df":9}}}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{"88":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"64":{"tf":1.0}},"df":1}}}},"o":{"docs":{},"df":0,"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"103":{"tf":1.0},"132":{"tf":1.0},"133":{"tf":2.0},"134":{"tf":1.0},"139":{"tf":1.0},"17":{"tf":1.0},"94":{"tf":1.0},"98":{"tf":1.0},"99":{"tf":1.0}},"df":9,"_":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{"133":{"tf":1.4142135623730951}},"df":1}}}}}},"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{"133":{"tf":1.4142135623730951}},"df":1}}}}}}}},"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"192":{"tf":1.0}},"df":1}}}}}}},"n":{"docs":{},"df":0,"u":{"docs":{"21":{"tf":1.0},"49":{"tf":1.4142135623730951},"53":{"tf":1.0},"68":{"tf":1.0}},"df":4}},"o":{"docs":{"125":{"tf":1.0},"44":{"tf":1.0},"47":{"tf":1.0},"52":{"tf":1.0},"60":{"tf":1.0},"69":{"tf":1.4142135623730951},"72":{"tf":1.4142135623730951}},"df":7,"a":{"docs":{},"df":0,"l":{"docs":{"2":{"tf":1.0}},"df":1}},"e":{"docs":{"126":{"tf":1.0},"127":{"tf":1.0},"60":{"tf":1.0}},"df":3},"o":{"docs":{},"df":0,"d":{"docs":{"158":{"tf":1.0},"56":{"tf":1.0},"61":{"tf":1.0},"84":{"tf":1.0},"92":{"tf":1.0}},"df":5}}},"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{"106":{"tf":1.4142135623730951},"107":{"tf":2.8284271247461903},"108":{"tf":2.23606797749979},"109":{"tf":2.0},"11":{"tf":1.0},"115":{"tf":1.0},"117":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":1.4142135623730951},"153":{"tf":1.7320508075688772},"69":{"tf":1.4142135623730951},"76":{"tf":1.7320508075688772}},"df":12,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.0}},"df":3}}}}}},"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{"76":{"tf":1.0}},"df":1}}}}}},"a":{"docs":{"127":{"tf":3.1622776601683795}},"df":1,".":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{},"df":0,"r":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{},"df":0,"r":{"docs":{},"df":0,"0":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"127":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}},"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"r":{"docs":{},"df":0,".":{"docs":{},"df":0,"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"f":{"docs":{},"df":0,"y":{"docs":{},"df":0,"(":{"docs":{},"df":0,"|":{"docs":{},"df":0,"_":{"docs":{"127":{"tf":1.7320508075688772}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{"107":{"tf":1.4142135623730951},"108":{"tf":1.4142135623730951},"109":{"tf":2.8284271247461903},"112":{"tf":1.7320508075688772}},"df":4,"<":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{"109":{"tf":1.4142135623730951}},"df":1}}},"e":{"docs":{},"df":0,"n":{"docs":{"109":{"tf":3.0},"112":{"tf":1.0}},"df":2}}}}}}}}},"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"102":{"tf":1.4142135623730951}},"df":1}}}}},"l":{"docs":{"53":{"tf":1.0},"68":{"tf":1.0}},"df":2}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{"49":{"tf":1.0},"51":{"tf":1.0},"59":{"tf":1.0}},"df":3},"n":{"docs":{},"df":0,"t":{"docs":{"126":{"tf":1.0}},"df":1}},"p":{"docs":{},"df":0,"h":{"docs":{"78":{"tf":1.0}},"df":1,"i":{"docs":{},"df":0,"c":{"docs":{"86":{"tf":1.4142135623730951}},"df":1}}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"121":{"tf":1.0},"126":{"tf":1.0},"187":{"tf":1.0},"70":{"tf":1.0}},"df":4,"e":{"docs":{},"df":0,"r":{"docs":{"125":{"tf":1.0},"189":{"tf":1.0}},"df":2}},"l":{"docs":{},"df":0,"i":{"docs":{"190":{"tf":1.0}},"df":1}}}},"e":{"docs":{},"df":0,"n":{"docs":{"59":{"tf":1.0}},"df":1}},"p":{"docs":{"44":{"tf":1.0}},"df":1}},"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{"5":{"tf":1.0}},"df":1}}}},"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"p":{"docs":{"5":{"tf":1.0}},"df":1}},"w":{"docs":{"136":{"tf":1.4142135623730951},"138":{"tf":1.0}},"df":2}}},"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{"1":{"tf":1.0},"115":{"tf":1.0},"120":{"tf":1.0},"122":{"tf":1.0},"123":{"tf":1.4142135623730951},"124":{"tf":1.0},"125":{"tf":1.0},"128":{"tf":1.0},"159":{"tf":1.0},"163":{"tf":1.0},"164":{"tf":1.0},"166":{"tf":1.0},"173":{"tf":1.7320508075688772},"71":{"tf":1.0}},"df":14}}}}}},"i":{"docs":{"63":{"tf":1.0}},"df":1,"d":{"docs":{"151":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"141":{"tf":1.0}},"df":1}}}}}}},"y":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"p":{"docs":{"11":{"tf":1.0}},"df":1}}}}}}}},"h":{"docs":{"172":{"tf":1.0}},"df":1,"a":{"docs":{},"df":0,"l":{"docs":{"114":{"tf":1.4142135623730951},"115":{"tf":2.449489742783178},"116":{"tf":1.0},"117":{"tf":2.0},"118":{"tf":1.4142135623730951},"119":{"tf":1.0},"129":{"tf":1.0},"141":{"tf":1.4142135623730951},"142":{"tf":1.7320508075688772},"144":{"tf":1.4142135623730951},"146":{"tf":1.4142135623730951},"147":{"tf":1.7320508075688772},"148":{"tf":1.4142135623730951},"150":{"tf":1.0},"153":{"tf":1.0},"155":{"tf":1.4142135623730951},"193":{"tf":1.0},"195":{"tf":1.0},"69":{"tf":2.0},"76":{"tf":1.4142135623730951}},"df":20,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"s":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"76":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"s":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"76":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}},"f":{"docs":{"184":{"tf":1.0}},"df":1,"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"y":{"docs":{"122":{"tf":1.0}},"df":1}}}},"t":{"docs":{"28":{"tf":1.0},"64":{"tf":1.0},"68":{"tf":3.0},"78":{"tf":1.7320508075688772},"79":{"tf":1.0},"81":{"tf":1.0}},"df":6},"v":{"docs":{"189":{"tf":1.0}},"df":1}},"n":{"docs":{},"df":0,"d":{"docs":{"100":{"tf":1.0},"121":{"tf":1.0},"124":{"tf":1.0},"137":{"tf":1.0},"138":{"tf":1.0},"72":{"tf":1.0}},"df":6,"l":{"docs":{"127":{"tf":1.0},"133":{"tf":1.0},"139":{"tf":1.4142135623730951},"168":{"tf":1.0},"67":{"tf":1.0},"69":{"tf":1.0},"76":{"tf":1.0},"80":{"tf":1.0},"82":{"tf":1.0},"84":{"tf":1.0},"86":{"tf":1.0},"96":{"tf":1.0}},"df":12,"e":{"docs":{},"df":0,"r":{"docs":{"120":{"tf":1.0},"122":{"tf":1.4142135623730951},"123":{"tf":1.0},"124":{"tf":1.0},"126":{"tf":1.7320508075688772},"127":{"tf":1.0},"64":{"tf":1.4142135623730951},"72":{"tf":1.0},"76":{"tf":1.0},"78":{"tf":1.4142135623730951},"80":{"tf":4.242640687119285},"81":{"tf":1.0},"82":{"tf":1.7320508075688772},"83":{"tf":2.23606797749979},"84":{"tf":3.4641016151377544}},"df":15}}}}},"p":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"107":{"tf":1.0},"120":{"tf":1.4142135623730951},"87":{"tf":1.0}},"df":3}},"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"123":{"tf":1.0},"163":{"tf":1.0},"83":{"tf":1.0}},"df":3}}}}},"r":{"docs":{},"df":0,"d":{"docs":{"137":{"tf":1.0},"138":{"tf":1.4142135623730951},"68":{"tf":1.0},"8":{"tf":1.0},"83":{"tf":1.0}},"df":5,"e":{"docs":{},"df":0,"r":{"docs":{"136":{"tf":1.0}},"df":1}},"f":{"docs":{},"df":0,"a":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{"62":{"tf":1.0},"68":{"tf":1.0},"83":{"tf":2.0}},"df":3,"(":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{"83":{"tf":1.0}},"df":1}}},"_":{"docs":{"62":{"tf":1.7320508075688772}},"df":1},"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"62":{"tf":1.0}},"df":1}}}}}}}}}}}}}},"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"10":{"tf":1.0},"101":{"tf":1.4142135623730951},"102":{"tf":1.7320508075688772},"107":{"tf":1.7320508075688772},"108":{"tf":1.4142135623730951},"11":{"tf":1.0},"114":{"tf":2.23606797749979},"115":{"tf":1.4142135623730951},"117":{"tf":2.0},"119":{"tf":2.0},"124":{"tf":1.0},"130":{"tf":1.0},"14":{"tf":1.0},"141":{"tf":1.0},"164":{"tf":1.0},"166":{"tf":1.4142135623730951},"191":{"tf":1.0},"192":{"tf":1.0},"195":{"tf":1.0},"196":{"tf":1.0},"197":{"tf":1.0},"20":{"tf":1.0},"22":{"tf":1.0},"24":{"tf":1.4142135623730951},"26":{"tf":1.0},"27":{"tf":1.0},"30":{"tf":1.0},"32":{"tf":1.4142135623730951},"33":{"tf":1.0},"38":{"tf":1.7320508075688772},"53":{"tf":1.4142135623730951},"54":{"tf":2.0},"55":{"tf":1.0},"60":{"tf":1.0},"63":{"tf":1.4142135623730951},"65":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.7320508075688772},"70":{"tf":1.0},"77":{"tf":1.7320508075688772},"80":{"tf":1.4142135623730951},"81":{"tf":1.0},"86":{"tf":1.0},"88":{"tf":1.4142135623730951},"92":{"tf":1.4142135623730951},"94":{"tf":1.4142135623730951},"96":{"tf":1.0}},"df":48}}}}},"s":{"docs":{},"df":0,"h":{"docs":{"161":{"tf":1.0}},"df":1,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"132":{"tf":1.0},"17":{"tf":1.0}},"df":2}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"17":{"tf":1.0}},"df":1}}}}},"v":{"docs":{},"df":0,"e":{"docs":{"115":{"tf":1.0},"125":{"tf":1.0},"76":{"tf":1.0},"77":{"tf":1.0},"86":{"tf":1.0}},"df":5,"n":{"docs":{},"df":0,"’":{"docs":{},"df":0,"t":{"docs":{"61":{"tf":1.0}},"df":1}}}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"133":{"tf":2.23606797749979},"46":{"tf":1.0},"63":{"tf":1.0},"68":{"tf":1.0}},"df":4,"e":{"docs":{},"df":0,"r":{"docs":{"11":{"tf":1.0},"172":{"tf":1.0},"173":{"tf":1.0},"174":{"tf":1.0},"175":{"tf":1.0},"184":{"tf":2.0},"53":{"tf":1.4142135623730951},"62":{"tf":3.3166247903554},"68":{"tf":1.0}},"df":9}}},"p":{"docs":{"122":{"tf":1.0},"132":{"tf":1.4142135623730951},"133":{"tf":1.0},"134":{"tf":1.4142135623730951},"135":{"tf":1.0},"136":{"tf":1.0},"137":{"tf":1.0},"17":{"tf":1.0}},"df":8,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"132":{"tf":1.0},"134":{"tf":2.23606797749979},"136":{"tf":1.0},"139":{"tf":1.7320508075688772}},"df":4,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"134":{"tf":1.0}},"df":1}}}}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{"134":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{"136":{"tf":1.0},"138":{"tf":1.0}},"df":2}}}}},":":{"docs":{},"df":0,":":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{"134":{"tf":1.0}},"df":1}}}}}}}}}}}}}}},"r":{"docs":{},"df":0,"t":{"docs":{"125":{"tf":1.0}},"df":1}},"v":{"docs":{},"df":0,"i":{"docs":{"124":{"tf":1.0}},"df":1}}},"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{"63":{"tf":2.23606797749979},"64":{"tf":2.23606797749979},"67":{"tf":1.4142135623730951},"68":{"tf":1.7320508075688772},"76":{"tf":1.0},"77":{"tf":2.0}},"df":6,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"_":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"x":{"docs":{},"df":0,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"_":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"64":{"tf":1.0},"68":{"tf":1.0}},"df":2,"_":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"68":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"p":{"docs":{"120":{"tf":1.0},"191":{"tf":1.0},"5":{"tf":1.0},"53":{"tf":1.0},"68":{"tf":1.0},"92":{"tf":1.0},"98":{"tf":1.0}},"df":7,"f":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"67":{"tf":1.0}},"df":1}}}}}}},"n":{"docs":{},"df":0,"c":{"docs":{"63":{"tf":1.0}},"df":1}},"r":{"docs":{},"df":0,"e":{"docs":{"101":{"tf":1.0},"125":{"tf":1.4142135623730951},"126":{"tf":1.0},"160":{"tf":1.0},"173":{"tf":1.0},"176":{"tf":1.0},"183":{"tf":1.0},"196":{"tf":1.0},"198":{"tf":1.0},"200":{"tf":1.0},"201":{"tf":1.0},"42":{"tf":1.0},"49":{"tf":1.0},"5":{"tf":1.0},"50":{"tf":1.0},"6":{"tf":1.0},"60":{"tf":1.4142135623730951},"69":{"tf":1.0},"75":{"tf":1.0},"84":{"tf":1.0}},"df":20,"’":{"docs":{"188":{"tf":1.0},"79":{"tf":1.0},"81":{"tf":1.0},"83":{"tf":1.0}},"df":4}}},"x":{"docs":{"62":{"tf":1.0},"83":{"tf":1.0}},"df":2}},"i":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0}},"df":2,"g":{"docs":{},"df":0,"h":{"docs":{"106":{"tf":3.0},"107":{"tf":2.449489742783178},"108":{"tf":2.23606797749979},"109":{"tf":2.0},"114":{"tf":1.0},"122":{"tf":1.0},"127":{"tf":1.0},"189":{"tf":1.0},"193":{"tf":1.0},"26":{"tf":1.0},"35":{"tf":1.0}},"df":11,"e":{"docs":{},"df":0,"r":{"docs":{"1":{"tf":1.0},"123":{"tf":1.0},"160":{"tf":1.0},"165":{"tf":1.4142135623730951},"76":{"tf":1.7320508075688772},"84":{"tf":1.0},"93":{"tf":1.0}},"df":7}},"l":{"docs":{},"df":0,"i":{"docs":{"19":{"tf":1.0},"31":{"tf":1.0},"5":{"tf":1.0},"70":{"tf":1.0}},"df":4,"g":{"docs":{},"df":0,"h":{"docs":{},"df":0,"t":{"docs":{"156":{"tf":1.0},"53":{"tf":1.0},"56":{"tf":1.0}},"df":3}}}}},"z":{"docs":{"109":{"tf":2.23606797749979},"112":{"tf":1.4142135623730951}},"df":2}}},"o":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"h":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"83":{"tf":1.0}},"df":1,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"o":{"docs":{},"df":0,"k":{"docs":{"81":{"tf":1.0}},"df":1}}}}}}}}}}}},"{":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"81":{"tf":1.0}},"df":1}}}}}}}},"t":{"docs":{"68":{"tf":1.0}},"df":1}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"w":{"docs":{},"df":0,"d":{"docs":{"53":{"tf":1.0},"68":{"tf":1.0}},"df":2}}}}}},"o":{"docs":{},"df":0,"b":{"docs":{},"df":0,"b":{"docs":{},"df":0,"y":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"36":{"tf":1.0}},"df":1}}}}}},"l":{"docs":{},"df":0,"d":{"docs":{"126":{"tf":1.0},"134":{"tf":1.0},"190":{"tf":1.0},"95":{"tf":1.0}},"df":4}},"m":{"docs":{},"df":0,"e":{"docs":{"86":{"tf":1.0}},"df":1,"b":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{"45":{"tf":1.0}},"df":1}}}}}},"o":{"docs":{},"df":0,"d":{"docs":{"164":{"tf":1.0}},"df":1}},"s":{"docs":{},"df":0,"t":{"docs":{"177":{"tf":1.0},"178":{"tf":1.0},"22":{"tf":1.0},"24":{"tf":1.0},"27":{"tf":1.0},"32":{"tf":1.0},"63":{"tf":2.0},"77":{"tf":1.7320508075688772},"78":{"tf":1.0},"79":{"tf":1.0},"81":{"tf":1.0}},"df":11,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"81":{"tf":1.0}},"df":1}}}}}}}}},"p":{"docs":{},"df":0,"p":{"docs":{"172":{"tf":1.0}},"df":1},"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"l":{"docs":{},"df":0,"n":{"docs":{"191":{"tf":1.4142135623730951},"64":{"tf":1.0}},"df":2,"!":{"docs":{},"df":0,"(":{"docs":{},"df":0,"\\"":{"docs":{},"df":0,"f":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"191":{"tf":1.0}},"df":1}}}}},"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{"64":{"tf":1.4142135623730951},"67":{"tf":1.0},"68":{"tf":1.0},"77":{"tf":1.0}},"df":4}}}}},"t":{"docs":{},"df":0,"h":{"docs":{"191":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"83":{"tf":1.0}},"df":1}}}}}},"t":{"docs":{},"df":0,"t":{"docs":{},"df":0,"p":{"docs":{},"df":0,":":{"docs":{},"df":0,"/":{"docs":{},"df":0,"/":{"docs":{},"df":0,"o":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"d":{"docs":{},"df":0,".":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"g":{"docs":{},"df":0,"/":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"/":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"x":{"docs":{},"df":0,"y":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"/":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"g":{"docs":{},"df":0,"s":{"docs":{},"df":0,".":{"docs":{},"df":0,"h":{"docs":{},"df":0,"t":{"docs":{},"df":0,"m":{"docs":{},"df":0,"l":{"docs":{"53":{"tf":1.0},"68":{"tf":1.0}},"df":2}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"df":0,":":{"docs":{},"df":0,"/":{"docs":{},"df":0,"/":{"docs":{},"df":0,"g":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"u":{"docs":{},"df":0,"b":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"/":{"docs":{},"df":0,"k":{"docs":{},"df":0,"n":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"l":{"docs":{"57":{"tf":1.0}},"df":1}}}}},"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"58":{"tf":1.0},"59":{"tf":1.0},"62":{"tf":1.0},"67":{"tf":1.0}},"df":4}}}}}}}}}}}}}}},"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"p":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{"38":{"tf":1.0}},"df":1}}}}}}}},"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,".":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"f":{"docs":{},"df":0,"t":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"/":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"k":{"docs":{"39":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"u":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{"187":{"tf":1.0}},"df":1}},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{"86":{"tf":1.0}},"df":1}}},"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"r":{"docs":{"87":{"tf":1.0}},"df":1}}}},"w":{"docs":{"13":{"tf":1.0},"70":{"tf":1.0}},"df":2}},"i":{"docs":{},"df":0,".":{"docs":{"115":{"tf":1.4142135623730951},"187":{"tf":1.0},"2":{"tf":1.4142135623730951},"63":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0},"87":{"tf":1.0},"9":{"tf":1.0}},"df":8},"/":{"docs":{},"df":0,"o":{"docs":{"103":{"tf":1.0},"13":{"tf":1.0},"146":{"tf":1.0},"5":{"tf":1.0},"77":{"tf":1.0},"86":{"tf":1.4142135623730951}},"df":6}},"1":{"docs":{},"df":0,"6":{"docs":{"82":{"tf":1.0}},"df":1}},"2":{"docs":{},"df":0,"c":{"docs":{"11":{"tf":1.0},"115":{"tf":1.0},"117":{"tf":1.0},"4":{"tf":1.0},"5":{"tf":1.0}},"df":5}},"<":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"z":{"docs":{},"df":0,"e":{"docs":{},"df":0,"o":{"docs":{},"df":0,"f":{"docs":{},"df":0,"(":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,")":{"docs":{},"df":0,"/":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"z":{"docs":{},"df":0,"e":{"docs":{},"df":0,"o":{"docs":{},"df":0,"f":{"docs":{},"df":0,"(":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"[":{"docs":{},"df":0,"0":{"docs":{"163":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}},"c":{"docs":{"196":{"tf":1.0}},"df":1},"d":{"docs":{"31":{"tf":1.0},"34":{"tf":1.0},"36":{"tf":1.0},"44":{"tf":1.0}},"df":4,"e":{"docs":{},"df":0,"a":{"docs":{"190":{"tf":1.0},"84":{"tf":1.0}},"df":2},"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{"28":{"tf":1.0},"66":{"tf":1.0}},"df":2}}}}}},"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"4":{"tf":1.0}},"df":1,"a":{"docs":{},"df":0,"t":{"docs":{"178":{"tf":1.0}},"df":1}}}}}},"f":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{"157":{"tf":1.0},"158":{"tf":1.0}},"df":2}}}},"g":{"docs":{},"df":0,"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"122":{"tf":1.0},"64":{"tf":1.0},"66":{"tf":1.0},"87":{"tf":1.0}},"df":4}}}},"i":{"docs":{},"df":0,"r":{"docs":{"158":{"tf":1.4142135623730951}},"df":1}},"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"g":{"docs":{"110":{"tf":1.0},"83":{"tf":1.0}},"df":2}},"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{"107":{"tf":1.0}},"df":1}}}}}},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{"114":{"tf":1.0},"61":{"tf":1.0},"9":{"tf":1.0}},"df":3,"i":{"docs":{},"df":0,"n":{"docs":{"91":{"tf":1.0},"96":{"tf":1.0}},"df":2,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0}},"df":2}}}}}}},"m":{"docs":{"62":{"tf":1.0},"83":{"tf":1.0}},"df":2,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{"123":{"tf":1.0},"126":{"tf":1.0},"68":{"tf":1.4142135623730951}},"df":3}}},"u":{"docs":{},"df":0,"t":{"docs":{"102":{"tf":1.0},"96":{"tf":1.0}},"df":2}}},"p":{"docs":{},"df":0,"l":{"docs":{"101":{"tf":1.0},"104":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.0},"117":{"tf":1.0},"125":{"tf":1.4142135623730951},"133":{"tf":1.4142135623730951},"146":{"tf":1.0},"153":{"tf":1.0},"154":{"tf":1.4142135623730951},"155":{"tf":3.7416573867739413},"93":{"tf":1.0},"99":{"tf":1.0}},"df":14,"<":{"docs":{"155":{"tf":2.23606797749979}},"df":1,"e":{"docs":{},"df":0,"n":{"docs":{"109":{"tf":1.0}},"df":1}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{"109":{"tf":1.0}},"df":1}}}}}}},"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"107":{"tf":1.0},"113":{"tf":1.0},"115":{"tf":3.1622776601683795},"117":{"tf":1.7320508075688772},"118":{"tf":1.7320508075688772},"119":{"tf":1.0},"125":{"tf":1.0},"126":{"tf":1.7320508075688772},"128":{"tf":1.0},"13":{"tf":1.0},"132":{"tf":2.23606797749979},"133":{"tf":2.0},"142":{"tf":1.0},"148":{"tf":1.4142135623730951},"150":{"tf":1.0},"153":{"tf":1.0},"155":{"tf":1.0},"163":{"tf":1.0},"165":{"tf":1.0},"195":{"tf":1.4142135623730951},"69":{"tf":1.0},"7":{"tf":1.0},"71":{"tf":1.0},"76":{"tf":1.0},"84":{"tf":1.0}},"df":25}}}}},"i":{"docs":{"80":{"tf":1.0}},"df":1,"c":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"136":{"tf":1.0},"137":{"tf":1.0}},"df":2}}}}}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"101":{"tf":1.4142135623730951},"114":{"tf":1.0},"125":{"tf":1.0},"127":{"tf":1.0},"133":{"tf":1.0},"163":{"tf":1.0},"55":{"tf":1.0},"60":{"tf":1.0},"62":{"tf":1.0},"77":{"tf":1.0},"78":{"tf":1.0},"81":{"tf":1.0},"86":{"tf":1.0},"91":{"tf":1.0}},"df":14,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"92":{"tf":1.0}},"df":1}}}}}}},"s":{"docs":{},"df":0,"s":{"docs":{"110":{"tf":1.0},"136":{"tf":1.0},"76":{"tf":1.0}},"df":3}}},"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"v":{"docs":{"166":{"tf":1.0},"3":{"tf":1.0},"83":{"tf":1.0}},"df":3}}}}},"n":{"docs":{},"df":0,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{"109":{"tf":1.7320508075688772}},"df":1}}}},"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"p":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"p":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"160":{"tf":1.0}},"df":1}}}}}}}},"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"u":{"docs":{},"df":0,"d":{"docs":{"105":{"tf":1.0},"115":{"tf":1.0},"118":{"tf":1.0},"120":{"tf":1.0},"128":{"tf":1.0},"129":{"tf":1.4142135623730951},"130":{"tf":1.0},"131":{"tf":1.0},"137":{"tf":1.0},"138":{"tf":1.0},"158":{"tf":1.7320508075688772},"161":{"tf":1.0},"169":{"tf":1.0},"172":{"tf":1.0},"173":{"tf":1.4142135623730951},"174":{"tf":1.0},"175":{"tf":1.0},"184":{"tf":1.4142135623730951},"21":{"tf":1.0},"24":{"tf":1.0},"30":{"tf":1.0},"35":{"tf":1.0},"50":{"tf":1.0},"53":{"tf":1.0},"66":{"tf":1.4142135623730951},"68":{"tf":1.0},"69":{"tf":1.0},"72":{"tf":1.0},"77":{"tf":1.0},"78":{"tf":1.4142135623730951}},"df":30}}},"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"160":{"tf":1.0}},"df":1}}}}},"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"81":{"tf":1.0}},"df":1}}}}},"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"155":{"tf":1.0}},"df":1}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{"134":{"tf":1.0},"137":{"tf":1.4142135623730951},"151":{"tf":1.0},"187":{"tf":1.0},"188":{"tf":1.0},"190":{"tf":1.0}},"df":6}},"m":{"docs":{"122":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"122":{"tf":1.4142135623730951},"123":{"tf":1.0},"124":{"tf":1.4142135623730951}},"df":3,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"125":{"tf":1.0}},"df":1}}}}}}}}}}}},"u":{"docs":{},"df":0,"r":{"docs":{"110":{"tf":1.0}},"df":1}}},"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"114":{"tf":1.0},"120":{"tf":1.0},"94":{"tf":1.0}},"df":3}}}},"x":{"docs":{"163":{"tf":2.0},"62":{"tf":1.0},"78":{"tf":1.4142135623730951},"79":{"tf":1.7320508075688772}},"df":4}},"i":{"docs":{},"df":0,"c":{"docs":{"127":{"tf":1.0},"134":{"tf":1.4142135623730951},"16":{"tf":1.0},"164":{"tf":1.4142135623730951},"165":{"tf":1.7320508075688772},"44":{"tf":1.0},"60":{"tf":1.7320508075688772},"68":{"tf":1.0},"82":{"tf":1.7320508075688772},"9":{"tf":1.0}},"df":10},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"80":{"tf":1.0},"86":{"tf":1.0}},"df":2}}}}}},"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{},"df":0,"u":{"docs":{"165":{"tf":1.0}},"df":1}}}}}},"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"64":{"tf":1.0},"68":{"tf":1.0}},"df":2}}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"68":{"tf":1.0},"78":{"tf":1.0}},"df":2}}}},"o":{"docs":{"53":{"tf":3.0},"68":{"tf":4.0}},"df":2,"r":{"docs":{},"df":0,"m":{"docs":{"104":{"tf":1.0},"11":{"tf":1.0},"128":{"tf":1.0},"129":{"tf":1.0},"161":{"tf":1.4142135623730951},"187":{"tf":1.0},"198":{"tf":1.0},"20":{"tf":1.0},"200":{"tf":1.0},"201":{"tf":1.0},"30":{"tf":1.0},"5":{"tf":1.0},"62":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.4142135623730951},"69":{"tf":1.4142135623730951},"77":{"tf":1.0},"78":{"tf":1.0},"86":{"tf":1.0}},"df":20}}}},"g":{"docs":{"136":{"tf":1.0}},"df":1},"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"190":{"tf":1.0}},"df":1}}}}},"i":{"docs":{},"df":0,"t":{"docs":{"100":{"tf":1.0},"17":{"tf":1.0},"72":{"tf":1.0}},"df":3,"(":{"docs":{},"df":0,"c":{"docs":{},"df":0,"x":{"docs":{"100":{"tf":1.0}},"df":1}}},":":{"docs":{},"df":0,":":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"x":{"docs":{},"df":0,"t":{"docs":{"100":{"tf":1.0}},"df":1}}}}}}}}},"i":{"docs":{"103":{"tf":1.0},"104":{"tf":1.0},"117":{"tf":1.0},"118":{"tf":1.0},"119":{"tf":1.0},"151":{"tf":1.0},"55":{"tf":1.0},"61":{"tf":1.4142135623730951},"62":{"tf":1.4142135623730951},"81":{"tf":1.0},"84":{"tf":1.0}},"df":11,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{"119":{"tf":1.0},"127":{"tf":1.7320508075688772}},"df":2}}}}}}},"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"134":{"tf":1.0},"151":{"tf":2.0},"160":{"tf":1.7320508075688772},"189":{"tf":1.0},"190":{"tf":3.0}},"df":5,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"y":{"docs":{"160":{"tf":1.0}},"df":1}}}}}}}}}},"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"104":{"tf":1.4142135623730951},"190":{"tf":1.0}},"df":2}}},"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"103":{"tf":1.0},"106":{"tf":3.7416573867739413},"107":{"tf":3.0},"108":{"tf":3.1622776601683795},"109":{"tf":4.123105625617661},"11":{"tf":1.0},"110":{"tf":1.0},"111":{"tf":1.0},"112":{"tf":1.4142135623730951},"115":{"tf":1.0},"121":{"tf":1.4142135623730951},"122":{"tf":1.0},"127":{"tf":1.4142135623730951},"151":{"tf":1.0},"155":{"tf":2.23606797749979}},"df":15,"<":{"docs":{"155":{"tf":1.7320508075688772}},"df":1},"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0}},"df":2,"e":{"docs":{},"df":0,".":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{},"df":0,"_":{"docs":{},"df":0,"z":{"docs":{"109":{"tf":1.0}},"df":1}}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{"109":{"tf":1.0}},"df":1}}}}}}}}}}}}},"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"109":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"109":{"tf":1.0}},"df":1}}}}}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"_":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"n":{"docs":{"109":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"(":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"u":{"docs":{"109":{"tf":1.0}},"df":1}}}}}}}}}}}}}}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0}},"df":2}}}}}},"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0}},"df":2}}},"s":{"docs":{},"df":0,"t":{"docs":{"155":{"tf":3.0}},"df":1,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,">":{"docs":{},"df":0,"(":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"155":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"134":{"tf":1.0},"166":{"tf":1.4142135623730951},"61":{"tf":1.0}},"df":3}}},"i":{"docs":{},"df":0,"d":{"docs":{"109":{"tf":1.0},"123":{"tf":1.0},"125":{"tf":1.7320508075688772},"127":{"tf":1.0},"171":{"tf":1.0},"179":{"tf":1.0},"181":{"tf":1.0},"80":{"tf":1.4142135623730951},"84":{"tf":1.0}},"df":9}},"n":{"docs":{"62":{"tf":1.0},"83":{"tf":1.0}},"df":2},"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"21":{"tf":1.0},"24":{"tf":1.0},"28":{"tf":1.0},"31":{"tf":1.0},"32":{"tf":1.0},"62":{"tf":1.7320508075688772},"67":{"tf":1.0},"75":{"tf":1.0}},"df":8}}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"114":{"tf":1.0},"19":{"tf":1.0},"21":{"tf":1.4142135623730951},"37":{"tf":1.0},"38":{"tf":1.4142135623730951},"39":{"tf":1.4142135623730951},"40":{"tf":1.7320508075688772},"42":{"tf":1.0},"43":{"tf":1.7320508075688772},"45":{"tf":1.0},"46":{"tf":2.23606797749979},"47":{"tf":1.7320508075688772},"49":{"tf":1.4142135623730951},"50":{"tf":1.7320508075688772},"52":{"tf":1.7320508075688772},"53":{"tf":1.0},"57":{"tf":1.4142135623730951},"61":{"tf":1.4142135623730951},"64":{"tf":1.4142135623730951},"67":{"tf":1.4142135623730951},"68":{"tf":1.4142135623730951}},"df":21},"n":{"docs":{},"df":0,"c":{"docs":{"103":{"tf":1.0},"105":{"tf":1.4142135623730951},"118":{"tf":1.4142135623730951},"127":{"tf":1.4142135623730951},"166":{"tf":1.0},"188":{"tf":1.0},"67":{"tf":1.0},"93":{"tf":1.0},"96":{"tf":1.4142135623730951},"99":{"tf":1.0}},"df":10},"t":{"docs":{},"df":0,"i":{"docs":{"134":{"tf":1.0},"139":{"tf":1.0},"188":{"tf":1.0},"97":{"tf":1.0}},"df":4}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"100":{"tf":1.0},"108":{"tf":1.4142135623730951},"109":{"tf":1.0},"110":{"tf":1.0},"111":{"tf":1.0},"122":{"tf":1.0},"125":{"tf":1.0},"127":{"tf":1.4142135623730951},"131":{"tf":1.0},"133":{"tf":1.0},"136":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.0},"144":{"tf":1.0},"16":{"tf":1.0},"160":{"tf":1.0},"163":{"tf":1.0},"164":{"tf":1.0},"165":{"tf":1.0},"166":{"tf":1.0},"173":{"tf":1.0},"178":{"tf":1.0},"180":{"tf":1.0},"183":{"tf":1.0},"191":{"tf":1.0},"53":{"tf":1.0},"60":{"tf":1.0},"63":{"tf":1.0},"64":{"tf":1.4142135623730951},"68":{"tf":1.4142135623730951},"78":{"tf":1.0},"84":{"tf":1.0},"87":{"tf":1.4142135623730951},"88":{"tf":1.0},"92":{"tf":1.0},"99":{"tf":1.4142135623730951}},"df":36}}},"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"112":{"tf":1.0},"124":{"tf":2.23606797749979},"131":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.0},"173":{"tf":1.0},"174":{"tf":1.0},"19":{"tf":1.0},"37":{"tf":1.0},"38":{"tf":1.0},"41":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.0},"52":{"tf":1.0},"62":{"tf":1.7320508075688772},"64":{"tf":1.0},"69":{"tf":1.0},"78":{"tf":1.0},"8":{"tf":1.0},"80":{"tf":1.0},"83":{"tf":1.0}},"df":21}},"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"69":{"tf":1.0},"72":{"tf":1.0}},"df":2}}}}}}}},"t":{"docs":{"163":{"tf":1.0},"168":{"tf":1.7320508075688772},"173":{"tf":1.7320508075688772}},"df":3,"1":{"docs":{},"df":0,"6":{"docs":{},"df":0,"_":{"docs":{},"df":0,"t":{"docs":{"163":{"tf":1.0}},"df":1}}}},"e":{"docs":{},"df":0,"g":{"docs":{},"df":0,"r":{"docs":{"11":{"tf":1.0},"15":{"tf":1.0},"16":{"tf":1.0},"170":{"tf":1.0},"171":{"tf":1.0},"196":{"tf":1.0},"24":{"tf":1.0},"26":{"tf":1.4142135623730951},"27":{"tf":1.0},"31":{"tf":1.0},"34":{"tf":1.0},"80":{"tf":1.0}},"df":12}},"n":{"docs":{},"df":0,"d":{"docs":{"141":{"tf":1.0},"28":{"tf":1.0},"60":{"tf":1.0},"80":{"tf":1.0},"84":{"tf":1.0},"92":{"tf":1.0}},"df":6}},"r":{"docs":{"196":{"tf":1.0}},"df":1,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"114":{"tf":1.0},"118":{"tf":1.0},"13":{"tf":1.0},"142":{"tf":1.0},"161":{"tf":1.0},"164":{"tf":1.0},"173":{"tf":1.0},"174":{"tf":1.0},"24":{"tf":1.0},"26":{"tf":1.0},"28":{"tf":1.0},"31":{"tf":1.0},"69":{"tf":1.0},"86":{"tf":1.4142135623730951},"88":{"tf":1.4142135623730951},"95":{"tf":1.0},"98":{"tf":1.0},"99":{"tf":1.0}},"df":18}}},"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"130":{"tf":1.0}},"df":1}}},"f":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{"103":{"tf":1.7320508075688772},"107":{"tf":1.4142135623730951},"108":{"tf":1.4142135623730951},"109":{"tf":1.7320508075688772},"11":{"tf":1.0},"110":{"tf":1.0},"112":{"tf":1.0},"117":{"tf":1.0},"125":{"tf":1.0},"126":{"tf":1.0},"13":{"tf":1.0},"142":{"tf":1.4142135623730951},"153":{"tf":1.7320508075688772},"173":{"tf":1.4142135623730951},"174":{"tf":1.4142135623730951},"175":{"tf":1.4142135623730951},"178":{"tf":1.0},"193":{"tf":1.0},"195":{"tf":1.0},"198":{"tf":1.0},"26":{"tf":1.0},"27":{"tf":1.0},"30":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.4142135623730951},"36":{"tf":1.0},"4":{"tf":1.0},"5":{"tf":1.0},"60":{"tf":1.7320508075688772},"68":{"tf":1.7320508075688772},"86":{"tf":1.4142135623730951},"87":{"tf":1.0},"88":{"tf":1.0}},"df":33,"e":{"docs":{},"df":0,"/":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"k":{"docs":{"53":{"tf":2.0},"68":{"tf":1.4142135623730951}},"df":2,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"f":{"docs":{},"df":0,"g":{"docs":{"53":{"tf":1.4142135623730951},"68":{"tf":1.0}},"df":2}}}}}}}}}}}}}}},"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"125":{"tf":1.7320508075688772},"126":{"tf":1.4142135623730951},"127":{"tf":1.0}},"df":3}}},"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{"168":{"tf":1.0}},"df":1}}}},"n":{"docs":{"118":{"tf":1.0},"119":{"tf":1.0},"87":{"tf":1.0}},"df":3},"o":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"130":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":1.0},"166":{"tf":1.4142135623730951},"168":{"tf":1.0},"170":{"tf":1.0},"181":{"tf":1.0}},"df":7}}}},"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{"120":{"tf":1.7320508075688772},"121":{"tf":1.0},"122":{"tf":2.8284271247461903},"123":{"tf":3.605551275463989},"124":{"tf":2.23606797749979},"125":{"tf":2.0},"126":{"tf":2.0},"127":{"tf":3.0},"128":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.0},"133":{"tf":1.0},"165":{"tf":2.23606797749979},"4":{"tf":1.0},"5":{"tf":1.4142135623730951},"62":{"tf":1.0},"69":{"tf":1.0},"80":{"tf":1.7320508075688772},"82":{"tf":1.0},"84":{"tf":5.477225575051661}},"df":20,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"c":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"125":{"tf":1.4142135623730951}},"df":1,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{"125":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}},"f":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{"125":{"tf":1.0},"133":{"tf":1.0}},"df":2,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"|":{"docs":{},"df":0,"_":{"docs":{"133":{"tf":1.0}},"df":1},"c":{"docs":{"125":{"tf":1.4142135623730951},"126":{"tf":1.4142135623730951},"127":{"tf":2.8284271247461903}},"df":3}}}}}}}}},"s":{"docs":{},"df":0,"—":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"84":{"tf":1.0}},"df":1}}}}}}}}}}},"o":{"docs":{},"df":0,"_":{"docs":{},"df":0,"a":{"docs":{},"df":0,"f":{"docs":{},"df":0,"_":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{},"df":0,"_":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"<":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"g":{"docs":{},"df":0,"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"a":{"docs":{},"df":0,"f":{"docs":{},"df":0,"1":{"docs":{},"df":0,">":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"76":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"(":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"109":{"tf":1.0}},"df":1}}}}}}}}}}}}},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"(":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"109":{"tf":1.0}},"df":1}}}}}}}}}},"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"(":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"109":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}},"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{"104":{"tf":1.0},"105":{"tf":1.4142135623730951}},"df":2,"(":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"104":{"tf":1.0}},"df":1}}}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"155":{"tf":1.0}},"df":1,"<":{"docs":{},"df":0,"n":{"docs":{"155":{"tf":1.4142135623730951}},"df":1}},"_":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{},"df":0,"_":{"docs":{},"df":0,"z":{"docs":{},"df":0,"(":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"109":{"tf":1.0},"112":{"tf":1.0}},"df":2}}}}}}}}}}},"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"_":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"n":{"docs":{},"df":0,"(":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"109":{"tf":1.0}},"df":1}}}}}}}}},"u":{"docs":{},"df":0,"p":{"docs":{},"df":0,"(":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"109":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}},"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"155":{"tf":1.0}},"df":1,"<":{"docs":{},"df":0,"n":{"docs":{"155":{"tf":1.4142135623730951}},"df":1}}}}}}}}}},"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"u":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"70":{"tf":1.0}},"df":1,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"0":{"tf":1.0}},"df":1}}}}}}}}}},"v":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0},"80":{"tf":1.0},"83":{"tf":1.4142135623730951}},"df":4}}},"r":{"docs":{},"df":0,"i":{"docs":{"190":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"c":{"docs":{"136":{"tf":1.0},"80":{"tf":1.0}},"df":2},"k":{"docs":{"15":{"tf":1.0},"63":{"tf":1.0},"68":{"tf":1.0},"77":{"tf":1.0},"83":{"tf":1.0}},"df":5},"l":{"docs":{},"df":0,"v":{"docs":{"16":{"tf":1.0},"168":{"tf":1.0},"19":{"tf":1.0},"64":{"tf":1.4142135623730951},"80":{"tf":1.4142135623730951}},"df":5}}}}},"o":{"docs":{},"df":0,"/":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"5":{"tf":1.0}},"df":1}}}}}}}}}},"r":{"docs":{},"df":0,"q":{"docs":{},"df":0,"n":{"docs":{"82":{"tf":1.0}},"df":1}}},"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0}},"df":2}},"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0}},"df":2}}}},"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0}},"df":2}}}}}}},"n":{"docs":{},"df":0,"\'":{"docs":{},"df":0,"t":{"docs":{"109":{"tf":1.0}},"df":1}},"’":{"docs":{},"df":0,"t":{"docs":{"102":{"tf":1.0},"72":{"tf":1.0}},"df":2}}},"r":{"docs":{"165":{"tf":1.4142135623730951}},"df":1},"s":{"docs":{},"df":0,"u":{"docs":{"169":{"tf":1.4142135623730951},"170":{"tf":1.4142135623730951},"32":{"tf":1.0},"53":{"tf":1.0},"68":{"tf":1.0},"8":{"tf":1.0},"80":{"tf":1.4142135623730951}},"df":7}}},"t":{"docs":{},"df":0,"\'":{"docs":{"80":{"tf":1.0},"84":{"tf":1.0}},"df":2},"e":{"docs":{},"df":0,"m":{"docs":{"100":{"tf":1.0},"160":{"tf":1.7320508075688772}},"df":2},"r":{"docs":{"163":{"tf":2.23606797749979},"189":{"tf":1.0}},"df":2}},"m":{"docs":{"78":{"tf":1.4142135623730951}},"df":1},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"138":{"tf":1.0},"78":{"tf":1.0},"80":{"tf":1.0},"91":{"tf":1.0}},"df":4}}}},"’":{"docs":{"120":{"tf":1.0},"122":{"tf":1.0},"123":{"tf":1.0},"126":{"tf":1.0},"130":{"tf":1.0},"133":{"tf":1.0},"134":{"tf":1.0},"137":{"tf":1.0},"158":{"tf":1.4142135623730951},"186":{"tf":1.0},"188":{"tf":1.0},"64":{"tf":1.0},"68":{"tf":1.0},"77":{"tf":1.4142135623730951},"80":{"tf":1.4142135623730951},"82":{"tf":1.0},"86":{"tf":1.0},"92":{"tf":1.0}},"df":18}},"²":{"docs":{},"df":0,"c":{"docs":{"196":{"tf":1.0}},"df":1}}},"j":{"docs":{"24":{"tf":1.0},"35":{"tf":1.4142135623730951}},"df":2,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{"6":{"tf":1.0}},"df":1}}}}}},"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"123":{"tf":1.0}},"df":1}}}}},"o":{"docs":{},"df":0,"b":{"docs":{"158":{"tf":1.0},"160":{"tf":1.0}},"df":2},"h":{"docs":{},"df":0,"n":{"docs":{"58":{"tf":1.0}},"df":1}},"i":{"docs":{},"df":0,"n":{"docs":{"86":{"tf":1.0}},"df":1}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{"27":{"tf":1.0},"30":{"tf":1.0},"32":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.4142135623730951},"53":{"tf":1.0},"68":{"tf":1.0}},"df":7,"/":{"docs":{},"df":0,"s":{"docs":{},"df":0,"w":{"docs":{},"df":0,"d":{"docs":{"53":{"tf":1.0},"68":{"tf":1.0}},"df":2}}}}}}}},"k":{"docs":{"61":{"tf":1.0},"67":{"tf":1.0}},"df":2,"b":{"docs":{"12":{"tf":1.0}},"df":1,"/":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{"68":{"tf":1.4142135623730951}},"df":1}}}}},"e":{"docs":{},"df":0,"e":{"docs":{},"df":0,"p":{"docs":{"115":{"tf":1.0},"121":{"tf":1.0},"133":{"tf":1.0},"135":{"tf":1.0},"188":{"tf":1.0},"62":{"tf":1.0},"81":{"tf":1.0},"84":{"tf":1.4142135623730951}},"df":8}},"r":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"16":{"tf":1.0},"17":{"tf":1.0},"63":{"tf":2.0},"64":{"tf":1.0},"68":{"tf":1.0}},"df":5}}}},"y":{"docs":{"164":{"tf":1.0},"24":{"tf":1.0},"56":{"tf":1.0}},"df":3,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{"86":{"tf":1.0}},"df":1}}}}},"w":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{"78":{"tf":1.0}},"df":1}}}}}},"h":{"docs":{},"df":0,"z":{"docs":{"53":{"tf":2.449489742783178},"68":{"tf":2.449489742783178}},"df":2}},"i":{"docs":{},"df":0,"b":{"docs":{"11":{"tf":1.7320508075688772},"187":{"tf":1.0},"188":{"tf":1.0},"66":{"tf":2.0},"72":{"tf":1.0},"87":{"tf":1.4142135623730951}},"df":6,"i":{"docs":{"61":{"tf":1.0},"67":{"tf":1.0}},"df":2}},"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"88":{"tf":1.0}},"df":1}},"y":{"docs":{},"df":0,"t":{"docs":{"87":{"tf":1.0}},"df":1}}}}},"n":{"docs":{},"df":0,"d":{"docs":{"12":{"tf":1.0},"13":{"tf":1.0},"16":{"tf":1.0},"93":{"tf":1.0}},"df":4}},"t":{"docs":{"69":{"tf":1.0}},"df":1}},"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"122":{"tf":1.0},"125":{"tf":1.4142135623730951},"127":{"tf":1.0},"175":{"tf":1.0},"28":{"tf":1.0},"61":{"tf":1.0},"80":{"tf":1.0},"9":{"tf":1.0},"92":{"tf":1.0}},"df":9,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"g":{"docs":{"3":{"tf":1.4142135623730951}},"df":1}}}},"n":{"docs":{"151":{"tf":1.0},"153":{"tf":1.0},"35":{"tf":1.0},"64":{"tf":1.0},"86":{"tf":1.4142135623730951}},"df":5}}}}},"l":{"docs":{"44":{"tf":1.0}},"df":1,"3":{"docs":{},"df":0,"g":{"docs":{},"df":0,"d":{"docs":{},"df":0,"2":{"docs":{},"df":0,"0":{"docs":{"11":{"tf":1.0}},"df":1}}}}},"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"11":{"tf":1.0},"53":{"tf":1.0}},"df":2}}},"c":{"docs":{},"df":0,"k":{"docs":{"16":{"tf":1.0},"17":{"tf":1.0},"84":{"tf":1.0}},"df":3}},"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{"0":{"tf":1.0},"1":{"tf":1.0},"16":{"tf":1.0},"160":{"tf":1.0},"168":{"tf":1.0},"175":{"tf":1.0},"181":{"tf":1.0},"2":{"tf":1.0},"4":{"tf":1.4142135623730951},"78":{"tf":1.0},"88":{"tf":1.4142135623730951}},"df":11}}}}},"p":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"p":{"docs":{"44":{"tf":1.0},"53":{"tf":1.0},"68":{"tf":1.0}},"df":3,"’":{"docs":{"19":{"tf":1.0}},"df":1}}}}},"r":{"docs":{},"df":0,"g":{"docs":{"158":{"tf":1.0},"188":{"tf":1.0},"27":{"tf":1.0},"65":{"tf":1.0},"84":{"tf":1.0}},"df":5}},"s":{"docs":{},"df":0,"t":{"docs":{"108":{"tf":1.0},"126":{"tf":1.0},"53":{"tf":1.0},"77":{"tf":1.0},"96":{"tf":1.0}},"df":5,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"122":{"tf":1.7320508075688772},"123":{"tf":1.7320508075688772},"124":{"tf":1.7320508075688772},"125":{"tf":1.7320508075688772},"126":{"tf":1.7320508075688772},"127":{"tf":1.7320508075688772}},"df":6}}}}}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{"123":{"tf":1.0}},"df":1}},"r":{"docs":{"120":{"tf":1.0},"127":{"tf":1.4142135623730951},"40":{"tf":1.0},"53":{"tf":1.0},"7":{"tf":1.4142135623730951}},"df":5},"s":{"docs":{},"df":0,"t":{"docs":{"46":{"tf":1.0},"59":{"tf":1.0}},"df":2}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"168":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"63":{"tf":1.0},"64":{"tf":1.7320508075688772}},"df":2}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"30":{"tf":1.0}},"df":1}}}}}}}},"y":{"docs":{"91":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"r":{"docs":{"114":{"tf":1.7320508075688772},"115":{"tf":1.0},"126":{"tf":1.0},"131":{"tf":1.0},"141":{"tf":1.0},"195":{"tf":1.0},"197":{"tf":1.0},"27":{"tf":1.0},"76":{"tf":1.4142135623730951},"93":{"tf":1.0}},"df":10}},"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"133":{"tf":2.23606797749979},"166":{"tf":2.449489742783178},"20":{"tf":1.0},"61":{"tf":1.4142135623730951}},"df":4,".":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"n":{"docs":{"133":{"tf":1.0}},"df":1}}}}},"s":{"docs":{"133":{"tf":1.0}},"df":1}}}}}},"z":{"docs":{},"df":0,"i":{"docs":{"81":{"tf":1.0}},"df":1}}},"c":{"docs":{},"df":0,"d":{"docs":{"118":{"tf":1.0}},"df":1}},"d":{"docs":{},"df":0,"r":{"docs":{"83":{"tf":1.4142135623730951}},"df":1}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"160":{"tf":1.0}},"df":1},"k":{"docs":{"136":{"tf":1.4142135623730951}},"df":1},"v":{"docs":{"75":{"tf":1.0},"87":{"tf":1.0}},"df":2}},"d":{"docs":{"11":{"tf":1.0},"118":{"tf":1.0},"69":{"tf":1.0},"70":{"tf":1.4142135623730951},"72":{"tf":1.0}},"df":5},"f":{"docs":{},"df":0,"t":{"docs":{"123":{"tf":1.0},"77":{"tf":1.4142135623730951},"78":{"tf":1.0}},"df":3}},"g":{"docs":{},"df":0,"i":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"142":{"tf":1.0}},"df":1}}}},"n":{"docs":{"79":{"tf":1.0}},"df":1,"g":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{"125":{"tf":1.0},"61":{"tf":1.4142135623730951},"67":{"tf":1.4142135623730951},"68":{"tf":1.0},"79":{"tf":1.0}},"df":5,"(":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"61":{"tf":1.0}},"df":1}}}}}}}},"s":{"docs":{},"df":0,"s":{"docs":{"108":{"tf":1.0},"115":{"tf":1.0},"130":{"tf":1.0}},"df":3}},"t":{"docs":{"127":{"tf":1.7320508075688772},"188":{"tf":1.0},"44":{"tf":1.0},"63":{"tf":1.4142135623730951},"77":{"tf":1.0}},"df":5,"’":{"docs":{"10":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.0},"127":{"tf":1.0},"173":{"tf":1.0},"60":{"tf":1.0},"63":{"tf":2.0},"64":{"tf":2.0},"65":{"tf":1.0},"68":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0},"76":{"tf":1.0},"90":{"tf":1.4142135623730951}},"df":15}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"1":{"tf":1.0},"117":{"tf":1.0},"122":{"tf":1.0},"158":{"tf":1.0},"16":{"tf":1.0},"160":{"tf":1.0},"165":{"tf":1.4142135623730951},"186":{"tf":1.0},"187":{"tf":1.0},"188":{"tf":2.23606797749979},"189":{"tf":2.8284271247461903},"190":{"tf":3.1622776601683795},"193":{"tf":1.0},"197":{"tf":1.0},"26":{"tf":1.0},"53":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.0},"71":{"tf":1.0},"76":{"tf":1.0}},"df":20},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{"99":{"tf":1.0}},"df":1}}}}}},"i":{"docs":{},"df":0,"b":{"docs":{"180":{"tf":1.7320508075688772}},"df":1,".":{"docs":{},"df":0,"r":{"docs":{"158":{"tf":1.0}},"df":1}},"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"14":{"tf":1.4142135623730951},"16":{"tf":1.4142135623730951},"162":{"tf":1.4142135623730951},"17":{"tf":1.0}},"df":4}}},"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,".":{"docs":{},"df":0,"a":{"docs":{"178":{"tf":1.0}},"df":1}}}}},"m":{"docs":{"191":{"tf":1.0}},"df":1,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"{":{"docs":{},"df":0,"e":{"docs":{},"df":0,"x":{"docs":{},"df":0,"p":{"docs":{"191":{"tf":1.0}},"df":1}}}}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"129":{"tf":2.0},"13":{"tf":1.0},"14":{"tf":1.7320508075688772},"142":{"tf":1.0},"158":{"tf":1.0},"161":{"tf":1.0},"165":{"tf":1.0},"173":{"tf":1.7320508075688772},"174":{"tf":1.0},"175":{"tf":1.4142135623730951},"177":{"tf":1.4142135623730951},"178":{"tf":1.4142135623730951},"180":{"tf":1.4142135623730951},"184":{"tf":1.0},"191":{"tf":1.7320508075688772},"62":{"tf":1.0},"78":{"tf":1.4142135623730951}},"df":17},"y":{"docs":{},"df":0,".":{"docs":{},"df":0,"a":{"docs":{"175":{"tf":1.0}},"df":1}}}}}},"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"l":{"docs":{"40":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"d":{"docs":{"15":{"tf":1.4142135623730951},"17":{"tf":1.0}},"df":2}}}},"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{"9":{"tf":1.4142135623730951}},"df":1},"s":{"docs":{"53":{"tf":1.0},"68":{"tf":1.0},"9":{"tf":2.449489742783178}},"df":3}}}},"g":{"docs":{},"df":0,"h":{"docs":{},"df":0,"t":{"docs":{"118":{"tf":1.0}},"df":1}}},"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"115":{"tf":1.0},"159":{"tf":1.0},"178":{"tf":1.0}},"df":3}}},"n":{"docs":{},"df":0,"e":{"docs":{"127":{"tf":1.0},"15":{"tf":1.0},"28":{"tf":1.4142135623730951},"53":{"tf":1.4142135623730951},"62":{"tf":1.0},"64":{"tf":1.4142135623730951},"68":{"tf":1.4142135623730951},"72":{"tf":1.0},"78":{"tf":1.0}},"df":9,"a":{"docs":{},"df":0,"r":{"docs":{"191":{"tf":1.0},"87":{"tf":1.0}},"df":2}}},"k":{"docs":{"11":{"tf":1.0},"16":{"tf":1.0},"161":{"tf":1.0},"172":{"tf":1.0},"173":{"tf":1.0},"174":{"tf":1.0},"175":{"tf":1.0},"184":{"tf":1.0},"20":{"tf":1.0},"24":{"tf":1.7320508075688772},"34":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"36":{"tf":1.4142135623730951},"53":{"tf":1.7320508075688772},"60":{"tf":1.4142135623730951},"61":{"tf":1.0},"68":{"tf":1.4142135623730951},"77":{"tf":1.0},"78":{"tf":1.7320508075688772},"9":{"tf":1.4142135623730951}},"df":20,".":{"docs":{},"df":0,"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{"44":{"tf":1.0}},"df":1}}}},"/":{"docs":{},"df":0,"v":{"docs":{},"df":0,"2":{"docs":{"44":{"tf":1.4142135623730951}},"df":1,".":{"docs":{},"df":0,"1":{"docs":{"44":{"tf":1.0}},"df":1}}}}},"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"61":{"tf":1.0}},"df":1}}}}},"e":{"docs":{},"df":0,"r":{"docs":{"137":{"tf":1.0},"161":{"tf":1.0},"182":{"tf":1.0},"20":{"tf":1.4142135623730951},"62":{"tf":1.4142135623730951},"67":{"tf":1.0}},"df":6}}},"u":{"docs":{},"df":0,"x":{"docs":{"117":{"tf":1.0},"40":{"tf":1.0},"41":{"tf":1.0},"42":{"tf":1.0},"43":{"tf":1.0}},"df":5}}},"s":{"docs":{},"df":0,"t":{"docs":{"107":{"tf":1.0},"11":{"tf":1.0},"158":{"tf":1.4142135623730951},"160":{"tf":1.0},"19":{"tf":1.4142135623730951},"192":{"tf":1.0},"6":{"tf":1.0},"64":{"tf":1.4142135623730951},"84":{"tf":1.0}},"df":9}},"t":{"docs":{},"df":0,"t":{"docs":{},"df":0,"l":{"docs":{"104":{"tf":1.0},"127":{"tf":1.0},"158":{"tf":1.0},"167":{"tf":1.4142135623730951},"62":{"tf":1.4142135623730951},"88":{"tf":1.0}},"df":6}}},"v":{"docs":{},"df":0,"e":{"docs":{"137":{"tf":1.0},"87":{"tf":1.0}},"df":2}}},"l":{"docs":{},"df":0,"d":{"docs":{},"df":0,"b":{"docs":{"64":{"tf":1.4142135623730951}},"df":1}},"v":{"docs":{},"df":0,"m":{"docs":{"189":{"tf":1.0},"190":{"tf":1.4142135623730951},"21":{"tf":2.23606797749979},"39":{"tf":1.0},"62":{"tf":1.0}},"df":5,"’":{"docs":{"190":{"tf":1.0}},"df":1}}}},"m":{"docs":{},"df":0,"3":{"docs":{},"df":0,"s":{"docs":{},"df":0,"6":{"docs":{},"df":0,"9":{"docs":{},"df":0,"6":{"docs":{},"df":0,"5":{"docs":{"100":{"tf":1.0},"55":{"tf":1.0},"61":{"tf":1.4142135623730951},"63":{"tf":1.0},"81":{"tf":1.0}},"df":5,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{"84":{"tf":1.0}},"df":1}}}}}}}}},"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"100":{"tf":1.0}},"df":1}}}}}}}}}},"e":{"docs":{},"df":0,"v":{"docs":{},"df":0,"b":{"docs":{"63":{"tf":2.449489742783178},"64":{"tf":1.0},"68":{"tf":1.0},"79":{"tf":1.0},"81":{"tf":1.0},"83":{"tf":1.0}},"df":6}}}}}}}}},"a":{"docs":{"68":{"tf":2.449489742783178}},"df":1}},"o":{"docs":{"59":{"tf":1.0}},"df":1,"a":{"docs":{},"df":0,"d":{"docs":{"122":{"tf":1.4142135623730951},"124":{"tf":1.0},"137":{"tf":1.0},"14":{"tf":1.7320508075688772},"62":{"tf":1.0},"63":{"tf":1.0},"64":{"tf":1.0},"68":{"tf":3.7416573867739413},"72":{"tf":1.0},"83":{"tf":1.4142135623730951}},"df":10}},"c":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"112":{"tf":1.0},"136":{"tf":1.4142135623730951},"177":{"tf":1.0},"61":{"tf":1.7320508075688772},"63":{"tf":1.0},"66":{"tf":1.7320508075688772},"78":{"tf":1.0},"83":{"tf":1.4142135623730951},"87":{"tf":1.4142135623730951}},"df":9}},"k":{"docs":{"125":{"tf":1.0},"126":{"tf":3.0},"80":{"tf":1.0}},"df":3,"/":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"126":{"tf":1.0}},"df":1}}}}}}}}},"g":{"docs":{"77":{"tf":1.7320508075688772},"78":{"tf":1.4142135623730951}},"df":2,"i":{"docs":{},"df":0,"c":{"docs":{"151":{"tf":1.0},"161":{"tf":1.0},"87":{"tf":1.0}},"df":3}}},"n":{"docs":{},"df":0,"g":{"docs":{"113":{"tf":1.0},"126":{"tf":1.4142135623730951},"127":{"tf":1.0},"137":{"tf":1.0},"168":{"tf":1.4142135623730951},"63":{"tf":1.0},"87":{"tf":1.0}},"df":7,"e":{"docs":{},"df":0,"r":{"docs":{"110":{"tf":1.0},"123":{"tf":1.0},"125":{"tf":1.4142135623730951},"127":{"tf":1.4142135623730951},"131":{"tf":1.0},"99":{"tf":1.0}},"df":6}}}},"o":{"docs":{},"df":0,"k":{"docs":{"108":{"tf":1.4142135623730951},"109":{"tf":1.0},"11":{"tf":1.0},"115":{"tf":1.0},"142":{"tf":1.0},"156":{"tf":1.0},"160":{"tf":1.0},"173":{"tf":2.0},"178":{"tf":1.0},"183":{"tf":1.0},"191":{"tf":1.4142135623730951},"60":{"tf":1.0},"64":{"tf":1.0},"68":{"tf":1.4142135623730951},"70":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0},"80":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0},"86":{"tf":1.0},"87":{"tf":1.0},"88":{"tf":1.0},"90":{"tf":1.7320508075688772}},"df":24,"u":{"docs":{},"df":0,"p":{"docs":{"83":{"tf":1.0}},"df":1}}},"p":{"docs":{"121":{"tf":1.7320508075688772},"122":{"tf":1.4142135623730951},"123":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":1.0},"126":{"tf":1.0},"127":{"tf":1.4142135623730951},"133":{"tf":1.4142135623730951},"134":{"tf":1.0},"165":{"tf":1.0},"189":{"tf":2.0},"191":{"tf":1.0},"60":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.4142135623730951},"71":{"tf":1.7320508075688772},"72":{"tf":1.0},"76":{"tf":1.0},"77":{"tf":1.7320508075688772},"78":{"tf":1.0},"79":{"tf":1.0},"81":{"tf":1.0},"82":{"tf":1.0},"83":{"tf":1.4142135623730951}},"df":24}},"s":{"docs":{},"df":0,"s":{"docs":{"188":{"tf":1.0}},"df":1},"t":{"docs":{"74":{"tf":1.0}},"df":1}},"t":{"docs":{"108":{"tf":1.0},"123":{"tf":1.0},"127":{"tf":1.0},"138":{"tf":1.0},"190":{"tf":1.0},"70":{"tf":1.0},"72":{"tf":1.0},"84":{"tf":1.0}},"df":8},"w":{"docs":{"103":{"tf":1.0},"106":{"tf":2.449489742783178},"107":{"tf":2.23606797749979},"108":{"tf":2.0},"109":{"tf":1.4142135623730951},"117":{"tf":1.0},"122":{"tf":1.0},"128":{"tf":1.0},"190":{"tf":1.0},"53":{"tf":1.0},"68":{"tf":1.0},"71":{"tf":1.0}},"df":12,"e":{"docs":{},"df":0,"r":{"docs":{"137":{"tf":1.0},"197":{"tf":1.0},"87":{"tf":1.0}},"df":3},"s":{"docs":{},"df":0,"t":{"docs":{"122":{"tf":1.0}},"df":1}}}}},"r":{"docs":{"62":{"tf":1.4142135623730951},"83":{"tf":1.0}},"df":2},"s":{"docs":{"44":{"tf":1.0}},"df":1,"m":{"docs":{},"df":0,"3":{"docs":{},"df":0,"0":{"docs":{},"df":0,"3":{"docs":{},"df":0,"d":{"docs":{},"df":0,"l":{"docs":{},"df":0,"h":{"docs":{},"df":0,"c":{"docs":{"11":{"tf":1.4142135623730951}},"df":1}}}}}}}},"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"b":{"docs":{"44":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"120":{"tf":1.0},"61":{"tf":1.0},"96":{"tf":1.0}},"df":3}}}}},"x":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"122":{"tf":1.0}},"df":1}}}}}},"m":{"docs":{"100":{"tf":1.7320508075688772},"115":{"tf":1.0},"124":{"tf":1.0},"17":{"tf":1.0},"188":{"tf":1.7320508075688772},"19":{"tf":1.0},"190":{"tf":1.0},"2":{"tf":1.0},"38":{"tf":3.0},"43":{"tf":1.7320508075688772},"56":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":2.0},"60":{"tf":1.0},"64":{"tf":1.7320508075688772},"67":{"tf":1.7320508075688772},"69":{"tf":1.7320508075688772},"7":{"tf":1.0},"71":{"tf":1.4142135623730951},"72":{"tf":1.0},"77":{"tf":1.4142135623730951},"78":{"tf":1.0},"80":{"tf":1.0},"81":{"tf":2.0},"82":{"tf":1.4142135623730951},"84":{"tf":1.4142135623730951},"87":{"tf":1.0},"90":{"tf":1.4142135623730951}},"df":28,"*":{"docs":{},"df":0,"n":{"docs":{"115":{"tf":1.0}},"df":1}},"+":{"docs":{},"df":0,"n":{"docs":{"115":{"tf":1.0}},"df":1}},"0":{"docs":{"124":{"tf":1.4142135623730951},"38":{"tf":1.4142135623730951},"61":{"tf":1.4142135623730951},"67":{"tf":1.4142135623730951}},"df":4},"1":{"docs":{"38":{"tf":1.0}},"df":1},"2":{"docs":{},"df":0,"3":{"docs":{"38":{"tf":1.0}},"df":1}},"3":{"docs":{"124":{"tf":1.0},"38":{"tf":1.0},"55":{"tf":1.0},"61":{"tf":1.7320508075688772},"63":{"tf":2.23606797749979},"64":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.0},"79":{"tf":1.0},"81":{"tf":1.0}},"df":11,"3":{"docs":{"38":{"tf":1.0}},"df":1,"f":{"docs":{"38":{"tf":1.0}},"df":1}},"5":{"docs":{},"df":0,"p":{"docs":{"38":{"tf":1.0}},"df":1,"f":{"docs":{"38":{"tf":1.0}},"df":1}}}},"4":{"docs":{"38":{"tf":1.0},"61":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.0},"72":{"tf":1.0}},"df":5,"f":{"docs":{"11":{"tf":1.0},"38":{"tf":1.0},"61":{"tf":1.0},"63":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.4142135623730951}},"df":6}},"7":{"docs":{"38":{"tf":1.0},"61":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.0}},"df":4,"f":{"docs":{"38":{"tf":1.0},"61":{"tf":1.0},"67":{"tf":1.0}},"df":3}},"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"105":{"tf":1.0},"106":{"tf":1.0},"107":{"tf":1.0},"112":{"tf":1.0},"176":{"tf":1.0},"177":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":2.6457513110645907},"64":{"tf":1.7320508075688772},"68":{"tf":1.0},"72":{"tf":1.0},"79":{"tf":1.0},"81":{"tf":1.0},"83":{"tf":1.0}},"df":14}}},"o":{"docs":{"41":{"tf":1.0}},"df":1},"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"45":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"o":{"docs":{"100":{"tf":1.0},"113":{"tf":1.0},"157":{"tf":1.0},"160":{"tf":3.605551275463989}},"df":4}}},"d":{"docs":{},"df":0,"e":{"docs":{"119":{"tf":1.0},"67":{"tf":1.0},"88":{"tf":1.0}},"df":3}},"g":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"105":{"tf":1.0},"62":{"tf":1.0},"67":{"tf":1.0}},"df":3}},"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"11":{"tf":1.0},"118":{"tf":1.0}},"df":2}}}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{"100":{"tf":1.4142135623730951},"101":{"tf":1.0},"104":{"tf":1.0},"115":{"tf":1.0},"116":{"tf":1.0},"121":{"tf":1.4142135623730951},"122":{"tf":1.7320508075688772},"123":{"tf":1.7320508075688772},"124":{"tf":1.4142135623730951},"125":{"tf":1.4142135623730951},"126":{"tf":1.7320508075688772},"127":{"tf":2.0},"133":{"tf":1.0},"134":{"tf":1.0},"146":{"tf":1.0},"15":{"tf":1.4142135623730951},"153":{"tf":1.0},"154":{"tf":1.0},"155":{"tf":1.0},"158":{"tf":1.0},"159":{"tf":1.0},"166":{"tf":2.0},"17":{"tf":1.0},"178":{"tf":1.0},"191":{"tf":1.4142135623730951},"60":{"tf":2.449489742783178},"62":{"tf":1.0},"64":{"tf":2.23606797749979},"67":{"tf":1.0},"68":{"tf":1.7320508075688772},"71":{"tf":1.0},"76":{"tf":1.0},"77":{"tf":1.7320508075688772},"79":{"tf":1.0},"80":{"tf":1.0},"81":{"tf":1.0},"83":{"tf":1.0},"86":{"tf":1.0},"98":{"tf":1.0},"99":{"tf":1.0}},"df":40,"+":{"docs":{},"df":0,"0":{"docs":{},"df":0,"x":{"docs":{},"df":0,"4":{"docs":{"62":{"tf":1.0}},"df":1}}}},"l":{"docs":{},"df":0,"i":{"docs":{"77":{"tf":1.0},"8":{"tf":1.0}},"df":2}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"131":{"tf":1.0},"80":{"tf":1.0}},"df":2}}},"e":{"docs":{},"df":0,"n":{"docs":{"28":{"tf":1.0}},"df":1}}}}},"j":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"171":{"tf":1.0},"34":{"tf":1.0},"7":{"tf":1.0},"87":{"tf":1.0}},"df":4}}},"k":{"docs":{},"df":0,"e":{"docs":{"101":{"tf":1.0},"102":{"tf":1.0},"103":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.0},"115":{"tf":1.0},"125":{"tf":1.0},"127":{"tf":1.4142135623730951},"133":{"tf":1.0},"138":{"tf":1.0},"147":{"tf":1.0},"16":{"tf":1.0},"169":{"tf":1.0},"174":{"tf":1.0},"175":{"tf":1.0},"176":{"tf":1.0},"177":{"tf":1.0},"19":{"tf":1.0},"190":{"tf":1.0},"2":{"tf":1.0},"21":{"tf":1.0},"30":{"tf":1.0},"34":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.0},"44":{"tf":1.0},"52":{"tf":1.0},"63":{"tf":1.4142135623730951},"67":{"tf":1.4142135623730951},"68":{"tf":1.7320508075688772},"70":{"tf":1.0},"72":{"tf":1.0},"78":{"tf":1.0},"80":{"tf":1.0},"9":{"tf":1.0},"92":{"tf":1.0},"93":{"tf":1.0},"98":{"tf":1.0},"99":{"tf":1.4142135623730951}},"df":39}},"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{"13":{"tf":1.0},"24":{"tf":1.0},"87":{"tf":1.0}},"df":3}},"g":{"docs":{},"df":0,"l":{"docs":{"182":{"tf":1.4142135623730951}},"df":1}},"i":{"docs":{"120":{"tf":1.4142135623730951},"122":{"tf":1.0},"126":{"tf":1.0},"129":{"tf":1.0},"130":{"tf":1.0},"134":{"tf":1.0},"157":{"tf":1.0},"160":{"tf":1.0},"161":{"tf":1.0},"81":{"tf":1.0}},"df":10},"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"11":{"tf":1.0},"163":{"tf":1.4142135623730951},"172":{"tf":1.4142135623730951},"174":{"tf":1.4142135623730951},"66":{"tf":1.0},"69":{"tf":1.0},"71":{"tf":1.0},"90":{"tf":1.4142135623730951},"93":{"tf":1.0}},"df":9}},"f":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"114":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"’":{"docs":{"73":{"tf":1.0},"74":{"tf":1.0},"90":{"tf":1.0}},"df":3}}}}}}}}}}},"p":{"docs":{"107":{"tf":1.0},"164":{"tf":1.0},"165":{"tf":1.4142135623730951},"168":{"tf":1.0},"173":{"tf":1.0},"193":{"tf":1.0},"195":{"tf":1.0},"197":{"tf":1.0},"28":{"tf":1.4142135623730951},"4":{"tf":1.0},"5":{"tf":1.4142135623730951},"66":{"tf":1.0},"69":{"tf":1.0},"71":{"tf":1.0},"86":{"tf":1.0},"87":{"tf":1.7320508075688772}},"df":16},"r":{"docs":{},"df":0,"k":{"docs":{"151":{"tf":1.4142135623730951},"165":{"tf":1.4142135623730951},"60":{"tf":1.0},"72":{"tf":1.0},"80":{"tf":1.0},"82":{"tf":1.0},"92":{"tf":1.4142135623730951}},"df":7,"e":{"docs":{},"df":0,"r":{"docs":{"111":{"tf":1.0}},"df":1}}}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"59":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"z":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{"59":{"tf":1.0}},"df":1}}}}}}}},"t":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"119":{"tf":1.0},"136":{"tf":1.0},"158":{"tf":1.0},"53":{"tf":1.7320508075688772},"61":{"tf":1.0},"68":{"tf":1.4142135623730951}},"df":6}},"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"5":{"tf":1.0},"9":{"tf":1.4142135623730951}},"df":2}}},"h":{"docs":{"191":{"tf":1.0},"194":{"tf":1.0}},"df":2,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"115":{"tf":1.0},"191":{"tf":1.0}},"df":2}}}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"107":{"tf":1.0},"88":{"tf":1.0}},"df":2}}}},"x":{"docs":{"163":{"tf":1.0}},"df":1,"i":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{"11":{"tf":1.4142135623730951},"137":{"tf":1.0},"87":{"tf":1.0}},"df":3}}}}},"y":{"docs":{},"df":0,"b":{"docs":{"138":{"tf":1.0}},"df":1}}},"c":{"docs":{},"df":0,"u":{"docs":{"12":{"tf":1.0},"129":{"tf":1.0},"191":{"tf":1.0},"24":{"tf":1.0},"28":{"tf":1.4142135623730951},"36":{"tf":1.4142135623730951}},"df":6,"x":{"docs":{},"df":0,"p":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"o":{"docs":{"36":{"tf":1.0}},"df":1}}}}}}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{"101":{"tf":1.4142135623730951},"105":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.0},"112":{"tf":1.0},"122":{"tf":1.4142135623730951},"126":{"tf":1.0},"13":{"tf":1.0},"134":{"tf":1.0},"137":{"tf":1.0},"164":{"tf":1.0},"175":{"tf":1.0},"177":{"tf":1.0},"53":{"tf":1.0},"72":{"tf":1.0},"75":{"tf":1.0},"98":{"tf":1.0}},"df":17,"t":{"docs":{"190":{"tf":1.0},"196":{"tf":1.0},"68":{"tf":1.0}},"df":3},"w":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"122":{"tf":1.0}},"df":1}}}}},"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"122":{"tf":1.0}},"df":1}}}},"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{"123":{"tf":1.0},"77":{"tf":1.0},"80":{"tf":1.4142135623730951},"84":{"tf":1.0}},"df":4}}}},"e":{"docs":{},"df":0,"t":{"docs":{"166":{"tf":1.0}},"df":1}},"g":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"88":{"tf":1.0}},"df":1}}}}},"m":{"docs":{},"df":0,"b":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"166":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"11":{"tf":1.0},"111":{"tf":1.0},"122":{"tf":1.4142135623730951},"123":{"tf":1.0},"124":{"tf":1.0},"128":{"tf":1.0},"13":{"tf":1.0},"132":{"tf":1.4142135623730951},"133":{"tf":2.23606797749979},"134":{"tf":1.0},"136":{"tf":2.0},"137":{"tf":2.6457513110645907},"14":{"tf":1.4142135623730951},"154":{"tf":1.0},"163":{"tf":1.0},"164":{"tf":1.0},"165":{"tf":1.4142135623730951},"168":{"tf":1.0},"17":{"tf":1.0},"193":{"tf":1.0},"195":{"tf":1.0},"197":{"tf":1.0},"20":{"tf":1.0},"28":{"tf":1.7320508075688772},"32":{"tf":1.0},"35":{"tf":1.0},"4":{"tf":1.0},"5":{"tf":1.4142135623730951},"53":{"tf":1.0},"61":{"tf":2.23606797749979},"66":{"tf":1.4142135623730951},"67":{"tf":1.4142135623730951},"68":{"tf":1.0},"69":{"tf":1.4142135623730951},"78":{"tf":1.0},"83":{"tf":2.0},"86":{"tf":1.4142135623730951},"87":{"tf":2.8284271247461903},"88":{"tf":1.4142135623730951},"95":{"tf":1.0}},"df":40},"y":{"docs":{},"df":0,".":{"docs":{},"df":0,"x":{"docs":{"61":{"tf":1.0},"67":{"tf":2.0},"68":{"tf":1.0}},"df":3}}}}}},"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"15":{"tf":1.0},"159":{"tf":1.0},"5":{"tf":1.4142135623730951},"9":{"tf":1.0}},"df":4}}}}},"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"179":{"tf":1.0}},"df":1}},"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{"128":{"tf":1.0},"188":{"tf":1.0},"29":{"tf":1.0},"63":{"tf":1.0},"68":{"tf":1.0},"77":{"tf":1.7320508075688772},"78":{"tf":1.4142135623730951},"79":{"tf":1.4142135623730951}},"df":8}}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{"62":{"tf":1.4142135623730951}},"df":1}}}},"l":{"docs":{"0":{"tf":1.0},"102":{"tf":1.0},"14":{"tf":1.4142135623730951},"187":{"tf":1.0},"20":{"tf":1.0},"5":{"tf":1.4142135623730951},"86":{"tf":1.0}},"df":7}},"h":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{"101":{"tf":1.0},"102":{"tf":1.0},"105":{"tf":1.7320508075688772},"109":{"tf":1.0},"115":{"tf":1.4142135623730951},"118":{"tf":1.0},"134":{"tf":1.4142135623730951},"136":{"tf":1.4142135623730951},"137":{"tf":1.0},"142":{"tf":1.4142135623730951},"146":{"tf":1.4142135623730951},"153":{"tf":1.0},"154":{"tf":1.0},"155":{"tf":2.23606797749979},"163":{"tf":1.0},"165":{"tf":2.23606797749979},"92":{"tf":1.0},"95":{"tf":1.0}},"df":18}}},"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"190":{"tf":1.0}},"df":1}}}}},"h":{"docs":{},"df":0,"z":{"docs":{"11":{"tf":1.0},"81":{"tf":1.0}},"df":2}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{"69":{"tf":2.8284271247461903},"70":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0}},"df":4,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{"0":{"tf":1.0},"106":{"tf":1.0},"11":{"tf":2.23606797749979},"141":{"tf":1.4142135623730951},"19":{"tf":1.0},"199":{"tf":1.0},"32":{"tf":1.0},"34":{"tf":1.4142135623730951},"35":{"tf":1.0},"36":{"tf":1.0},"55":{"tf":1.0},"61":{"tf":1.4142135623730951},"63":{"tf":1.0},"66":{"tf":1.4142135623730951},"68":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":1.4142135623730951},"86":{"tf":2.0},"87":{"tf":2.0}},"df":19,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"’":{"docs":{"195":{"tf":1.0},"197":{"tf":1.0},"28":{"tf":1.0}},"df":3}}}}}}}}}}},"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"t":{"docs":{"191":{"tf":1.0}},"df":1}}},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{"191":{"tf":1.0}},"df":1}}}},"p":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"120":{"tf":1.4142135623730951},"32":{"tf":1.0}},"df":2}}}}}}}}}}}},"d":{"docs":{},"df":0,"d":{"docs":{},"df":0,"l":{"docs":{"72":{"tf":1.0}},"df":1}}},"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"71":{"tf":1.0},"77":{"tf":1.0}},"df":2}}}}}}}}},"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"139":{"tf":1.0}},"df":1}}},"n":{"docs":{"163":{"tf":1.0}},"df":1,"d":{"docs":{"135":{"tf":1.0},"26":{"tf":1.0},"84":{"tf":1.0}},"df":3},"i":{"docs":{"11":{"tf":1.4142135623730951},"53":{"tf":1.0}},"df":2,"m":{"docs":{"104":{"tf":1.0},"78":{"tf":1.0}},"df":2,"u":{"docs":{},"df":0,"m":{"docs":{"19":{"tf":1.0}},"df":1}}}}},"s":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"63":{"tf":1.0}},"df":1}}}},"n":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"76":{"tf":1.0}},"df":1}}}}}}},"s":{"docs":{"101":{"tf":1.0},"190":{"tf":1.0},"20":{"tf":1.0},"3":{"tf":1.0}},"df":4},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{"93":{"tf":1.0}},"df":1}}}},"t":{"docs":{"9":{"tf":1.0}},"df":1},"x":{"docs":{"2":{"tf":1.0}},"df":1}},"m":{"docs":{},"df":0,"u":{"docs":{"87":{"tf":1.4142135623730951}},"df":1}},"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"117":{"tf":1.0}},"df":1}},"d":{"docs":{"104":{"tf":1.0},"155":{"tf":1.0},"158":{"tf":1.4142135623730951}},"df":3,"e":{"docs":{"103":{"tf":1.0},"106":{"tf":1.4142135623730951},"108":{"tf":1.0},"109":{"tf":3.1622776601683795},"110":{"tf":1.7320508075688772},"112":{"tf":1.0},"117":{"tf":1.0},"64":{"tf":1.0},"72":{"tf":1.4142135623730951},"76":{"tf":1.0}},"df":10,"l":{"docs":{"12":{"tf":1.0},"124":{"tf":1.0},"130":{"tf":1.0},"63":{"tf":1.0}},"df":4},"r":{"docs":{},"df":0,"n":{"docs":{"26":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"f":{"docs":{"68":{"tf":1.0}},"df":1,"i":{"docs":{"102":{"tf":1.0},"103":{"tf":1.0},"107":{"tf":1.0},"124":{"tf":1.4142135623730951},"127":{"tf":1.0},"178":{"tf":1.0},"68":{"tf":1.0},"75":{"tf":2.0},"87":{"tf":1.0},"94":{"tf":1.0}},"df":10}}},"u":{"docs":{},"df":0,"l":{"docs":{"168":{"tf":1.0},"93":{"tf":1.0}},"df":2}}},"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"83":{"tf":1.0}},"df":1}}}},"n":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"68":{"tf":2.0},"77":{"tf":1.0}},"df":2}}}}},"o":{"docs":{},"df":0,"d":{"docs":{"50":{"tf":1.0}},"df":1}},"r":{"docs":{},"df":0,"e":{"docs":{"11":{"tf":1.0},"112":{"tf":1.0},"117":{"tf":1.4142135623730951},"123":{"tf":1.0},"124":{"tf":1.0},"126":{"tf":1.0},"127":{"tf":1.0},"128":{"tf":1.0},"129":{"tf":1.0},"131":{"tf":1.4142135623730951},"137":{"tf":1.0},"154":{"tf":1.0},"158":{"tf":1.0},"160":{"tf":1.0},"163":{"tf":1.4142135623730951},"164":{"tf":1.0},"168":{"tf":1.0},"187":{"tf":1.0},"189":{"tf":1.0},"191":{"tf":1.0},"193":{"tf":1.0},"195":{"tf":1.0},"196":{"tf":1.0},"197":{"tf":1.0},"198":{"tf":1.0},"199":{"tf":1.0},"2":{"tf":1.0},"200":{"tf":1.0},"201":{"tf":1.0},"3":{"tf":1.0},"5":{"tf":1.0},"60":{"tf":1.0},"62":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.4142135623730951},"70":{"tf":1.0},"71":{"tf":1.0},"77":{"tf":1.4142135623730951},"78":{"tf":1.0},"80":{"tf":1.7320508075688772},"84":{"tf":1.0},"86":{"tf":1.0},"92":{"tf":1.0},"99":{"tf":1.0}},"df":44}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"179":{"tf":1.0}},"df":1}}}},"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"118":{"tf":1.0},"86":{"tf":1.0}},"df":2}}},"v":{"docs":{},"df":0,"e":{"docs":{"106":{"tf":1.7320508075688772},"111":{"tf":1.0},"112":{"tf":1.0},"125":{"tf":1.4142135623730951},"127":{"tf":2.23606797749979},"133":{"tf":1.0},"154":{"tf":1.0},"155":{"tf":1.0},"53":{"tf":1.4142135623730951},"64":{"tf":1.0},"69":{"tf":1.0}},"df":11},"t":{"docs":{"83":{"tf":1.0}},"df":1},"w":{"docs":{"62":{"tf":1.0},"83":{"tf":1.0}},"df":2}}},"r":{"docs":{"62":{"tf":1.0}},"df":1},"s":{"docs":{},"df":0,"p":{"docs":{"62":{"tf":1.0}},"df":1}},"u":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"108":{"tf":1.0},"184":{"tf":1.0},"63":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.0},"77":{"tf":1.0},"80":{"tf":1.0}},"df":7}},"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{"123":{"tf":1.0},"131":{"tf":1.4142135623730951}},"df":2,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"43":{"tf":1.4142135623730951},"64":{"tf":1.4142135623730951},"68":{"tf":1.7320508075688772}},"df":3}}}},"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"30":{"tf":1.0},"80":{"tf":1.4142135623730951}},"df":2}}},"p":{"docs":{},"df":0,"l":{"docs":{"101":{"tf":1.0},"119":{"tf":1.0},"120":{"tf":1.0},"123":{"tf":1.4142135623730951},"124":{"tf":1.0},"125":{"tf":2.23606797749979},"126":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.0},"148":{"tf":1.0},"158":{"tf":1.0},"164":{"tf":1.0},"76":{"tf":1.0},"80":{"tf":1.4142135623730951},"93":{"tf":1.0}},"df":15,"e":{"docs":{},"df":0,"x":{"docs":{"117":{"tf":1.0}},"df":1}}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"k":{"docs":{"130":{"tf":1.4142135623730951}},"df":1}}},"h":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"120":{"tf":1.0}},"df":1}}}}},"u":{"docs":{},"df":0,"d":{"docs":{"35":{"tf":1.0}},"df":1}}}}}},"t":{"docs":{"100":{"tf":1.4142135623730951},"101":{"tf":1.0},"102":{"tf":1.4142135623730951},"122":{"tf":2.0},"123":{"tf":1.4142135623730951},"124":{"tf":1.0},"125":{"tf":1.7320508075688772},"126":{"tf":1.0},"127":{"tf":1.7320508075688772},"128":{"tf":1.0},"133":{"tf":2.0},"134":{"tf":1.0},"155":{"tf":2.0},"164":{"tf":1.0},"165":{"tf":2.449489742783178},"173":{"tf":2.0},"71":{"tf":1.0},"76":{"tf":1.4142135623730951},"80":{"tf":2.449489742783178},"81":{"tf":1.7320508075688772},"84":{"tf":1.7320508075688772},"91":{"tf":1.0},"92":{"tf":2.23606797749979},"93":{"tf":2.6457513110645907},"98":{"tf":1.0},"99":{"tf":1.0}},"df":26,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"101":{"tf":1.0},"102":{"tf":1.0},"103":{"tf":1.0},"122":{"tf":1.0},"125":{"tf":1.7320508075688772},"126":{"tf":1.7320508075688772},"127":{"tf":1.4142135623730951},"164":{"tf":2.23606797749979},"80":{"tf":1.0},"92":{"tf":1.0},"94":{"tf":1.0},"96":{"tf":1.0},"98":{"tf":1.0}},"df":13}},"t":{"docs":{"125":{"tf":1.0}},"df":1}},"e":{"docs":{},"df":0,"x":{"docs":{"126":{"tf":3.0},"127":{"tf":3.1622776601683795},"130":{"tf":1.0}},"df":3,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{},"df":0,"(":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{},"df":0,"(":{"docs":{},"df":0,"0":{"docs":{"126":{"tf":1.0}},"df":1}}}}}}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{},"df":0,"(":{"docs":{},"df":0,"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"127":{"tf":1.7320508075688772}},"df":1}}}}}}}}}}}}}}}}}}}}}},"<":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"<":{"docs":{},"df":0,"u":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{"126":{"tf":1.0}},"df":1}}}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"<":{"docs":{},"df":0,"o":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"<":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"m":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{},"df":0,"f":{"docs":{},"df":0,"4":{"docs":{},"df":0,"0":{"docs":{},"df":0,"5":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"g":{"docs":{},"df":0,"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{"127":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}}}},"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"<":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"m":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{},"df":0,"2":{"docs":{"127":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"t":{"docs":{"126":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"126":{"tf":1.0}},"df":1}}}}},"v":{"docs":{"59":{"tf":1.0}},"df":1},"y":{"docs":{},"df":0,"_":{"docs":{},"df":0,"g":{"docs":{},"df":0,"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{"127":{"tf":2.23606797749979}},"df":1,".":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"(":{"docs":{},"df":0,"c":{"docs":{},"df":0,"s":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"127":{"tf":2.0}},"df":1}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"d":{"docs":{},"df":0,"p":{"docs":{},"df":0,".":{"docs":{},"df":0,"g":{"docs":{},"df":0,"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{"127":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"b":{"docs":{},"df":0,".":{"docs":{},"df":0,"a":{"docs":{"184":{"tf":1.0}},"df":1},"d":{"docs":{},"df":0,"l":{"docs":{"184":{"tf":1.0}},"df":1}},"s":{"docs":{},"df":0,"o":{"docs":{"184":{"tf":1.0}},"df":1}}}}}}}}},"n":{"docs":{"115":{"tf":1.0},"155":{"tf":2.8284271247461903},"166":{"tf":1.0},"68":{"tf":1.0}},"df":4,"/":{"docs":{},"df":0,"a":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0}},"df":2}},"1":{"docs":{},"df":0,"0":{"docs":{"68":{"tf":1.0}},"df":1}},"3":{"docs":{"63":{"tf":1.0}},"df":1},"5":{"docs":{"67":{"tf":1.0},"81":{"tf":1.0}},"df":2},"6":{"docs":{"61":{"tf":1.0}},"df":1},"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"b":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{"191":{"tf":1.0}},"df":1}}}}}},"m":{"docs":{},"df":0,"e":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0},"11":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":2.0},"144":{"tf":1.7320508075688772},"147":{"tf":1.7320508075688772},"160":{"tf":1.0},"173":{"tf":1.0},"178":{"tf":1.0},"180":{"tf":1.4142135623730951},"182":{"tf":1.0},"188":{"tf":1.4142135623730951},"190":{"tf":1.0},"20":{"tf":1.0},"55":{"tf":2.0},"57":{"tf":1.0},"58":{"tf":2.449489742783178},"61":{"tf":1.0},"63":{"tf":1.4142135623730951},"64":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.0},"80":{"tf":1.0},"84":{"tf":1.0},"90":{"tf":1.0}},"df":26}},"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"v":{"docs":{"162":{"tf":1.0},"179":{"tf":1.0},"182":{"tf":1.0},"38":{"tf":1.0},"62":{"tf":1.0}},"df":5}},"u":{"docs":{},"df":0,"r":{"docs":{"54":{"tf":1.0}},"df":1}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"136":{"tf":1.0}},"df":1}}}},"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"100":{"tf":1.0},"105":{"tf":1.0},"146":{"tf":1.0},"172":{"tf":1.4142135623730951},"175":{"tf":2.23606797749979},"62":{"tf":1.0}},"df":6}}}}}}},"e":{"docs":{},"df":0,"d":{"docs":{"103":{"tf":1.0},"108":{"tf":1.0},"119":{"tf":1.4142135623730951},"120":{"tf":1.0},"121":{"tf":1.0},"123":{"tf":1.7320508075688772},"124":{"tf":1.0},"125":{"tf":1.4142135623730951},"126":{"tf":1.4142135623730951},"127":{"tf":2.0},"128":{"tf":1.0},"131":{"tf":1.0},"132":{"tf":1.0},"133":{"tf":1.0},"136":{"tf":1.4142135623730951},"155":{"tf":1.0},"158":{"tf":1.0},"160":{"tf":1.0},"162":{"tf":1.0},"163":{"tf":1.0},"164":{"tf":1.0},"165":{"tf":1.0},"173":{"tf":1.0},"182":{"tf":1.0},"183":{"tf":1.4142135623730951},"184":{"tf":1.0},"188":{"tf":1.0},"191":{"tf":1.0},"20":{"tf":1.0},"31":{"tf":1.0},"40":{"tf":1.0},"46":{"tf":1.0},"52":{"tf":1.0},"53":{"tf":1.4142135623730951},"55":{"tf":1.0},"60":{"tf":1.0},"61":{"tf":1.0},"63":{"tf":1.4142135623730951},"64":{"tf":1.0},"66":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.0},"70":{"tf":1.0},"72":{"tf":1.0},"76":{"tf":1.7320508075688772},"77":{"tf":1.4142135623730951},"80":{"tf":1.4142135623730951},"84":{"tf":1.4142135623730951},"9":{"tf":1.0},"91":{"tf":1.0},"93":{"tf":1.0},"96":{"tf":1.0},"99":{"tf":1.4142135623730951}},"df":53,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"123":{"tf":1.0}},"df":1}}}}}}}},"g":{"docs":{"82":{"tf":1.0}},"df":1},"s":{"docs":{},"df":0,"t":{"docs":{"113":{"tf":1.4142135623730951},"125":{"tf":1.0},"84":{"tf":1.0}},"df":3}},"t":{"docs":{},"df":0,"w":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"k":{"docs":{"13":{"tf":1.0}},"df":1}}}}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"112":{"tf":1.0},"123":{"tf":1.0},"126":{"tf":1.0},"133":{"tf":1.0},"134":{"tf":1.0},"138":{"tf":1.0},"139":{"tf":1.0},"5":{"tf":1.0},"80":{"tf":1.0}},"df":9}}},"w":{"docs":{"105":{"tf":1.0},"123":{"tf":1.0},"127":{"tf":1.0},"146":{"tf":1.0},"150":{"tf":1.0},"159":{"tf":1.0},"160":{"tf":1.0},"173":{"tf":1.0},"180":{"tf":1.0},"20":{"tf":1.0},"53":{"tf":1.0},"56":{"tf":1.4142135623730951},"57":{"tf":1.4142135623730951},"62":{"tf":1.0},"67":{"tf":1.4142135623730951},"70":{"tf":1.0},"76":{"tf":1.4142135623730951},"8":{"tf":1.0},"80":{"tf":1.0},"93":{"tf":1.0}},"df":20,"(":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"h":{"docs":{"146":{"tf":1.0}},"df":1}}}}}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"104":{"tf":1.0}},"df":1}}}}}},"e":{"docs":{},"df":0,"r":{"docs":{"19":{"tf":1.4142135623730951},"38":{"tf":1.4142135623730951},"43":{"tf":1.7320508075688772},"68":{"tf":1.0}},"df":4}},"l":{"docs":{},"df":0,"i":{"docs":{"122":{"tf":1.0}},"df":1,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"s":{"docs":{},"df":0,"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"r":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"76":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}},"t":{"docs":{},"df":0,"y":{"docs":{},"df":0,"p":{"docs":{"190":{"tf":1.0}},"df":1}}}},"x":{"docs":{},"df":0,"t":{"docs":{"133":{"tf":1.0},"158":{"tf":1.0},"19":{"tf":1.0},"44":{"tf":1.0},"47":{"tf":1.0},"52":{"tf":1.0},"53":{"tf":1.4142135623730951},"61":{"tf":1.0},"63":{"tf":1.0},"64":{"tf":2.23606797749979},"68":{"tf":1.4142135623730951}},"df":11}}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{"142":{"tf":1.0},"187":{"tf":1.0},"70":{"tf":1.0}},"df":3}},"g":{"docs":{},"df":0,"h":{"docs":{},"df":0,"t":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"60":{"tf":1.0}},"df":1}}}}},"t":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{"5":{"tf":1.0}},"df":1}}},"x":{"docs":{"63":{"tf":1.0}},"df":1}},"m":{"docs":{"21":{"tf":1.0}},"df":1},"o":{"docs":{},"df":0,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"191":{"tf":1.0},"60":{"tf":1.7320508075688772},"71":{"tf":1.0},"72":{"tf":1.0},"76":{"tf":1.0},"77":{"tf":1.7320508075688772},"78":{"tf":1.0},"79":{"tf":1.0},"81":{"tf":1.0},"83":{"tf":1.0}},"df":10}},"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"l":{"docs":{"183":{"tf":1.0},"184":{"tf":1.0},"82":{"tf":1.0}},"df":3}}}}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"d":{"docs":{"14":{"tf":1.0},"15":{"tf":1.0},"16":{"tf":1.4142135623730951},"17":{"tf":1.0},"174":{"tf":1.0},"177":{"tf":1.0},"191":{"tf":1.0},"20":{"tf":1.0},"60":{"tf":1.7320508075688772},"71":{"tf":1.0},"72":{"tf":1.0},"76":{"tf":1.0},"77":{"tf":2.0},"78":{"tf":1.0},"79":{"tf":1.0},"81":{"tf":1.0},"83":{"tf":1.0}},"df":17}}}},"g":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"h":{"docs":{"63":{"tf":2.0},"64":{"tf":1.0},"68":{"tf":1.0}},"df":3}}}}},"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"61":{"tf":1.0}},"df":1}}}},"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"124":{"tf":1.0}},"df":1}}},"n":{"docs":{"11":{"tf":1.0}},"df":1}}},"n":{"docs":{"100":{"tf":1.0},"122":{"tf":1.0},"146":{"tf":1.4142135623730951},"155":{"tf":1.0},"164":{"tf":1.0},"175":{"tf":1.0},"20":{"tf":1.0},"61":{"tf":1.0},"62":{"tf":1.4142135623730951},"63":{"tf":1.0},"80":{"tf":2.0}},"df":11,"e":{"docs":{"125":{"tf":1.0},"127":{"tf":1.7320508075688772},"162":{"tf":1.4142135623730951},"190":{"tf":1.0},"38":{"tf":2.8284271247461903},"43":{"tf":2.0},"46":{"tf":1.0},"47":{"tf":1.0},"49":{"tf":1.0},"53":{"tf":1.4142135623730951},"61":{"tf":2.8284271247461903},"62":{"tf":1.0},"63":{"tf":2.449489742783178},"64":{"tf":1.7320508075688772},"67":{"tf":2.449489742783178},"68":{"tf":2.6457513110645907},"81":{"tf":1.0},"99":{"tf":1.0}},"df":18,"x":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"83":{"tf":1.4142135623730951}},"df":1}}}}}},"r":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"87":{"tf":1.0},"88":{"tf":1.0}},"df":2}}},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"126":{"tf":1.0},"13":{"tf":1.0},"53":{"tf":1.0},"64":{"tf":1.0},"69":{"tf":1.0}},"df":5}}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"36":{"tf":1.0}},"df":1}}},"e":{"docs":{"102":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.0},"123":{"tf":1.4142135623730951},"124":{"tf":1.0},"133":{"tf":1.0},"134":{"tf":1.0},"158":{"tf":1.0},"165":{"tf":1.0},"166":{"tf":1.7320508075688772},"191":{"tf":1.0},"38":{"tf":1.0},"40":{"tf":1.0},"43":{"tf":1.7320508075688772},"44":{"tf":1.0},"46":{"tf":1.0},"53":{"tf":1.0},"61":{"tf":1.4142135623730951},"62":{"tf":1.7320508075688772},"63":{"tf":1.0},"64":{"tf":1.4142135623730951},"67":{"tf":2.0},"68":{"tf":2.0},"71":{"tf":1.0},"74":{"tf":1.0},"76":{"tf":1.0},"77":{"tf":1.4142135623730951},"80":{"tf":1.0},"81":{"tf":1.0},"83":{"tf":1.0}},"df":30},"h":{"docs":{"71":{"tf":1.0},"77":{"tf":1.0},"86":{"tf":1.0},"94":{"tf":1.0}},"df":4},"i":{"docs":{},"df":0,"c":{"docs":{"101":{"tf":1.0},"92":{"tf":1.0}},"df":2}}},"w":{"docs":{"109":{"tf":1.4142135623730951},"125":{"tf":1.0},"126":{"tf":1.0},"127":{"tf":1.4142135623730951},"166":{"tf":1.0},"167":{"tf":1.0},"183":{"tf":1.0},"184":{"tf":1.0},"188":{"tf":1.0},"41":{"tf":1.0},"44":{"tf":1.0},"53":{"tf":1.0},"61":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.4142135623730951},"64":{"tf":1.7320508075688772},"65":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":2.23606797749979},"78":{"tf":1.0},"92":{"tf":2.0},"93":{"tf":1.4142135623730951}},"df":22}},"r":{"docs":{},"df":0,"f":{"docs":{},"df":0,"5":{"docs":{},"df":0,"2":{"docs":{},"df":0,"8":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{"87":{"tf":1.0},"88":{"tf":1.0}},"df":2}}}}}}},"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{"133":{"tf":1.0},"173":{"tf":1.0}},"df":2}},"m":{"docs":{"168":{"tf":1.0}},"df":1,"b":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"114":{"tf":1.0},"115":{"tf":1.4142135623730951},"120":{"tf":1.0},"128":{"tf":1.0},"154":{"tf":1.0},"158":{"tf":1.0},"166":{"tf":1.0},"17":{"tf":1.0},"189":{"tf":1.0},"191":{"tf":1.0},"194":{"tf":1.0},"44":{"tf":1.4142135623730951},"62":{"tf":1.4142135623730951},"67":{"tf":1.0},"69":{"tf":1.0},"95":{"tf":1.0}},"df":16,"(":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0}},"df":2}}}},"e":{"docs":{},"df":0,"r":{"docs":{"72":{"tf":1.0}},"df":1}}},"t":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"115":{"tf":1.0}},"df":1}}}}}},"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"127":{"tf":1.0}},"df":1}}},"x":{"docs":{},"df":0,"p":{"docs":{"36":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"b":{"docs":{},"df":0,"j":{"docs":{},"df":0,"d":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{"21":{"tf":1.4142135623730951},"62":{"tf":1.4142135623730951},"83":{"tf":1.0}},"df":3}}}},"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"104":{"tf":2.0},"105":{"tf":1.0},"146":{"tf":1.7320508075688772},"161":{"tf":1.0},"73":{"tf":1.0},"76":{"tf":2.23606797749979},"97":{"tf":1.0}},"df":7}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"v":{"docs":{"136":{"tf":1.0}},"df":1}}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"100":{"tf":1.0},"101":{"tf":1.0},"104":{"tf":1.0},"125":{"tf":1.0},"126":{"tf":1.0},"127":{"tf":1.4142135623730951},"166":{"tf":1.0},"99":{"tf":1.0}},"df":8}}}}},"c":{"docs":{},"df":0,"c":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{"84":{"tf":1.0}},"df":1}},"u":{"docs":{},"df":0,"p":{"docs":{},"df":0,"i":{"docs":{"187":{"tf":1.4142135623730951},"62":{"tf":1.0}},"df":2}},"r":{"docs":{"120":{"tf":1.0},"136":{"tf":1.0},"165":{"tf":1.4142135623730951},"92":{"tf":1.0}},"df":4}}}},"f":{"docs":{},"df":0,"f":{"docs":{"119":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"r":{"docs":{"114":{"tf":1.0},"129":{"tf":1.0},"168":{"tf":1.0},"69":{"tf":1.7320508075688772},"70":{"tf":1.0},"96":{"tf":1.0}},"df":6}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{"50":{"tf":1.0},"51":{"tf":1.0}},"df":2}}},"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"86":{"tf":1.7320508075688772}},"df":1}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"90":{"tf":1.0}},"df":1}}}}},"h":{"docs":{"75":{"tf":1.0}},"df":1},"k":{"docs":{"100":{"tf":1.0},"108":{"tf":1.7320508075688772}},"df":2,"(":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"83":{"tf":1.0}},"df":1}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{},"df":0,".":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"h":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"108":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"l":{"docs":{},"df":0,"d":{"docs":{"53":{"tf":1.4142135623730951},"86":{"tf":1.0}},"df":2,"e":{"docs":{},"df":0,"r":{"docs":{"68":{"tf":1.4142135623730951},"78":{"tf":1.0}},"df":2}}}},"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"61":{"tf":1.0},"81":{"tf":1.0}},"df":2}}},"n":{"docs":{"101":{"tf":1.0},"102":{"tf":1.0},"103":{"tf":1.4142135623730951},"105":{"tf":1.0},"117":{"tf":1.4142135623730951},"122":{"tf":1.0},"123":{"tf":1.0},"125":{"tf":1.4142135623730951},"126":{"tf":1.0},"127":{"tf":1.7320508075688772},"128":{"tf":1.0},"132":{"tf":1.0},"134":{"tf":1.0},"14":{"tf":1.0},"158":{"tf":1.0},"164":{"tf":1.4142135623730951},"168":{"tf":1.0},"173":{"tf":1.4142135623730951},"174":{"tf":1.0},"184":{"tf":1.0},"190":{"tf":1.0},"197":{"tf":1.0},"21":{"tf":1.0},"38":{"tf":1.4142135623730951},"49":{"tf":1.0},"53":{"tf":1.7320508075688772},"61":{"tf":1.4142135623730951},"63":{"tf":1.0},"64":{"tf":1.0},"67":{"tf":1.7320508075688772},"68":{"tf":2.0},"69":{"tf":1.4142135623730951},"71":{"tf":1.0},"75":{"tf":1.7320508075688772},"77":{"tf":1.0},"78":{"tf":1.4142135623730951},"80":{"tf":1.7320508075688772},"87":{"tf":1.0},"92":{"tf":1.4142135623730951},"93":{"tf":1.0},"96":{"tf":1.4142135623730951},"97":{"tf":1.0}},"df":42,"_":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"(":{"docs":{},"df":0,"_":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"y":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"133":{"tf":1.0}},"df":1}}}}}}}}}}}},"c":{"docs":{"100":{"tf":1.0},"127":{"tf":1.0},"133":{"tf":1.0},"68":{"tf":1.0},"78":{"tf":1.0},"93":{"tf":1.0},"99":{"tf":1.7320508075688772}},"df":7},"t":{"docs":{},"df":0,"o":{"docs":{"62":{"tf":1.0},"68":{"tf":1.0}},"df":2}}},"o":{"docs":{},"df":0,"m":{"docs":{"133":{"tf":1.0},"136":{"tf":2.23606797749979}},"df":2}},"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"130":{"tf":1.0},"27":{"tf":1.0},"33":{"tf":1.0},"50":{"tf":1.0},"53":{"tf":1.4142135623730951},"54":{"tf":1.0},"6":{"tf":1.0},"63":{"tf":1.0},"68":{"tf":1.0}},"df":9,"d":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"155":{"tf":1.7320508075688772}},"df":1}}}}},"g":{"docs":{},"df":0,"l":{"docs":{"86":{"tf":1.0}},"df":1}},"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"d":{"docs":{"19":{"tf":1.0},"191":{"tf":1.0},"24":{"tf":1.0},"26":{"tf":1.0},"27":{"tf":1.7320508075688772},"31":{"tf":1.0},"43":{"tf":2.0},"44":{"tf":1.0},"46":{"tf":2.0},"47":{"tf":1.4142135623730951},"50":{"tf":1.7320508075688772},"52":{"tf":1.0},"53":{"tf":2.6457513110645907},"54":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":3.4641016151377544},"77":{"tf":2.0},"81":{"tf":1.0},"83":{"tf":1.4142135623730951}},"df":19,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"f":{"docs":{},"df":0,"g":{"docs":{"68":{"tf":1.7320508075688772}},"df":1}}},"g":{"docs":{},"df":0,"d":{"docs":{},"df":0,"b":{"docs":{"68":{"tf":2.6457513110645907}},"df":1}}}}}}}},"r":{"docs":{"103":{"tf":1.0},"11":{"tf":1.0},"114":{"tf":1.4142135623730951},"117":{"tf":1.4142135623730951},"124":{"tf":2.0},"125":{"tf":1.4142135623730951},"130":{"tf":1.0},"134":{"tf":1.7320508075688772},"136":{"tf":1.0},"137":{"tf":1.0},"138":{"tf":1.7320508075688772},"16":{"tf":1.0},"160":{"tf":1.4142135623730951},"191":{"tf":1.0},"194":{"tf":1.0},"69":{"tf":1.0},"77":{"tf":2.0},"78":{"tf":1.0},"79":{"tf":1.0},"80":{"tf":1.0},"83":{"tf":1.4142135623730951},"84":{"tf":1.0}},"df":22}},"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{"190":{"tf":1.0}},"df":1}}}},"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"164":{"tf":1.0}},"df":1}}}}},"t":{"docs":{"187":{"tf":1.0},"188":{"tf":1.7320508075688772},"189":{"tf":2.23606797749979},"190":{"tf":2.449489742783178},"78":{"tf":1.0}},"df":5,"i":{"docs":{},"df":0,"m":{"docs":{"112":{"tf":1.0},"186":{"tf":1.0},"187":{"tf":2.23606797749979},"188":{"tf":2.8284271247461903},"189":{"tf":1.7320508075688772},"190":{"tf":2.6457513110645907},"62":{"tf":1.0},"63":{"tf":1.0}},"df":8,"i":{"docs":{},"df":0,"s":{"docs":{"158":{"tf":1.0},"163":{"tf":1.0},"188":{"tf":1.0}},"df":3}}},"o":{"docs":{},"df":0,"n":{"docs":{"117":{"tf":1.0},"127":{"tf":2.0},"132":{"tf":1.0},"19":{"tf":1.0},"36":{"tf":1.0},"49":{"tf":1.0},"68":{"tf":1.4142135623730951},"99":{"tf":1.0}},"df":8,"<":{"docs":{},"df":0,"&":{"docs":{},"df":0,"g":{"docs":{},"df":0,"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{"127":{"tf":1.0}},"df":1}}}}}},"g":{"docs":{},"df":0,"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{"127":{"tf":1.0}},"df":1}}}}},"h":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"81":{"tf":1.0}},"df":1}}}}}}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"99":{"tf":1.0}},"df":1}}}}}}}}}},"t":{"docs":{"100":{"tf":1.0},"99":{"tf":1.0}},"df":2}}}}}}},"r":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"120":{"tf":1.0},"124":{"tf":2.0},"166":{"tf":1.7320508075688772},"173":{"tf":1.4142135623730951},"20":{"tf":1.0},"28":{"tf":1.0},"63":{"tf":1.0},"84":{"tf":1.0},"86":{"tf":1.0},"91":{"tf":1.0}},"df":10,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"x":{"docs":{"124":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}},"g":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{"173":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"105":{"tf":1.0},"61":{"tf":1.4142135623730951},"67":{"tf":1.4142135623730951}},"df":3,"(":{"docs":{},"df":0,"f":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{"61":{"tf":1.0}},"df":1}}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"61":{"tf":1.0}},"df":1}}}}}}}}},"s":{"docs":{"114":{"tf":1.0},"14":{"tf":1.0},"15":{"tf":1.0},"21":{"tf":1.0},"37":{"tf":1.0},"41":{"tf":1.0}},"df":6,"/":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{"62":{"tf":1.0}},"df":1}}}},"c":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"76":{"tf":1.0}},"df":1}}}},"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"119":{"tf":1.0}},"df":1,"w":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{"127":{"tf":1.4142135623730951},"61":{"tf":1.0}},"df":2}}}}}}},"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"v":{"docs":{"125":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0}},"df":3}}}}},"t":{"docs":{"107":{"tf":1.0},"120":{"tf":1.0},"126":{"tf":1.0},"127":{"tf":2.0},"128":{"tf":1.0},"133":{"tf":1.4142135623730951},"136":{"tf":1.4142135623730951},"160":{"tf":1.0},"163":{"tf":1.0},"176":{"tf":1.0},"177":{"tf":1.0},"187":{"tf":1.0},"188":{"tf":1.0},"3":{"tf":1.7320508075688772},"53":{"tf":1.0},"63":{"tf":1.4142135623730951},"67":{"tf":1.0},"68":{"tf":2.0},"69":{"tf":1.0},"78":{"tf":1.0},"79":{"tf":1.4142135623730951},"88":{"tf":1.0},"91":{"tf":1.0},"93":{"tf":1.0}},"df":24,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"24":{"tf":1.0}},"df":1}}},"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"103":{"tf":1.0},"106":{"tf":2.449489742783178},"107":{"tf":2.23606797749979},"108":{"tf":2.449489742783178},"109":{"tf":2.6457513110645907},"11":{"tf":1.0},"110":{"tf":1.7320508075688772},"115":{"tf":1.0},"121":{"tf":1.7320508075688772},"127":{"tf":1.0},"155":{"tf":2.23606797749979},"174":{"tf":1.4142135623730951},"180":{"tf":1.0},"53":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.7320508075688772},"76":{"tf":1.0},"81":{"tf":1.0}},"df":18,"<":{"docs":{"155":{"tf":1.7320508075688772}},"df":1},"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{"107":{"tf":1.4142135623730951},"108":{"tf":1.0}},"df":2}}},"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"109":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"_":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"n":{"docs":{"109":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"(":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"u":{"docs":{"109":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}},"s":{"docs":{},"df":0,"t":{"docs":{"155":{"tf":2.8284271247461903}},"df":1,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,">":{"docs":{},"df":0,"(":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"155":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}},"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{"163":{"tf":1.0},"182":{"tf":1.0},"86":{"tf":1.0}},"df":3}}},"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{"183":{"tf":1.0}},"df":1}}}}}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"131":{"tf":1.0},"160":{"tf":1.0},"166":{"tf":1.0},"21":{"tf":1.0},"53":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.0},"88":{"tf":1.4142135623730951},"93":{"tf":1.0}},"df":9,"a":{"docs":{},"df":0,"l":{"docs":{"13":{"tf":1.0}},"df":1}},"f":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"15":{"tf":1.0},"17":{"tf":1.0}},"df":2}}}},"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"125":{"tf":1.4142135623730951},"127":{"tf":1.0},"128":{"tf":1.4142135623730951},"157":{"tf":1.0},"99":{"tf":1.0}},"df":5}}}},"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{"188":{"tf":2.23606797749979},"53":{"tf":1.0},"68":{"tf":1.0},"82":{"tf":1.7320508075688772}},"df":4}}},"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{"193":{"tf":1.0},"195":{"tf":1.0},"197":{"tf":1.0}},"df":3}}}},"w":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"m":{"docs":{"70":{"tf":1.0}},"df":1}}}}}}}},"w":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{"101":{"tf":1.0},"103":{"tf":1.0},"164":{"tf":1.0},"96":{"tf":1.0},"98":{"tf":1.0}},"df":5}}}}}}}}},"p":{"docs":{"166":{"tf":3.1622776601683795},"72":{"tf":1.0},"76":{"tf":1.0},"81":{"tf":1.0},"93":{"tf":1.4142135623730951},"99":{"tf":1.0}},"df":6,".":{"docs":{},"df":0,"g":{"docs":{},"df":0,"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"_":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,".":{"docs":{},"df":0,"s":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"s":{"docs":{},"df":0,"c":{"docs":{},"df":0,".":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{"76":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"p":{"docs":{},"df":0,"w":{"docs":{},"df":0,"m":{"docs":{},"df":0,"0":{"docs":{"72":{"tf":1.0}},"df":1}}}},"s":{"docs":{},"df":0,"y":{"docs":{},"df":0,"s":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"l":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"76":{"tf":1.0}},"df":1}}}}}}}}}}}}},"t":{"docs":{"81":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"0":{"docs":{"76":{"tf":1.0}},"df":1}}}},"n":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"99":{"tf":1.0}},"df":1}}}}}}},"a":{"docs":{"154":{"tf":2.0},"155":{"tf":1.0}},"df":2,"0":{"docs":{"127":{"tf":1.7320508075688772},"153":{"tf":2.23606797749979},"154":{"tf":1.4142135623730951},"76":{"tf":1.0}},"df":4},"1":{"docs":{"127":{"tf":1.4142135623730951},"153":{"tf":2.23606797749979},"76":{"tf":1.0}},"df":3,"<":{"docs":{"155":{"tf":1.4142135623730951}},"df":1,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"<":{"docs":{},"df":0,"n":{"docs":{"155":{"tf":1.0}},"df":1}}}}}}},"n":{"docs":{"155":{"tf":2.0}},"df":1},"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"<":{"docs":{},"df":0,"n":{"docs":{"155":{"tf":1.0}},"df":1}}}}}}}}}},"c":{"docs":{"142":{"tf":1.0},"147":{"tf":2.0},"195":{"tf":1.0},"69":{"tf":1.0},"76":{"tf":1.0}},"df":5,"k":{"docs":{"154":{"tf":1.0},"166":{"tf":1.7320508075688772},"68":{"tf":1.0},"86":{"tf":1.0}},"df":4,"a":{"docs":{},"df":0,"g":{"docs":{"178":{"tf":1.4142135623730951},"188":{"tf":1.0},"20":{"tf":1.0},"40":{"tf":1.0},"58":{"tf":1.0},"78":{"tf":1.0}},"df":6}}},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{"43":{"tf":1.0}},"df":1}}}},"d":{"docs":{"166":{"tf":1.7320508075688772},"173":{"tf":1.0}},"df":2},"g":{"docs":{},"df":0,"e":{"docs":{"160":{"tf":1.0},"19":{"tf":1.0},"193":{"tf":1.0},"195":{"tf":1.0},"197":{"tf":1.0},"37":{"tf":1.0},"9":{"tf":1.0}},"df":7}},"n":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"127":{"tf":1.0},"68":{"tf":1.0},"72":{"tf":1.0},"76":{"tf":1.0},"77":{"tf":2.23606797749979},"78":{"tf":3.7416573867739413},"79":{"tf":2.0},"81":{"tf":1.0},"99":{"tf":1.4142135623730951}},"df":9,"_":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"78":{"tf":2.0}},"df":1}}}}},"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{"191":{"tf":1.0},"60":{"tf":1.4142135623730951},"64":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0},"76":{"tf":1.0},"77":{"tf":1.4142135623730951},"78":{"tf":1.0},"81":{"tf":1.0},"83":{"tf":1.0}},"df":10}},"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"l":{"docs":{"60":{"tf":1.0},"78":{"tf":1.4142135623730951}},"df":2}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"h":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"77":{"tf":1.0},"79":{"tf":1.0}},"df":2}}}}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"f":{"docs":{},"df":0,"o":{"docs":{"78":{"tf":1.0}},"df":1}}}},"k":{"docs":{"60":{"tf":1.4142135623730951},"77":{"tf":1.4142135623730951},"78":{"tf":3.0},"79":{"tf":1.0}},"df":4}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"86":{"tf":1.0}},"df":1}}}},"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"142":{"tf":1.0},"151":{"tf":1.0},"155":{"tf":1.0}},"df":3}}}},"t":{"docs":{"103":{"tf":1.0},"11":{"tf":2.0},"117":{"tf":1.0},"119":{"tf":1.4142135623730951},"120":{"tf":1.7320508075688772},"121":{"tf":1.0},"133":{"tf":1.0},"134":{"tf":1.0},"138":{"tf":1.0},"14":{"tf":1.0},"160":{"tf":1.0},"164":{"tf":1.0},"171":{"tf":1.0},"173":{"tf":1.0},"179":{"tf":1.0},"183":{"tf":1.0},"22":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"69":{"tf":1.0},"78":{"tf":1.0},"84":{"tf":1.0}},"df":22,"i":{"docs":{"63":{"tf":1.0},"92":{"tf":1.0}},"df":2,"c":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"124":{"tf":1.0},"188":{"tf":1.0},"189":{"tf":1.0},"2":{"tf":1.4142135623730951},"69":{"tf":1.7320508075688772},"72":{"tf":1.4142135623730951},"73":{"tf":1.7320508075688772},"74":{"tf":1.7320508075688772},"75":{"tf":1.0},"82":{"tf":1.0},"93":{"tf":1.0}},"df":11,"l":{"docs":{},"df":0,"i":{"docs":{"36":{"tf":1.0}},"df":1}}}}}}}}}},"s":{"docs":{},"df":0,"s":{"docs":{"122":{"tf":1.0},"125":{"tf":1.7320508075688772},"128":{"tf":1.0},"165":{"tf":1.0},"64":{"tf":1.0},"76":{"tf":1.0},"77":{"tf":1.4142135623730951},"96":{"tf":1.0}},"df":8},"t":{"docs":{"61":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"h":{"docs":{"177":{"tf":1.0},"44":{"tf":1.4142135623730951},"49":{"tf":1.4142135623730951},"50":{"tf":1.7320508075688772}},"df":4},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"n":{"docs":{"104":{"tf":1.0},"131":{"tf":1.0},"140":{"tf":1.0},"141":{"tf":1.4142135623730951},"142":{"tf":1.0},"157":{"tf":1.0},"160":{"tf":1.4142135623730951},"163":{"tf":1.0},"97":{"tf":1.7320508075688772}},"df":9}}}}},"u":{"docs":{},"df":0,"s":{"docs":{"123":{"tf":1.0}},"df":1}}},"c":{"docs":{"13":{"tf":1.4142135623730951},"53":{"tf":1.0},"68":{"tf":3.0},"83":{"tf":1.4142135623730951},"86":{"tf":1.0}},"df":5},"d":{"docs":{},"df":0,"f":{"docs":{"87":{"tf":1.0},"88":{"tf":1.0}},"df":2}},"e":{"docs":{},"df":0,"e":{"docs":{},"df":0,"k":{"docs":{"68":{"tf":1.0}},"df":1}},"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{"110":{"tf":1.0}},"df":1}}}}},"o":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{"3":{"tf":1.0}},"df":1}}},"r":{"docs":{"158":{"tf":1.0},"72":{"tf":1.0},"88":{"tf":1.4142135623730951}},"df":3,"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"70":{"tf":1.0}},"df":1,"l":{"docs":{},"df":0,"i":{"docs":{"121":{"tf":1.0},"93":{"tf":1.0}},"df":2}}}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"m":{"docs":{"100":{"tf":1.0},"103":{"tf":1.0},"106":{"tf":1.7320508075688772},"110":{"tf":1.0},"114":{"tf":1.0},"121":{"tf":1.0},"151":{"tf":1.0},"160":{"tf":1.0},"165":{"tf":2.0},"174":{"tf":1.0},"191":{"tf":1.4142135623730951},"35":{"tf":1.0},"72":{"tf":1.0},"83":{"tf":1.0},"92":{"tf":1.4142135623730951}},"df":15}}}},"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"13":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{"122":{"tf":1.4142135623730951},"128":{"tf":1.0}},"df":2}},"p":{"docs":{},"df":0,"h":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":2.6457513110645907},"112":{"tf":1.0}},"df":4,"e":{"docs":{},"df":0,"r":{"docs":{"100":{"tf":2.449489742783178},"103":{"tf":2.0},"106":{"tf":1.4142135623730951},"107":{"tf":1.0},"11":{"tf":1.0},"114":{"tf":1.4142135623730951},"115":{"tf":1.4142135623730951},"118":{"tf":1.0},"119":{"tf":1.4142135623730951},"121":{"tf":1.4142135623730951},"126":{"tf":1.0},"127":{"tf":3.1622776601683795},"146":{"tf":2.0},"150":{"tf":1.4142135623730951},"164":{"tf":1.4142135623730951},"165":{"tf":1.4142135623730951},"195":{"tf":1.4142135623730951},"197":{"tf":1.4142135623730951},"198":{"tf":1.0},"4":{"tf":1.0},"69":{"tf":2.6457513110645907},"7":{"tf":1.0},"71":{"tf":2.23606797749979},"72":{"tf":1.7320508075688772},"73":{"tf":1.0},"74":{"tf":1.0},"76":{"tf":2.23606797749979},"78":{"tf":1.0},"86":{"tf":2.23606797749979},"87":{"tf":1.0},"88":{"tf":2.0},"90":{"tf":1.0},"92":{"tf":1.0},"93":{"tf":1.0},"95":{"tf":2.23606797749979},"96":{"tf":1.7320508075688772},"99":{"tf":3.1622776601683795}},"df":37,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"(":{"docs":{"84":{"tf":1.0}},"df":1},"s":{"docs":{},"df":0,".":{"docs":{},"df":0,"s":{"docs":{},"df":0,"y":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"71":{"tf":1.0}},"df":1}}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{},"df":0,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"101":{"tf":1.0},"99":{"tf":1.4142135623730951}},"df":2}}}}}}}}}},":":{"docs":{},"df":0,":":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{"71":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"71":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}},"’":{"docs":{"69":{"tf":1.0}},"df":1}}}}}}}},"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"166":{"tf":1.4142135623730951},"44":{"tf":1.7320508075688772},"53":{"tf":1.4142135623730951},"87":{"tf":1.0}},"df":4}},"t":{"docs":{"126":{"tf":1.0},"159":{"tf":1.0},"160":{"tf":1.0}},"df":3}}}}},"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"d":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"<":{"docs":{"155":{"tf":1.7320508075688772}},"df":1}}}}}}}}}},"y":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"119":{"tf":1.0},"137":{"tf":1.0},"80":{"tf":1.0},"87":{"tf":1.4142135623730951}},"df":4}}}}},"i":{"docs":{"12":{"tf":1.0}},"df":1,"c":{"docs":{},"df":0,"k":{"docs":{"139":{"tf":1.0},"61":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.7320508075688772},"76":{"tf":1.0},"78":{"tf":1.0}},"df":6},"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"53":{"tf":1.0},"9":{"tf":1.0}},"df":2}}}},"d":{"docs":{"53":{"tf":1.0},"68":{"tf":1.0}},"df":2},"e":{"docs":{},"df":0,"c":{"docs":{"173":{"tf":1.0},"86":{"tf":1.0},"92":{"tf":1.4142135623730951}},"df":3}},"n":{"docs":{"103":{"tf":1.7320508075688772},"106":{"tf":1.4142135623730951},"107":{"tf":2.0},"108":{"tf":2.0},"109":{"tf":3.0},"11":{"tf":1.4142135623730951},"110":{"tf":1.4142135623730951},"115":{"tf":1.0},"117":{"tf":1.0},"142":{"tf":2.6457513110645907},"146":{"tf":1.0},"153":{"tf":1.7320508075688772},"154":{"tf":3.3166247903554},"155":{"tf":3.1622776601683795},"69":{"tf":1.4142135623730951},"76":{"tf":2.449489742783178}},"df":16,".":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"_":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"109":{"tf":1.0}},"df":1}}}}}}}}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"_":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"n":{"docs":{"109":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}},"<":{"docs":{},"df":0,"n":{"docs":{"155":{"tf":1.4142135623730951}},"df":1}},"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"109":{"tf":1.4142135623730951}},"df":1}}}}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"155":{"tf":2.23606797749979}},"df":1}}}}},"p":{"docs":{},"df":0,"e":{"docs":{"174":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"35":{"tf":1.0}},"df":1}}}},"k":{"docs":{},"df":0,"g":{"docs":{"40":{"tf":1.0}},"df":1}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{"101":{"tf":1.0},"133":{"tf":1.0},"136":{"tf":1.0},"166":{"tf":1.0},"178":{"tf":1.4142135623730951},"61":{"tf":1.4142135623730951},"91":{"tf":1.0}},"df":7,"h":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"d":{"docs":{"58":{"tf":1.0},"59":{"tf":1.0}},"df":2}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{"127":{"tf":1.4142135623730951},"53":{"tf":1.0},"68":{"tf":1.0},"80":{"tf":1.0},"84":{"tf":1.0}},"df":5}},"n":{"docs":{"125":{"tf":1.0}},"df":1},"t":{"docs":{},"df":0,"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"m":{"docs":{"114":{"tf":1.0},"122":{"tf":1.0},"123":{"tf":1.0},"124":{"tf":1.7320508075688772},"125":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.4142135623730951},"162":{"tf":1.4142135623730951},"168":{"tf":1.0},"177":{"tf":1.0},"183":{"tf":1.0},"184":{"tf":1.0},"31":{"tf":1.0}},"df":13}}}}},"y":{"docs":{"101":{"tf":1.0},"119":{"tf":1.0}},"df":2}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{"174":{"tf":1.0},"53":{"tf":1.0},"6":{"tf":1.0},"84":{"tf":1.0},"9":{"tf":1.0}},"df":5,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"108":{"tf":1.0}},"df":1}}}}},"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{"78":{"tf":1.0}},"df":1}}}},"l":{"docs":{"76":{"tf":1.4142135623730951}},"df":1},"u":{"docs":{"187":{"tf":1.0},"19":{"tf":1.0}},"df":2,"g":{"docs":{"44":{"tf":1.4142135623730951}},"df":1}}},"o":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"101":{"tf":1.0},"11":{"tf":1.0},"130":{"tf":1.0},"184":{"tf":1.0},"194":{"tf":1.4142135623730951},"38":{"tf":1.7320508075688772},"53":{"tf":1.0},"56":{"tf":1.0},"60":{"tf":1.4142135623730951},"62":{"tf":1.0},"64":{"tf":1.4142135623730951},"68":{"tf":1.0},"70":{"tf":1.0},"72":{"tf":1.0},"83":{"tf":1.0},"93":{"tf":1.0}},"df":16,"e":{"docs":{},"df":0,"r":{"docs":{"133":{"tf":2.0},"164":{"tf":2.449489742783178},"165":{"tf":1.0},"166":{"tf":1.0},"167":{"tf":1.4142135623730951},"173":{"tf":2.0},"192":{"tf":1.0},"83":{"tf":1.0}},"df":8}}}}},"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{"53":{"tf":1.0},"68":{"tf":1.0}},"df":2,"a":{"docs":{},"df":0,"r":{"docs":{"34":{"tf":1.0},"54":{"tf":1.0}},"df":2}}}}},"r":{"docs":{},"df":0,"t":{"docs":{"11":{"tf":1.4142135623730951},"119":{"tf":1.0},"142":{"tf":1.0},"153":{"tf":1.4142135623730951},"154":{"tf":2.449489742783178},"175":{"tf":1.0},"47":{"tf":1.7320508075688772},"64":{"tf":1.4142135623730951},"68":{"tf":1.0},"76":{"tf":2.0},"88":{"tf":2.0}},"df":11,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"a":{"docs":{"154":{"tf":1.0}},"df":1}}},"a":{"docs":{"153":{"tf":1.4142135623730951},"76":{"tf":1.7320508075688772}},"df":2,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{"76":{"tf":1.4142135623730951}},"df":1}}}}}}}},"b":{"docs":{},"df":0,"l":{"docs":{"114":{"tf":1.4142135623730951},"69":{"tf":1.0}},"df":2}},"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"153":{"tf":1.7320508075688772}},"df":1}}}},"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"87":{"tf":1.4142135623730951}},"df":1}}}}},"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"82":{"tf":1.0},"84":{"tf":1.0},"92":{"tf":1.0}},"df":3},"x":{"docs":{"13":{"tf":1.0}},"df":1}},"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"101":{"tf":1.0},"102":{"tf":1.0},"103":{"tf":1.0},"113":{"tf":1.0},"124":{"tf":1.0},"126":{"tf":1.0},"133":{"tf":1.0},"134":{"tf":1.0},"136":{"tf":1.4142135623730951},"146":{"tf":1.0},"158":{"tf":1.0},"186":{"tf":1.0},"2":{"tf":1.0},"72":{"tf":1.0},"80":{"tf":1.7320508075688772},"82":{"tf":1.0},"84":{"tf":1.4142135623730951}},"df":17}}}},"t":{"docs":{"161":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{"102":{"tf":1.0},"114":{"tf":1.0},"115":{"tf":1.0},"127":{"tf":1.0},"134":{"tf":1.0},"136":{"tf":1.4142135623730951},"137":{"tf":1.0},"80":{"tf":1.0}},"df":8}}}}},"w":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"160":{"tf":1.4142135623730951},"163":{"tf":1.0},"166":{"tf":1.0},"75":{"tf":1.0},"76":{"tf":1.4142135623730951},"86":{"tf":1.0}},"df":6}}}},"r":{"docs":{"6":{"tf":1.0}},"df":1,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"164":{"tf":1.0},"2":{"tf":1.0}},"df":2}}}}},"e":{"docs":{"125":{"tf":1.0},"69":{"tf":1.0}},"df":2,"c":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{"11":{"tf":1.0},"124":{"tf":1.0},"66":{"tf":1.0}},"df":3}}},"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"138":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":1.0}},"df":3}}}},"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{"123":{"tf":1.0},"84":{"tf":1.0}},"df":2,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"80":{"tf":1.0}},"df":1}},"v":{"docs":{"130":{"tf":1.0}},"df":1}}}}}},"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"53":{"tf":1.0}},"df":1}},"i":{"docs":{},"df":0,"x":{"docs":{"174":{"tf":1.0}},"df":1,"=":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{"174":{"tf":1.0}},"df":1}}}}}}},"p":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"157":{"tf":1.7320508075688772},"158":{"tf":1.0},"160":{"tf":1.7320508075688772}},"df":3}}}}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"q":{"docs":{},"df":0,"u":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"3":{"tf":1.0},"39":{"tf":1.0}},"df":2}}}}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"161":{"tf":1.0},"175":{"tf":1.0},"93":{"tf":1.0}},"df":3}},"r":{"docs":{},"df":0,"v":{"docs":{"166":{"tf":1.0}},"df":1}}},"s":{"docs":{"69":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{"29":{"tf":1.0},"31":{"tf":1.0},"67":{"tf":1.0},"71":{"tf":1.4142135623730951},"72":{"tf":1.0},"77":{"tf":1.0},"80":{"tf":1.0}},"df":7}}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"103":{"tf":1.0},"127":{"tf":1.0},"14":{"tf":1.0},"155":{"tf":1.0},"163":{"tf":1.0},"80":{"tf":1.0},"84":{"tf":1.0},"87":{"tf":1.0}},"df":8}}},"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{"103":{"tf":1.0},"125":{"tf":1.0},"67":{"tf":1.4142135623730951},"80":{"tf":1.0}},"df":4}}}}},"i":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"34":{"tf":1.0}},"df":1}}}}},"i":{"docs":{},"df":0,"t":{"docs":{"123":{"tf":1.0},"126":{"tf":1.0},"129":{"tf":1.0},"13":{"tf":1.4142135623730951},"130":{"tf":1.0},"131":{"tf":1.0},"158":{"tf":1.0},"16":{"tf":1.0},"165":{"tf":1.4142135623730951},"168":{"tf":1.4142135623730951},"173":{"tf":1.0}},"df":11}}},"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{"190":{"tf":1.0},"86":{"tf":1.0}},"df":2}}}},"t":{"docs":{"161":{"tf":1.0},"166":{"tf":1.0},"187":{"tf":1.7320508075688772},"29":{"tf":1.0},"31":{"tf":1.0},"62":{"tf":1.7320508075688772},"63":{"tf":1.4142135623730951},"64":{"tf":2.0},"68":{"tf":1.7320508075688772},"79":{"tf":1.4142135623730951},"81":{"tf":1.0},"83":{"tf":1.4142135623730951}},"df":12,"l":{"docs":{},"df":0,"n":{"docs":{"104":{"tf":1.0},"191":{"tf":1.0}},"df":2,"!":{"docs":{},"df":0,"(":{"docs":{},"df":0,"\\"":{"docs":{},"df":0,"f":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"191":{"tf":1.0}},"df":1}}}}},"t":{"docs":{},"df":0,"h":{"docs":{"191":{"tf":1.4142135623730951}},"df":1}},"{":{"docs":{},"df":0,":":{"docs":{},"df":0,"p":{"docs":{"166":{"tf":2.23606797749979}},"df":1}}}}}}}}}},"o":{"docs":{},"df":0,"r":{"docs":{"3":{"tf":1.0},"40":{"tf":1.0}},"df":2,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{"128":{"tf":1.0},"84":{"tf":1.7320508075688772}},"df":2}}}}},"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"g":{"docs":{"44":{"tf":1.0}},"df":1}}}}}},"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"v":{"docs":{"136":{"tf":1.0}},"df":1}}}}},"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"160":{"tf":1.0},"163":{"tf":1.0}},"df":2}}},"e":{"docs":{"24":{"tf":2.23606797749979},"26":{"tf":2.0},"29":{"tf":1.0},"32":{"tf":1.4142135623730951},"33":{"tf":2.23606797749979},"34":{"tf":1.0},"36":{"tf":1.0}},"df":7},"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{"121":{"tf":1.0},"123":{"tf":1.4142135623730951},"126":{"tf":1.0},"134":{"tf":1.0},"136":{"tf":1.0},"161":{"tf":1.0},"184":{"tf":1.0},"187":{"tf":1.0},"53":{"tf":1.0},"92":{"tf":1.7320508075688772},"93":{"tf":1.0},"98":{"tf":1.0}},"df":12}}}},"c":{"docs":{},"df":0,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{"78":{"tf":1.0}},"df":1}}}}}},"e":{"docs":{"134":{"tf":1.0},"68":{"tf":1.0}},"df":2,"d":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"160":{"tf":1.4142135623730951}},"df":1}}},"s":{"docs":{},"df":0,"s":{"docs":{"100":{"tf":1.0},"104":{"tf":1.0},"121":{"tf":1.0},"123":{"tf":1.7320508075688772},"131":{"tf":1.4142135623730951},"15":{"tf":1.0},"158":{"tf":1.0},"160":{"tf":1.0},"161":{"tf":1.0},"163":{"tf":1.0},"174":{"tf":1.0},"184":{"tf":1.0},"191":{"tf":1.0},"20":{"tf":1.0},"24":{"tf":1.4142135623730951},"29":{"tf":1.0},"49":{"tf":1.0},"53":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"60":{"tf":1.0},"63":{"tf":1.0},"64":{"tf":2.0},"65":{"tf":1.4142135623730951},"67":{"tf":1.0},"68":{"tf":1.7320508075688772},"77":{"tf":1.0},"81":{"tf":1.0},"86":{"tf":1.4142135623730951},"87":{"tf":1.4142135623730951}},"df":30,"(":{"docs":{},"df":0,"*":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"163":{"tf":1.0}},"df":1}}},"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"[":{"docs":{},"df":0,"i":{"docs":{"163":{"tf":1.0}},"df":1}}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"121":{"tf":1.0}},"df":1}}}}}},"o":{"docs":{},"df":0,"r":{"docs":{"11":{"tf":1.0},"131":{"tf":1.0},"16":{"tf":1.0},"194":{"tf":1.0},"30":{"tf":1.0},"32":{"tf":1.0},"49":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.7320508075688772},"80":{"tf":1.4142135623730951},"83":{"tf":1.0},"86":{"tf":1.7320508075688772},"90":{"tf":1.0}},"df":13}}}}}},"d":{"docs":{},"df":0,"u":{"docs":{},"df":0,"c":{"docs":{"174":{"tf":1.0},"178":{"tf":1.0},"190":{"tf":1.0},"63":{"tf":1.0}},"df":4,"t":{"docs":{"114":{"tf":1.0}},"df":1}}}},"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"30":{"tf":1.0},"36":{"tf":1.0}},"df":2}}}}}},"i":{"docs":{},"df":0,"l":{"docs":{"187":{"tf":2.0},"188":{"tf":1.4142135623730951},"189":{"tf":1.0},"78":{"tf":2.23606797749979}},"df":4,"e":{"docs":{},"df":0,".":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"v":{"docs":{},"df":0,".":{"docs":{},"df":0,"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{"188":{"tf":1.4142135623730951}},"df":1,"e":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"x":{"docs":{"188":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{"187":{"tf":1.0},"190":{"tf":1.0}},"df":2,"e":{"docs":{},"df":0,".":{"docs":{},"df":0,"o":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{"190":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}},"g":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"0":{"tf":1.0},"1":{"tf":1.0},"103":{"tf":1.4142135623730951},"104":{"tf":1.0},"114":{"tf":1.0},"12":{"tf":2.0},"120":{"tf":2.0},"121":{"tf":1.4142135623730951},"122":{"tf":1.0},"132":{"tf":1.0},"133":{"tf":1.7320508075688772},"138":{"tf":1.0},"14":{"tf":1.4142135623730951},"16":{"tf":1.0},"186":{"tf":1.4142135623730951},"187":{"tf":1.0},"189":{"tf":1.0},"19":{"tf":1.0},"20":{"tf":1.7320508075688772},"22":{"tf":1.0},"24":{"tf":1.4142135623730951},"27":{"tf":1.0},"28":{"tf":1.0},"3":{"tf":1.0},"31":{"tf":1.0},"32":{"tf":1.0},"34":{"tf":1.4142135623730951},"4":{"tf":1.0},"43":{"tf":1.7320508075688772},"5":{"tf":1.4142135623730951},"53":{"tf":1.0},"54":{"tf":1.4142135623730951},"55":{"tf":1.0},"60":{"tf":3.0},"61":{"tf":1.0},"62":{"tf":2.23606797749979},"63":{"tf":2.0},"64":{"tf":2.23606797749979},"67":{"tf":1.0},"68":{"tf":3.0},"71":{"tf":1.0},"77":{"tf":2.23606797749979},"78":{"tf":2.23606797749979},"81":{"tf":1.0},"83":{"tf":2.23606797749979},"92":{"tf":1.0},"98":{"tf":1.0},"99":{"tf":1.0}},"df":48,"m":{"docs":{"11":{"tf":1.0},"114":{"tf":1.0},"167":{"tf":1.0},"199":{"tf":1.0},"36":{"tf":1.0},"84":{"tf":1.0}},"df":6,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"’":{"docs":{"165":{"tf":1.0}},"df":1}}}},"’":{"docs":{"15":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"170":{"tf":1.0}},"df":1}}}}},"j":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"169":{"tf":1.0},"170":{"tf":1.4142135623730951},"171":{"tf":1.0},"174":{"tf":1.4142135623730951},"175":{"tf":1.4142135623730951},"176":{"tf":1.4142135623730951},"177":{"tf":1.4142135623730951},"178":{"tf":1.4142135623730951},"179":{"tf":1.4142135623730951},"180":{"tf":1.0},"183":{"tf":1.0},"184":{"tf":1.4142135623730951},"2":{"tf":1.0},"20":{"tf":1.4142135623730951},"40":{"tf":1.0},"50":{"tf":1.0},"55":{"tf":1.7320508075688772},"56":{"tf":2.0},"57":{"tf":2.0},"58":{"tf":2.0},"61":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.7320508075688772},"9":{"tf":1.0}},"df":24,".":{"docs":{},"df":0,"h":{"docs":{"184":{"tf":1.0}},"df":1}}}}}},"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"24":{"tf":1.0}},"df":1},"s":{"docs":{"122":{"tf":1.0}},"df":1}}},"n":{"docs":{},"df":0,"e":{"docs":{"174":{"tf":1.0}},"df":1}},"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"177":{"tf":1.0},"80":{"tf":1.0}},"df":2,"l":{"docs":{},"df":0,"i":{"docs":{"103":{"tf":1.0},"104":{"tf":1.0},"80":{"tf":1.0}},"df":3}},"t":{"docs":{},"df":0,"i":{"docs":{"103":{"tf":1.0},"154":{"tf":1.0},"16":{"tf":1.0}},"df":3}}}}},"s":{"docs":{},"df":0,"e":{"docs":{"9":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"126":{"tf":1.0},"15":{"tf":1.0},"17":{"tf":1.0}},"df":3}}},"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{"166":{"tf":1.0},"192":{"tf":1.0},"196":{"tf":1.0},"32":{"tf":1.0}},"df":4}}}}},"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{"1":{"tf":1.0},"114":{"tf":1.0},"115":{"tf":1.4142135623730951},"117":{"tf":1.4142135623730951},"118":{"tf":1.0},"120":{"tf":1.0},"123":{"tf":1.0},"124":{"tf":1.7320508075688772},"125":{"tf":1.0},"126":{"tf":1.4142135623730951},"127":{"tf":1.0},"129":{"tf":1.0},"13":{"tf":1.4142135623730951},"130":{"tf":1.4142135623730951},"132":{"tf":1.0},"14":{"tf":1.4142135623730951},"142":{"tf":1.4142135623730951},"146":{"tf":1.4142135623730951},"147":{"tf":1.0},"148":{"tf":1.4142135623730951},"15":{"tf":1.4142135623730951},"151":{"tf":1.0},"153":{"tf":1.0},"154":{"tf":1.0},"155":{"tf":2.23606797749979},"158":{"tf":1.4142135623730951},"16":{"tf":1.0},"160":{"tf":1.0},"161":{"tf":1.4142135623730951},"163":{"tf":1.0},"164":{"tf":1.0},"166":{"tf":1.0},"168":{"tf":1.4142135623730951},"173":{"tf":1.4142135623730951},"175":{"tf":1.0},"178":{"tf":1.4142135623730951},"186":{"tf":1.0},"193":{"tf":1.0},"195":{"tf":1.0},"197":{"tf":1.4142135623730951},"26":{"tf":1.0},"27":{"tf":1.0},"28":{"tf":1.0},"29":{"tf":1.0},"30":{"tf":1.0},"33":{"tf":1.0},"36":{"tf":1.0},"49":{"tf":1.0},"5":{"tf":1.0},"50":{"tf":1.4142135623730951},"60":{"tf":1.4142135623730951},"70":{"tf":1.4142135623730951},"77":{"tf":1.0},"80":{"tf":1.7320508075688772},"82":{"tf":1.0},"84":{"tf":1.0},"9":{"tf":1.4142135623730951},"93":{"tf":1.0}},"df":58}}}}},"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"n":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"133":{"tf":1.0}},"df":1}}}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"v":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"0":{"docs":{},"df":0,"x":{"docs":{},"df":0,"3":{"docs":{},"df":0,"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"_":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{"83":{"tf":1.0}},"df":1}}}}}}}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"_":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"g":{"docs":{"101":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"u":{"docs":{},"df":0,"b":{"docs":{"104":{"tf":2.449489742783178},"107":{"tf":2.23606797749979},"108":{"tf":2.23606797749979},"109":{"tf":2.8284271247461903},"112":{"tf":1.0},"125":{"tf":1.4142135623730951},"146":{"tf":2.0},"153":{"tf":2.6457513110645907},"154":{"tf":2.23606797749979},"155":{"tf":4.47213595499958},"158":{"tf":1.4142135623730951},"173":{"tf":2.8284271247461903},"183":{"tf":1.0},"184":{"tf":1.0},"64":{"tf":1.0},"72":{"tf":1.0},"91":{"tf":2.0},"92":{"tf":2.0},"93":{"tf":3.0}},"df":19,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"173":{"tf":1.0},"98":{"tf":1.0}},"df":2,"l":{"docs":{},"df":0,"i":{"docs":{"170":{"tf":1.0}},"df":1}}}}}},"l":{"docs":{},"df":0,"l":{"docs":{"106":{"tf":2.449489742783178},"107":{"tf":2.23606797749979},"108":{"tf":2.0},"109":{"tf":1.4142135623730951},"188":{"tf":1.0},"72":{"tf":1.0},"8":{"tf":1.0}},"df":7,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"n":{"docs":{"155":{"tf":1.7320508075688772}},"df":1}}}},"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"109":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"109":{"tf":1.0}},"df":1}}}}}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"_":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"109":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}},"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{"109":{"tf":1.7320508075688772},"111":{"tf":1.0}},"df":2}}}},"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"109":{"tf":1.7320508075688772}},"df":1}}}}},"u":{"docs":{},"df":0,"p":{"docs":{"155":{"tf":1.7320508075688772}},"df":1}}}},"n":{"docs":{"60":{"tf":1.0}},"df":1},"r":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{"7":{"tf":1.0}},"df":1}}}},"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{"11":{"tf":1.0},"13":{"tf":1.0},"157":{"tf":1.0},"161":{"tf":1.0},"168":{"tf":1.0},"7":{"tf":1.0}},"df":6}}}},"s":{"docs":{},"df":0,"h":{"docs":{"134":{"tf":1.0},"83":{"tf":1.0}},"df":2,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{"155":{"tf":1.7320508075688772}},"df":1}}}}},"t":{"docs":{"101":{"tf":1.0},"123":{"tf":1.0},"183":{"tf":1.0},"61":{"tf":1.0},"76":{"tf":1.0},"78":{"tf":1.0},"82":{"tf":1.0}},"df":7}},"w":{"docs":{},"df":0,"m":{"docs":{"72":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"_":{"docs":{},"df":0,"2":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,".":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"|":{"docs":{},"df":0,"w":{"docs":{"72":{"tf":1.0}},"df":1}}}}}}}}}}}},"t":{"docs":{},"df":0,"l":{"docs":{},"df":0,".":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"|":{"docs":{},"df":0,"w":{"docs":{"72":{"tf":1.0}},"df":1}}}}}}}}}}}},"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,".":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"|":{"docs":{},"df":0,"w":{"docs":{"72":{"tf":1.0}},"df":1}}}}}}}}}}}}},"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,".":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"|":{"docs":{},"df":0,"w":{"docs":{"72":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}},"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"l":{"docs":{},"df":0,".":{"docs":{},"df":0,"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"f":{"docs":{},"df":0,"y":{"docs":{},"df":0,"(":{"docs":{},"df":0,"|":{"docs":{},"df":0,"r":{"docs":{"75":{"tf":1.0}},"df":1}}}}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"g":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"s":{"docs":{},"df":0,"y":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"0":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"73":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}},"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"|":{"docs":{},"df":0,"w":{"docs":{"72":{"tf":1.0},"74":{"tf":1.0}},"df":2}}}}}}}}}}}},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,".":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"|":{"docs":{},"df":0,"w":{"docs":{"72":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}},"0":{"docs":{"72":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"l":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"75":{"tf":1.0}},"df":1}}}},"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{"75":{"tf":1.0}},"df":1}}}}}}}}}}}}}},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"75":{"tf":1.0}},"df":1}}}},"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{"75":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}},"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"l":{"docs":{},"df":0,"_":{"docs":{},"df":0,"g":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"s":{"docs":{},"df":0,"y":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"0":{"docs":{"75":{"tf":1.0}},"df":1}}}}}}}}}}}}}}},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"p":{"docs":{},"df":0,"w":{"docs":{},"df":0,"m":{"docs":{},"df":0,"4":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"75":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}},"x":{"docs":{"166":{"tf":1.4142135623730951}},"df":1},"y":{"docs":{"166":{"tf":1.4142135623730951}},"df":1},"z":{"docs":{"166":{"tf":1.4142135623730951}},"df":1}},"q":{"docs":{"64":{"tf":1.0},"68":{"tf":1.0}},"df":2,"2":{"docs":{"49":{"tf":1.0}},"df":1},"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{"19":{"tf":1.0},"191":{"tf":1.0},"22":{"tf":1.4142135623730951},"43":{"tf":2.0},"46":{"tf":1.4142135623730951},"47":{"tf":1.4142135623730951},"51":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"61":{"tf":1.0},"63":{"tf":4.69041575982343},"64":{"tf":3.3166247903554},"67":{"tf":1.7320508075688772},"68":{"tf":1.4142135623730951},"77":{"tf":2.8284271247461903},"79":{"tf":1.0},"81":{"tf":1.7320508075688772},"83":{"tf":1.4142135623730951}},"df":18}}},"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{"151":{"tf":1.4142135623730951},"91":{"tf":1.4142135623730951}},"df":2}}}}},"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"5":{"tf":1.0}},"df":1}}}}}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"190":{"tf":1.0},"56":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.7320508075688772},"67":{"tf":1.0}},"df":5,"/":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"/":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,".":{"docs":{},"df":0,"z":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{"59":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}},"t":{"docs":{"127":{"tf":1.0},"64":{"tf":1.0},"68":{"tf":1.7320508075688772}},"df":3}}}},"r":{"docs":{"155":{"tf":3.4641016151377544},"73":{"tf":1.0},"75":{"tf":1.4142135623730951}},"df":3,"0":{"docs":{"62":{"tf":1.7320508075688772},"83":{"tf":3.0}},"df":2},"1":{"docs":{"83":{"tf":1.0}},"df":1,"2":{"docs":{"83":{"tf":1.0}},"df":1}},"2":{"docs":{"83":{"tf":1.0}},"df":1},"3":{"docs":{"83":{"tf":1.0}},"df":1},"4":{"docs":{"62":{"tf":1.0}},"df":1},"7":{"docs":{"62":{"tf":1.4142135623730951}},"df":1},"_":{"docs":{},"df":0,"n":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{"168":{"tf":1.0}},"df":1}}}},"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{"103":{"tf":1.0},"122":{"tf":1.7320508075688772},"123":{"tf":1.4142135623730951},"80":{"tf":1.0}},"df":4}},"i":{"docs":{},"df":0,"s":{"docs":{"103":{"tf":1.0},"81":{"tf":1.0}},"df":2}},"m":{"docs":{"11":{"tf":1.0},"112":{"tf":1.0},"12":{"tf":1.4142135623730951},"61":{"tf":1.4142135623730951},"66":{"tf":2.449489742783178},"67":{"tf":1.0},"69":{"tf":1.0},"86":{"tf":1.7320508075688772},"87":{"tf":2.0},"92":{"tf":1.0}},"df":10,"/":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"13":{"tf":1.0}},"df":1}}}},"2":{"docs":{"61":{"tf":1.4142135623730951}},"df":1,"b":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"61":{"tf":1.7320508075688772}},"df":1}}}}},"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"17":{"tf":1.0}},"df":1}}},"g":{"docs":{"12":{"tf":1.4142135623730951},"30":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.0},"78":{"tf":1.0},"87":{"tf":1.4142135623730951}},"df":6}},"r":{"docs":{},"df":0,"e":{"docs":{"165":{"tf":1.0}},"df":1}},"s":{"docs":{},"df":0,"p":{"docs":{},"df":0,"b":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"12":{"tf":1.0}},"df":1}}}}}}},"t":{"docs":{},"df":0,"e":{"docs":{"11":{"tf":1.0},"68":{"tf":1.4142135623730951},"69":{"tf":1.0},"76":{"tf":2.23606797749979}},"df":4},"i":{"docs":{},"df":0,"o":{"docs":{"137":{"tf":1.0}},"df":1}}},"w":{"docs":{"146":{"tf":1.4142135623730951},"147":{"tf":1.0},"150":{"tf":1.0},"164":{"tf":1.4142135623730951},"165":{"tf":1.0},"166":{"tf":1.0},"173":{"tf":1.0},"62":{"tf":1.0},"76":{"tf":1.4142135623730951},"83":{"tf":1.0}},"df":10}},"e":{"docs":{"115":{"tf":1.0},"123":{"tf":1.0},"166":{"tf":1.0},"84":{"tf":1.7320508075688772},"87":{"tf":1.0},"91":{"tf":1.4142135623730951}},"df":6,")":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"62":{"tf":1.0}},"df":1}}}}}}},"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"107":{"tf":1.0},"53":{"tf":1.0},"64":{"tf":1.0},"81":{"tf":1.0}},"df":4}},"d":{"docs":{"101":{"tf":1.0},"109":{"tf":1.0},"121":{"tf":1.0},"122":{"tf":1.0},"123":{"tf":1.0},"124":{"tf":1.0},"127":{"tf":1.0},"164":{"tf":1.0},"165":{"tf":1.0},"199":{"tf":1.0},"28":{"tf":1.0},"3":{"tf":1.4142135623730951},"5":{"tf":1.0},"53":{"tf":1.0},"63":{"tf":1.0},"68":{"tf":1.4142135623730951},"7":{"tf":1.0},"70":{"tf":1.0},"73":{"tf":1.4142135623730951},"74":{"tf":1.0},"75":{"tf":1.4142135623730951},"83":{"tf":1.7320508075688772},"87":{"tf":1.0},"92":{"tf":2.0},"95":{"tf":1.7320508075688772}},"df":25,"_":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"(":{"docs":{},"df":0,"g":{"docs":{},"df":0,"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{"102":{"tf":1.0}},"df":1}}}}}}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"s":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"121":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}},"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"_":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"122":{"tf":1.0},"123":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":1.0},"126":{"tf":1.0}},"df":5}}}}}}}}}}},"p":{"docs":{},"df":0,"e":{"docs":{"101":{"tf":1.4142135623730951}},"df":1}}},"v":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"165":{"tf":1.0}},"df":1}}}}}}}},"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"163":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"r":{"docs":{"2":{"tf":1.4142135623730951}},"df":1}},"i":{"docs":{"104":{"tf":1.0},"115":{"tf":1.0}},"df":2},"o":{"docs":{},"df":0,"b":{"docs":{},"df":0,"j":{"docs":{"62":{"tf":1.4142135623730951}},"df":1}}}},"l":{"docs":{"112":{"tf":1.0},"119":{"tf":1.0},"128":{"tf":1.0},"130":{"tf":1.0},"138":{"tf":1.0},"165":{"tf":1.0},"30":{"tf":1.0},"32":{"tf":1.0},"63":{"tf":1.0},"65":{"tf":1.0},"81":{"tf":1.0},"87":{"tf":1.4142135623730951},"94":{"tf":1.0}},"df":13,"l":{"docs":{},"df":0,"i":{"docs":{"101":{"tf":1.4142135623730951},"125":{"tf":1.0},"62":{"tf":1.0},"75":{"tf":1.0}},"df":4},"o":{"docs":{},"df":0,"c":{"docs":{"134":{"tf":1.4142135623730951},"137":{"tf":1.0},"138":{"tf":1.7320508075688772}},"df":3}}}},"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"103":{"tf":1.0},"115":{"tf":1.0},"123":{"tf":1.4142135623730951},"137":{"tf":1.0},"60":{"tf":1.0},"67":{"tf":1.0},"7":{"tf":1.0},"84":{"tf":1.4142135623730951}},"df":8}}}},"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"d":{"docs":{"175":{"tf":1.0}},"df":1}}}}},"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"i":{"docs":{},"df":0,"v":{"docs":{"200":{"tf":1.0},"201":{"tf":1.0},"76":{"tf":1.0}},"df":3}},"n":{"docs":{},"df":0,"t":{"docs":{"19":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"141":{"tf":1.0},"171":{"tf":1.0},"173":{"tf":1.0},"187":{"tf":1.0},"19":{"tf":1.0},"190":{"tf":1.0},"70":{"tf":1.0}},"df":7}}}}},"n":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"155":{"tf":1.0}},"df":1}}}}}}}},"d":{"docs":{"77":{"tf":1.7320508075688772}},"df":1,"u":{"docs":{},"df":0,"c":{"docs":{"103":{"tf":1.0},"128":{"tf":1.0},"137":{"tf":1.0},"154":{"tf":1.0},"190":{"tf":1.0},"26":{"tf":1.0}},"df":6,"t":{"docs":{"188":{"tf":1.0}},"df":1}}}},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{"80":{"tf":1.7320508075688772}},"df":1},"t":{"docs":{"80":{"tf":1.7320508075688772}},"df":1}}}}}},"x":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"142":{"tf":1.4142135623730951},"147":{"tf":1.4142135623730951}},"df":2}}}}}},"f":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"126":{"tf":1.4142135623730951},"127":{"tf":2.8284271247461903},"128":{"tf":1.0}},"df":3}}},"e":{"docs":{},"df":0,"r":{"docs":{"101":{"tf":1.7320508075688772},"102":{"tf":1.0},"112":{"tf":1.0},"122":{"tf":1.4142135623730951},"126":{"tf":1.4142135623730951},"127":{"tf":2.449489742783178},"128":{"tf":1.0},"158":{"tf":1.4142135623730951},"164":{"tf":2.23606797749979},"166":{"tf":1.7320508075688772},"173":{"tf":1.0},"174":{"tf":1.0},"176":{"tf":1.0},"196":{"tf":1.0},"66":{"tf":1.4142135623730951},"67":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.0},"71":{"tf":1.0},"80":{"tf":1.0},"84":{"tf":1.0},"90":{"tf":1.4142135623730951},"93":{"tf":1.0},"95":{"tf":1.0},"96":{"tf":1.0},"98":{"tf":1.0}},"df":26}},"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"134":{"tf":1.0},"62":{"tf":1.0}},"df":2}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{"62":{"tf":1.0},"67":{"tf":1.0}},"df":2}}}}},"g":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{"114":{"tf":1.0}},"df":1,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"147":{"tf":1.0},"21":{"tf":1.0}},"df":2}}}}}}},"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"133":{"tf":1.0},"61":{"tf":1.7320508075688772},"66":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.0},"87":{"tf":1.0}},"df":6}},"s":{"docs":{},"df":0,"t":{"docs":{"107":{"tf":1.7320508075688772},"108":{"tf":1.0},"112":{"tf":1.7320508075688772},"117":{"tf":1.0},"142":{"tf":1.0},"144":{"tf":1.0},"147":{"tf":1.4142135623730951},"164":{"tf":1.4142135623730951},"165":{"tf":1.0},"193":{"tf":1.0},"195":{"tf":1.0},"197":{"tf":1.0},"28":{"tf":1.4142135623730951},"32":{"tf":1.0},"69":{"tf":1.7320508075688772},"72":{"tf":1.4142135623730951},"73":{"tf":1.4142135623730951},"74":{"tf":1.7320508075688772},"75":{"tf":1.7320508075688772},"83":{"tf":1.7320508075688772},"88":{"tf":1.4142135623730951},"90":{"tf":2.23606797749979},"91":{"tf":1.0},"92":{"tf":1.0}},"df":24,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"93":{"tf":1.7320508075688772}},"df":1}}}}}}},"r":{"docs":{},"df":0,"y":{"docs":{},"df":0,"/":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"x":{"docs":{"64":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}},"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"128":{"tf":1.0},"130":{"tf":1.0},"160":{"tf":1.0},"180":{"tf":1.0}},"df":4,"l":{"docs":{},"df":0,"i":{"docs":{"120":{"tf":1.0}},"df":1}}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"115":{"tf":1.0}},"df":1}}}}}},"l":{"docs":{"177":{"tf":1.0}},"df":1,"a":{"docs":{},"df":0,"t":{"docs":{"191":{"tf":1.0}},"df":1},"x":{"docs":{"124":{"tf":1.0}},"df":1}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{"126":{"tf":2.0},"146":{"tf":1.0},"159":{"tf":1.0},"187":{"tf":1.4142135623730951},"189":{"tf":1.4142135623730951},"190":{"tf":1.0},"50":{"tf":1.0},"53":{"tf":1.0},"62":{"tf":1.7320508075688772},"63":{"tf":1.4142135623730951},"78":{"tf":1.7320508075688772},"81":{"tf":1.0},"83":{"tf":1.0}},"df":13}},"v":{"docs":{"188":{"tf":1.0},"80":{"tf":1.0},"9":{"tf":1.0}},"df":3}},"i":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"35":{"tf":1.0},"95":{"tf":1.0}},"df":2}}}},"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"44":{"tf":1.4142135623730951},"90":{"tf":1.0}},"df":2,"_":{"docs":{},"df":0,"v":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"u":{"docs":{"93":{"tf":1.0}},"df":1}}}}}}},"c":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"135":{"tf":1.0},"137":{"tf":1.0}},"df":2}}}}},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"127":{"tf":1.0},"65":{"tf":1.0},"87":{"tf":1.4142135623730951}},"df":3}}},"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"b":{"docs":{"67":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"t":{"docs":{"19":{"tf":1.0},"31":{"tf":1.0},"64":{"tf":2.23606797749979},"68":{"tf":2.23606797749979}},"df":4},"v":{"docs":{"158":{"tf":1.4142135623730951},"63":{"tf":1.0},"67":{"tf":1.0},"86":{"tf":1.0}},"df":4}}},"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"78":{"tf":1.0}},"df":1}},"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"112":{"tf":1.0}},"df":1}}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"124":{"tf":1.0}},"df":1}}}}},"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"163":{"tf":1.0}},"df":1}}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{"126":{"tf":1.0},"127":{"tf":1.0},"55":{"tf":1.0},"63":{"tf":1.0}},"df":4,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"99":{"tf":1.0}},"df":1}}}}}}}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"137":{"tf":1.0},"53":{"tf":1.0},"62":{"tf":1.0},"68":{"tf":1.0},"8":{"tf":1.0}},"df":5}},"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"58":{"tf":1.0},"6":{"tf":1.4142135623730951},"8":{"tf":1.0}},"df":3}}}}}}},"r":{"docs":{"166":{"tf":1.0}},"df":1,"(":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"n":{"docs":{},"df":0,"(":{"docs":{},"df":0,"4":{"docs":{},"df":0,"0":{"docs":{},"df":0,"9":{"docs":{},"df":0,"6":{"docs":{"166":{"tf":1.0}},"df":1}}}},"n":{"docs":{"166":{"tf":2.0}},"df":1}}}}}}},"c":{"docs":{"166":{"tf":2.0},"173":{"tf":1.7320508075688772},"91":{"tf":1.4142135623730951},"92":{"tf":1.0},"93":{"tf":1.0}},"df":5},"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"166":{"tf":2.449489742783178}},"df":1}}}}},"e":{"docs":{},"df":0,"s":{"docs":{"104":{"tf":1.4142135623730951},"105":{"tf":1.0},"106":{"tf":1.0},"91":{"tf":1.0}},"df":4,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"111":{"tf":1.0},"166":{"tf":1.4142135623730951}},"df":2}}}}}}},"q":{"docs":{},"df":0,"u":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"53":{"tf":1.4142135623730951},"68":{"tf":1.4142135623730951},"8":{"tf":1.0},"84":{"tf":1.7320508075688772}},"df":4}}},"i":{"docs":{},"df":0,"r":{"docs":{"119":{"tf":1.0},"123":{"tf":1.0},"125":{"tf":2.8284271247461903},"126":{"tf":1.0},"127":{"tf":1.0},"128":{"tf":1.0},"133":{"tf":1.0},"134":{"tf":1.0},"139":{"tf":1.4142135623730951},"14":{"tf":1.0},"146":{"tf":1.0},"15":{"tf":1.0},"150":{"tf":1.0},"155":{"tf":1.0},"159":{"tf":1.0},"161":{"tf":1.4142135623730951},"165":{"tf":1.4142135623730951},"166":{"tf":1.0},"169":{"tf":1.0},"184":{"tf":1.0},"2":{"tf":1.0},"20":{"tf":1.4142135623730951},"24":{"tf":1.0},"27":{"tf":1.0},"53":{"tf":1.0},"54":{"tf":1.0},"60":{"tf":1.0},"61":{"tf":1.0},"63":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.4142135623730951},"76":{"tf":1.7320508075688772},"77":{"tf":1.0},"80":{"tf":1.0}},"df":34}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"122":{"tf":1.0},"146":{"tf":1.0},"165":{"tf":1.4142135623730951},"62":{"tf":1.0},"64":{"tf":2.6457513110645907}},"df":5,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"125":{"tf":1.0}},"df":1}}}}}},"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"83":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"+":{"docs":{},"df":0,"0":{"docs":{},"df":0,"x":{"docs":{},"df":0,"a":{"docs":{"83":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}},"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"106":{"tf":2.23606797749979},"107":{"tf":1.0},"108":{"tf":1.0}},"df":3}}},"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"c":{"docs":{"108":{"tf":1.0},"114":{"tf":1.0},"127":{"tf":1.0},"128":{"tf":1.4142135623730951},"3":{"tf":1.4142135623730951},"5":{"tf":1.7320508075688772},"8":{"tf":1.0},"80":{"tf":1.0}},"df":8}}}},"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"75":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{"165":{"tf":1.0},"80":{"tf":1.0},"87":{"tf":1.0}},"df":3}}}},"t":{"docs":{"157":{"tf":1.0},"180":{"tf":1.0},"183":{"tf":1.0},"99":{"tf":1.0}},"df":4,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"77":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"102":{"tf":1.0},"108":{"tf":1.0},"13":{"tf":1.0},"72":{"tf":1.0},"97":{"tf":1.0}},"df":5,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"/":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"12":{"tf":1.0}},"df":1}}}}}}}}}}}}}}},"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{"102":{"tf":1.0},"108":{"tf":1.7320508075688772},"134":{"tf":1.4142135623730951},"136":{"tf":1.4142135623730951},"137":{"tf":1.0},"153":{"tf":1.0},"177":{"tf":1.0},"187":{"tf":1.7320508075688772},"53":{"tf":1.0},"68":{"tf":1.0},"78":{"tf":1.0},"79":{"tf":1.0},"80":{"tf":1.7320508075688772},"83":{"tf":1.4142135623730951},"84":{"tf":1.0}},"df":15,"<":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{"108":{"tf":1.0}},"df":1}}}}}}}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"173":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"i":{"docs":{"124":{"tf":1.0}},"df":1}},"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"n":{"docs":{"108":{"tf":2.6457513110645907},"112":{"tf":1.0},"122":{"tf":1.0},"126":{"tf":1.0},"134":{"tf":1.4142135623730951},"136":{"tf":1.4142135623730951},"146":{"tf":2.0},"153":{"tf":1.0},"38":{"tf":1.0},"73":{"tf":1.4142135623730951},"76":{"tf":1.0},"83":{"tf":1.4142135623730951}},"df":12}}}},"u":{"docs":{},"df":0,"s":{"docs":{"105":{"tf":1.0}},"df":1}},"v":{"docs":{"44":{"tf":1.4142135623730951}},"df":1,"i":{"docs":{},"df":0,"s":{"docs":{"53":{"tf":1.0},"68":{"tf":2.449489742783178}},"df":2,"i":{"docs":{},"df":0,"t":{"docs":{"7":{"tf":1.0}},"df":1}}}}}},"f":{"docs":{},"df":0,"c":{"docs":{"18":{"tf":1.0}},"df":1}},"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{},"df":0,"t":{"docs":{"189":{"tf":1.0},"20":{"tf":1.0},"52":{"tf":1.0},"61":{"tf":1.4142135623730951},"77":{"tf":1.4142135623730951},"88":{"tf":1.0},"93":{"tf":1.0}},"df":7}}},"s":{"docs":{},"df":0,"c":{"docs":{"30":{"tf":1.0},"35":{"tf":1.0}},"df":2},"e":{"docs":{"122":{"tf":1.0},"127":{"tf":1.0}},"df":2}}},"o":{"docs":{"92":{"tf":1.0},"93":{"tf":1.0}},"df":2,"<":{"docs":{},"df":0,"u":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{"92":{"tf":1.0},"93":{"tf":1.0}},"df":2}}}},"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"35":{"tf":1.0}},"df":1}}}},"d":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{"188":{"tf":1.4142135623730951},"62":{"tf":1.4142135623730951},"68":{"tf":1.4142135623730951}},"df":3}}}},"l":{"docs":{},"df":0,"e":{"docs":{"119":{"tf":1.0}},"df":1}},"m":{"docs":{"12":{"tf":1.0},"187":{"tf":1.0},"189":{"tf":1.0},"86":{"tf":1.4142135623730951},"87":{"tf":1.4142135623730951}},"df":5},"o":{"docs":{},"df":0,"m":{"docs":{"110":{"tf":1.0}},"df":1},"t":{"docs":{"147":{"tf":1.0},"178":{"tf":1.0},"191":{"tf":1.4142135623730951},"44":{"tf":1.7320508075688772},"53":{"tf":1.4142135623730951},"61":{"tf":1.0},"68":{"tf":1.4142135623730951}},"df":7}},"s":{"docs":{},"df":0,"e":{"docs":{"77":{"tf":1.7320508075688772}},"df":1}},"u":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"183":{"tf":1.0},"81":{"tf":1.0}},"df":2}}}},"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"114":{"tf":1.0},"69":{"tf":1.0},"71":{"tf":1.0}},"df":3}}}},"w":{"docs":{"11":{"tf":1.0}},"df":1}},"s":{"docs":{"24":{"tf":1.4142135623730951},"26":{"tf":1.7320508075688772},"29":{"tf":1.0},"33":{"tf":1.4142135623730951}},"df":4,"/":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"p":{"docs":{"57":{"tf":1.0}},"df":1}}},"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{},"df":0,"m":{"docs":{},"df":0,"t":{"docs":{},"df":0,".":{"docs":{},"df":0,"g":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"63":{"tf":1.0}},"df":1}}}}}}}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"/":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"b":{"docs":{},"df":0,"v":{"docs":{},"df":0,"7":{"docs":{},"df":0,"m":{"docs":{"63":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}},"2":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{"5":{"tf":1.0}},"df":1}}}},"t":{"docs":{"188":{"tf":1.7320508075688772},"60":{"tf":1.0},"64":{"tf":1.4142135623730951},"67":{"tf":1.0},"76":{"tf":1.0},"80":{"tf":1.0},"81":{"tf":1.0},"82":{"tf":1.0},"84":{"tf":1.0}},"df":9,"i":{"docs":{},"df":0,"c":{"docs":{"100":{"tf":1.4142135623730951},"128":{"tf":1.4142135623730951}},"df":2,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"p":{"docs":{},"df":0,"(":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"100":{"tf":1.0}},"df":1}}}}}}}}}}}}},"o":{"docs":{"130":{"tf":1.7320508075688772},"170":{"tf":1.7320508075688772}},"df":2,"s":{"docs":{},"df":0,"s":{"docs":{"130":{"tf":1.0}},"df":1}}},"t":{"docs":{"63":{"tf":1.7320508075688772}},"df":1}},"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{"109":{"tf":1.0},"173":{"tf":1.0},"44":{"tf":1.7320508075688772},"53":{"tf":1.0},"96":{"tf":1.0}},"df":5}},"n":{"docs":{"120":{"tf":1.4142135623730951},"121":{"tf":1.0},"123":{"tf":1.0},"128":{"tf":1.0},"130":{"tf":1.0},"136":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0},"17":{"tf":1.0},"177":{"tf":1.0},"189":{"tf":1.0},"19":{"tf":1.0},"191":{"tf":1.0},"194":{"tf":1.0},"22":{"tf":1.0},"31":{"tf":1.0},"4":{"tf":1.0},"44":{"tf":1.0},"53":{"tf":1.4142135623730951},"60":{"tf":1.0},"61":{"tf":1.0},"63":{"tf":3.4641016151377544},"64":{"tf":2.23606797749979},"67":{"tf":2.0},"68":{"tf":3.0},"76":{"tf":1.0},"77":{"tf":3.0},"79":{"tf":1.4142135623730951},"81":{"tf":2.449489742783178},"83":{"tf":1.0}},"df":30,"_":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"k":{"docs":{"165":{"tf":1.4142135623730951}},"df":1}}}}},"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"63":{"tf":2.0},"68":{"tf":2.449489742783178}},"df":2}}},"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{"102":{"tf":1.0},"103":{"tf":1.0},"108":{"tf":1.4142135623730951},"110":{"tf":1.4142135623730951},"111":{"tf":1.0},"112":{"tf":1.0},"113":{"tf":1.0},"125":{"tf":1.0},"127":{"tf":1.4142135623730951},"137":{"tf":1.0},"138":{"tf":1.4142135623730951},"15":{"tf":1.7320508075688772},"154":{"tf":1.0},"61":{"tf":1.0},"78":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0},"99":{"tf":1.0}},"df":18}}}},"s":{"docs":{},"df":0,"t":{"docs":{"0":{"tf":1.4142135623730951},"1":{"tf":1.7320508075688772},"101":{"tf":1.0},"104":{"tf":1.0},"105":{"tf":1.0},"107":{"tf":1.0},"114":{"tf":1.0},"120":{"tf":1.0},"122":{"tf":1.4142135623730951},"125":{"tf":2.23606797749979},"126":{"tf":1.7320508075688772},"129":{"tf":1.0},"130":{"tf":1.4142135623730951},"133":{"tf":1.0},"136":{"tf":1.0},"139":{"tf":1.0},"14":{"tf":1.0},"140":{"tf":1.0},"141":{"tf":1.4142135623730951},"142":{"tf":1.0},"151":{"tf":1.0},"156":{"tf":1.4142135623730951},"157":{"tf":1.0},"158":{"tf":1.7320508075688772},"159":{"tf":1.7320508075688772},"160":{"tf":2.8284271247461903},"161":{"tf":1.4142135623730951},"162":{"tf":1.0},"163":{"tf":1.4142135623730951},"164":{"tf":2.0},"165":{"tf":1.4142135623730951},"166":{"tf":1.7320508075688772},"167":{"tf":2.0},"168":{"tf":2.0},"169":{"tf":1.0},"170":{"tf":1.7320508075688772},"171":{"tf":2.23606797749979},"172":{"tf":2.23606797749979},"173":{"tf":2.23606797749979},"174":{"tf":1.7320508075688772},"175":{"tf":1.7320508075688772},"176":{"tf":1.0},"177":{"tf":1.0},"178":{"tf":1.0},"179":{"tf":2.0},"180":{"tf":1.0},"181":{"tf":1.4142135623730951},"182":{"tf":1.7320508075688772},"183":{"tf":1.7320508075688772},"184":{"tf":2.23606797749979},"19":{"tf":1.0},"190":{"tf":1.0},"2":{"tf":2.0},"20":{"tf":1.0},"21":{"tf":1.0},"24":{"tf":1.0},"26":{"tf":1.4142135623730951},"29":{"tf":1.4142135623730951},"3":{"tf":1.4142135623730951},"30":{"tf":1.0},"31":{"tf":1.4142135623730951},"33":{"tf":1.0},"4":{"tf":1.7320508075688772},"5":{"tf":3.605551275463989},"56":{"tf":1.4142135623730951},"60":{"tf":1.4142135623730951},"61":{"tf":1.4142135623730951},"63":{"tf":1.0},"69":{"tf":1.0},"70":{"tf":1.0},"72":{"tf":1.0},"78":{"tf":1.7320508075688772},"80":{"tf":1.0},"88":{"tf":1.0},"91":{"tf":1.7320508075688772},"92":{"tf":1.0},"94":{"tf":1.0},"96":{"tf":1.0},"98":{"tf":1.0}},"df":79,"_":{"docs":{},"df":0,"b":{"docs":{},"df":0,"e":{"docs":{},"df":0,"g":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"_":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"w":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"68":{"tf":1.0},"78":{"tf":1.0}},"df":2}}}}}}}}}}}},"f":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"183":{"tf":1.0},"184":{"tf":1.7320508075688772}},"df":2}}}}}},"c":{"docs":{"186":{"tf":1.0},"189":{"tf":1.0},"190":{"tf":1.0},"21":{"tf":1.0},"38":{"tf":1.7320508075688772},"62":{"tf":1.0}},"df":6},"f":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{"190":{"tf":1.4142135623730951}},"df":1}}}},"i":{"docs":{"24":{"tf":1.0},"33":{"tf":1.4142135623730951}},"df":2},"u":{"docs":{},"df":0,"p":{"docs":{"21":{"tf":1.0},"38":{"tf":2.8284271247461903},"39":{"tf":1.0},"61":{"tf":1.0},"67":{"tf":1.0}},"df":5}},"’":{"docs":{"103":{"tf":1.0},"109":{"tf":1.0},"164":{"tf":1.0},"173":{"tf":1.0},"5":{"tf":1.0}},"df":5}}}},"v":{"docs":{},"df":0,"r":{"docs":{"91":{"tf":1.0},"92":{"tf":1.0},"93":{"tf":1.0}},"df":3}},"w":{"docs":{},"df":0,"<":{"docs":{},"df":0,"u":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{"92":{"tf":1.7320508075688772},"93":{"tf":1.7320508075688772}},"df":2}}}}}},"s":{"docs":{"174":{"tf":1.0},"190":{"tf":2.0},"43":{"tf":1.0},"64":{"tf":1.4142135623730951}},"df":4,"a":{"docs":{"9":{"tf":1.4142135623730951}},"df":1,"f":{"docs":{},"df":0,"e":{"docs":{"122":{"tf":1.0},"124":{"tf":1.7320508075688772},"125":{"tf":2.6457513110645907},"126":{"tf":2.449489742783178},"127":{"tf":2.0},"128":{"tf":1.0},"133":{"tf":1.0},"136":{"tf":1.0},"165":{"tf":1.0},"64":{"tf":1.0},"80":{"tf":2.0},"84":{"tf":1.4142135623730951},"92":{"tf":1.0},"93":{"tf":1.4142135623730951}},"df":14,"r":{"docs":{"168":{"tf":1.0}},"df":1},"t":{"docs":{},"df":0,"i":{"docs":{"1":{"tf":1.0},"120":{"tf":1.0},"125":{"tf":1.0},"127":{"tf":1.0},"163":{"tf":1.0},"78":{"tf":1.4142135623730951}},"df":6}}}},"m":{"docs":{},"df":0,"e":{"docs":{"101":{"tf":1.0},"112":{"tf":1.0},"120":{"tf":1.0},"123":{"tf":1.4142135623730951},"125":{"tf":1.0},"131":{"tf":1.0},"133":{"tf":1.0},"139":{"tf":1.0},"148":{"tf":1.0},"155":{"tf":1.4142135623730951},"160":{"tf":1.4142135623730951},"164":{"tf":1.0},"165":{"tf":1.0},"173":{"tf":1.4142135623730951},"21":{"tf":1.4142135623730951},"61":{"tf":1.0},"65":{"tf":1.0},"67":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":1.4142135623730951},"72":{"tf":1.0},"80":{"tf":1.7320508075688772},"84":{"tf":1.4142135623730951},"86":{"tf":1.0},"88":{"tf":1.0},"9":{"tf":1.0},"91":{"tf":1.0},"92":{"tf":1.0},"93":{"tf":1.0}},"df":29},"p":{"docs":{},"df":0,"l":{"docs":{"165":{"tf":1.0},"68":{"tf":1.0},"9":{"tf":1.0}},"df":3}}},"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"f":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"125":{"tf":1.0}},"df":1}}}}}},"i":{"docs":{"101":{"tf":1.0},"126":{"tf":1.0}},"df":2}}}}},"v":{"docs":{},"df":0,"e":{"docs":{"86":{"tf":1.0}},"df":1}},"w":{"docs":{"127":{"tf":1.0},"131":{"tf":1.0}},"df":2}},"c":{"docs":{"76":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"p":{"docs":{},"df":0,".":{"docs":{},"df":0,"f":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"e":{"docs":{},"df":0,"z":{"docs":{"76":{"tf":1.0}},"df":1}}}}},"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"76":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}},"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{"76":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{"80":{"tf":1.0}},"df":1}}}}}},"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{"128":{"tf":1.0}},"df":1}}}}},"o":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{"126":{"tf":1.0},"127":{"tf":1.0},"131":{"tf":1.0},"158":{"tf":1.0},"160":{"tf":1.0},"75":{"tf":1.0}},"df":6}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"67":{"tf":1.0}},"df":1}}}},"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{"161":{"tf":2.23606797749979},"175":{"tf":1.0},"176":{"tf":1.4142135623730951},"178":{"tf":1.0},"20":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.0}},"df":7}}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"155":{"tf":1.7320508075688772}},"df":1,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"s":{"docs":{"155":{"tf":3.1622776601683795}},"df":1}}}}}},"m":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"29":{"tf":1.0}},"df":1,"l":{"docs":{},"df":0,"i":{"docs":{"36":{"tf":1.0}},"df":1}}}}}}},"r":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"78":{"tf":1.0}},"df":1}}}},"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"11":{"tf":1.0},"122":{"tf":1.4142135623730951},"134":{"tf":1.0},"59":{"tf":1.0},"67":{"tf":1.0},"81":{"tf":1.4142135623730951},"88":{"tf":1.4142135623730951},"93":{"tf":1.0}},"df":8}}},"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"11":{"tf":1.0},"123":{"tf":3.0},"125":{"tf":2.23606797749979},"126":{"tf":2.0},"127":{"tf":1.7320508075688772},"128":{"tf":1.0},"131":{"tf":1.0},"132":{"tf":1.0},"133":{"tf":1.4142135623730951},"157":{"tf":1.0},"186":{"tf":1.0},"188":{"tf":1.4142135623730951},"3":{"tf":1.4142135623730951},"44":{"tf":1.0},"47":{"tf":1.0},"52":{"tf":1.0},"53":{"tf":1.7320508075688772},"54":{"tf":1.7320508075688772},"55":{"tf":1.0},"61":{"tf":2.23606797749979},"62":{"tf":3.1622776601683795},"64":{"tf":1.0},"65":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":3.0},"71":{"tf":1.0},"77":{"tf":1.4142135623730951},"8":{"tf":1.0},"84":{"tf":1.0},"86":{"tf":1.0},"87":{"tf":1.0}},"df":32}}}},"u":{"docs":{},"df":0,"r":{"docs":{"17":{"tf":1.0}},"df":1}}},"e":{"docs":{"1":{"tf":1.0},"102":{"tf":1.0},"103":{"tf":1.0},"109":{"tf":1.0},"115":{"tf":1.0},"122":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":1.0},"134":{"tf":1.0},"160":{"tf":1.0},"163":{"tf":1.0},"165":{"tf":1.4142135623730951},"188":{"tf":1.4142135623730951},"189":{"tf":1.0},"190":{"tf":1.0},"193":{"tf":1.0},"195":{"tf":1.0},"196":{"tf":1.0},"197":{"tf":1.0},"198":{"tf":1.0},"200":{"tf":1.0},"201":{"tf":1.0},"53":{"tf":1.0},"55":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.0},"64":{"tf":1.7320508075688772},"68":{"tf":1.4142135623730951},"69":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0},"76":{"tf":1.0},"77":{"tf":1.0},"78":{"tf":1.0},"81":{"tf":1.0},"83":{"tf":1.4142135623730951},"86":{"tf":1.0},"90":{"tf":1.0}},"df":38,"n":{"docs":{"103":{"tf":1.0},"126":{"tf":1.0},"127":{"tf":1.0}},"df":3}},"g":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"35":{"tf":1.0}},"df":1}}},"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"87":{"tf":1.0}},"df":1}}}}},"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"133":{"tf":1.4142135623730951},"157":{"tf":1.0},"158":{"tf":1.0},"161":{"tf":1.0},"53":{"tf":1.7320508075688772},"55":{"tf":1.0},"68":{"tf":1.7320508075688772},"76":{"tf":1.0},"86":{"tf":1.0}},"df":9}}},"f":{"docs":{"101":{"tf":1.4142135623730951},"104":{"tf":2.0},"107":{"tf":2.0},"108":{"tf":2.0},"109":{"tf":1.0},"133":{"tf":1.0},"146":{"tf":1.0},"155":{"tf":2.0},"93":{"tf":1.4142135623730951},"99":{"tf":1.0}},"df":10,"(":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"h":{"docs":{"146":{"tf":1.0}},"df":1}}}}}}},".":{"docs":{},"df":0,"0":{"docs":{"146":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"125":{"tf":1.4142135623730951}},"df":1}}}}},"a":{"docs":{"104":{"tf":1.4142135623730951}},"df":1},"b":{"docs":{"104":{"tf":1.4142135623730951}},"df":1},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"133":{"tf":1.0}},"df":1}}},"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,".":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"133":{"tf":1.0}},"df":1}}}}}}}},"p":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"v":{"docs":{},"df":0,"r":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"93":{"tf":1.0}},"df":1}}}}}}}},"r":{"docs":{},"df":0,"v":{"docs":{},"df":0,"r":{"docs":{},"df":0,".":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"v":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"u":{"docs":{"93":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}},"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"h":{"docs":{"109":{"tf":2.449489742783178},"112":{"tf":1.0}},"df":2,".":{"docs":{},"df":0,"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"f":{"docs":{},"df":0,"y":{"docs":{},"df":0,"(":{"docs":{},"df":0,"|":{"docs":{},"df":0,"_":{"docs":{},"df":0,"r":{"docs":{"107":{"tf":2.0},"108":{"tf":1.7320508075688772},"109":{"tf":2.6457513110645907},"112":{"tf":1.0}},"df":4}},"r":{"docs":{"108":{"tf":1.0}},"df":1}}}}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"108":{"tf":1.0}},"df":1}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"108":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}}}}}}}}}},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"108":{"tf":2.0}},"df":1}}}}}}}}}}}}}}}}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"107":{"tf":1.0}},"df":1}}}}}}}}}}}}},".":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"109":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{"154":{"tf":1.0}},"df":1}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"99":{"tf":1.0}},"df":1}}}}}}},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"h":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"80":{"tf":1.0}},"df":1}}}}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"u":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"36":{"tf":1.0}},"df":1}}}}}}}}},"h":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"63":{"tf":2.8284271247461903},"64":{"tf":1.0},"68":{"tf":2.8284271247461903},"77":{"tf":4.0},"78":{"tf":1.4142135623730951},"79":{"tf":1.4142135623730951},"81":{"tf":1.4142135623730951}},"df":7}}}}}},"n":{"docs":{},"df":0,"d":{"docs":{"103":{"tf":1.0},"125":{"tf":1.4142135623730951},"126":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.0},"88":{"tf":2.0}},"df":6},"s":{"docs":{"107":{"tf":1.0},"187":{"tf":1.0}},"df":2,"i":{"docs":{},"df":0,"t":{"docs":{"138":{"tf":1.0},"151":{"tf":1.0}},"df":2}},"o":{"docs":{},"df":0,"r":{"docs":{"118":{"tf":1.0},"86":{"tf":1.0}},"df":2}}}},"p":{"docs":{"44":{"tf":1.0}},"df":1,"a":{"docs":{},"df":0,"r":{"docs":{"125":{"tf":1.0},"53":{"tf":1.0},"56":{"tf":1.0},"68":{"tf":1.0}},"df":4}}},"q":{"docs":{},"df":0,"u":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{"151":{"tf":1.0}},"df":1}}}}},"r":{"docs":{},"df":0,"_":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"g":{"docs":{"101":{"tf":1.0}},"df":1}}}}}}}}}}}}}}},"i":{"docs":{"34":{"tf":1.0},"69":{"tf":1.4142135623730951},"86":{"tf":1.0}},"df":3,"a":{"docs":{},"df":0,"l":{"docs":{"103":{"tf":1.0},"115":{"tf":1.0},"198":{"tf":1.0},"27":{"tf":1.0},"34":{"tf":1.0},"4":{"tf":1.0},"69":{"tf":1.0},"76":{"tf":2.0},"88":{"tf":1.4142135623730951},"99":{"tf":1.4142135623730951}},"df":10,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"0":{"docs":{"76":{"tf":1.0}},"df":1}}}}}}},"_":{"docs":{},"df":0,"1":{"docs":{"101":{"tf":1.0},"99":{"tf":1.0}},"df":2,".":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{"101":{"tf":1.0}},"df":1}}}}}}}}}},"2":{"docs":{"99":{"tf":1.0}},"df":1}},"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"101":{"tf":1.7320508075688772},"98":{"tf":1.4142135623730951},"99":{"tf":1.7320508075688772}},"df":3,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{"101":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}},"v":{"docs":{"2":{"tf":1.0},"68":{"tf":1.0},"88":{"tf":1.0}},"df":3,"e":{"docs":{},"df":0,"r":{"docs":{"31":{"tf":1.0},"64":{"tf":1.4142135623730951},"68":{"tf":1.0}},"df":3}},"i":{"docs":{},"df":0,"c":{"docs":{"82":{"tf":1.7320508075688772}},"df":1}}}},"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"53":{"tf":1.0},"63":{"tf":1.0},"64":{"tf":1.0},"68":{"tf":2.0},"77":{"tf":1.7320508075688772}},"df":5}}}}},"t":{"docs":{"102":{"tf":1.0},"103":{"tf":1.0},"106":{"tf":1.0},"107":{"tf":3.1622776601683795},"108":{"tf":3.0},"109":{"tf":1.0},"110":{"tf":1.4142135623730951},"114":{"tf":1.0},"115":{"tf":1.0},"118":{"tf":1.4142135623730951},"122":{"tf":1.0},"127":{"tf":2.23606797749979},"132":{"tf":1.0},"137":{"tf":1.0},"139":{"tf":1.0},"141":{"tf":1.0},"15":{"tf":1.0},"158":{"tf":1.0},"160":{"tf":1.0},"166":{"tf":1.4142135623730951},"180":{"tf":1.0},"184":{"tf":1.0},"190":{"tf":1.0},"2":{"tf":1.0},"20":{"tf":1.0},"28":{"tf":1.4142135623730951},"32":{"tf":1.0},"53":{"tf":1.0},"61":{"tf":1.0},"63":{"tf":1.0},"64":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.4142135623730951},"69":{"tf":1.0},"72":{"tf":1.0},"74":{"tf":1.4142135623730951},"76":{"tf":1.7320508075688772},"77":{"tf":1.0},"80":{"tf":1.0},"84":{"tf":1.0}},"df":40,"_":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"109":{"tf":1.0}},"df":1}}}}}}}},"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0}},"df":2}}}}}}}}}}}}}},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0}},"df":2}}}}}}}}}}},"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{"109":{"tf":1.0}},"df":1}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0}},"df":2}}}}}}}}}}}}}}},"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"107":{"tf":1.0}},"df":1}}}}}}}}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"108":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"93":{"tf":1.0}},"df":1,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"93":{"tf":1.0}},"df":1}}}}}}}}}}},"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"_":{"docs":{},"df":0,"1":{"docs":{},"df":0,"h":{"docs":{},"df":0,"z":{"docs":{"122":{"tf":1.0},"123":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":1.0},"126":{"tf":1.0},"127":{"tf":1.0}},"df":6}}}}}}}}}},"u":{"docs":{},"df":0,"p":{"docs":{"134":{"tf":1.0},"27":{"tf":1.0},"29":{"tf":1.0},"64":{"tf":1.4142135623730951},"84":{"tf":1.0}},"df":5,"_":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"121":{"tf":1.0}},"df":1}}}}}}}},"s":{"docs":{},"df":0,"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"_":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"102":{"tf":1.0}},"df":1}}}}}}}}}}}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"19":{"tf":1.0},"5":{"tf":1.0},"56":{"tf":1.0},"77":{"tf":1.0}},"df":4}}}},"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{"11":{"tf":1.0}},"df":1}},"r":{"docs":{},"df":0,"e":{"docs":{"121":{"tf":1.0},"122":{"tf":1.0},"123":{"tf":1.0},"125":{"tf":1.4142135623730951},"126":{"tf":1.0},"127":{"tf":2.0},"128":{"tf":1.4142135623730951},"2":{"tf":1.0},"21":{"tf":1.0},"80":{"tf":1.0},"95":{"tf":1.0}},"df":11}}},"e":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"66":{"tf":1.0},"90":{"tf":1.0}},"df":2}},"l":{"docs":{},"df":0,"l":{"docs":{"177":{"tf":1.0},"64":{"tf":1.0}},"df":2}}},"i":{"docs":{},"df":0,"p":{"docs":{"132":{"tf":1.0},"133":{"tf":1.0},"21":{"tf":1.0}},"df":3}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"126":{"tf":1.0},"128":{"tf":1.0}},"df":2}},"w":{"docs":{"127":{"tf":1.0},"44":{"tf":1.0},"54":{"tf":1.0},"62":{"tf":1.0},"64":{"tf":1.0},"68":{"tf":1.0},"75":{"tf":1.0},"83":{"tf":1.0}},"df":8,"n":{"docs":{"190":{"tf":1.0},"38":{"tf":1.0},"44":{"tf":1.0},"61":{"tf":1.0},"64":{"tf":1.0}},"df":5}}},"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"k":{"docs":{"137":{"tf":1.0}},"df":1,"_":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"_":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"137":{"tf":1.0}},"df":1}}}}}}}}}},"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"k":{"docs":{"137":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"t":{"docs":{"146":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"126":{"tf":1.0}},"df":1}},"d":{"docs":{},"df":0,"e":{"docs":{"11":{"tf":1.0}},"df":1}},"g":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"11":{"tf":1.0},"122":{"tf":1.4142135623730951},"133":{"tf":1.0},"158":{"tf":1.0},"165":{"tf":3.605551275463989},"191":{"tf":1.0},"80":{"tf":1.0}},"df":7},"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"134":{"tf":1.4142135623730951},"172":{"tf":1.0},"173":{"tf":1.4142135623730951},"184":{"tf":1.0},"60":{"tf":1.0},"78":{"tf":1.0},"83":{"tf":1.0}},"df":7}}}},"i":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"87":{"tf":1.0}},"df":1}}}}}},"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"91":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"86":{"tf":1.4142135623730951}},"df":1}}}}},"m":{"docs":{},"df":0,"d":{"docs":{"16":{"tf":1.0}},"df":1},"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"123":{"tf":1.0},"125":{"tf":1.0},"127":{"tf":1.4142135623730951},"168":{"tf":1.0},"173":{"tf":1.0},"84":{"tf":1.7320508075688772},"86":{"tf":1.0},"96":{"tf":1.0}},"df":8,"l":{"docs":{},"df":0,"i":{"docs":{"106":{"tf":1.0},"114":{"tf":1.0},"158":{"tf":1.0},"164":{"tf":1.0},"84":{"tf":1.0},"88":{"tf":1.0}},"df":6}}}}}},"p":{"docs":{},"df":0,"l":{"docs":{"121":{"tf":1.0},"123":{"tf":1.0},"126":{"tf":1.0},"133":{"tf":1.0},"184":{"tf":1.0},"61":{"tf":1.0},"88":{"tf":1.0},"90":{"tf":1.0}},"df":8,"e":{"docs":{},"df":0,"r":{"docs":{"158":{"tf":1.0},"160":{"tf":1.0}},"df":2},"s":{"docs":{},"df":0,"t":{"docs":{"121":{"tf":1.0},"178":{"tf":1.0}},"df":2}}},"i":{"docs":{"158":{"tf":1.0},"168":{"tf":1.0},"184":{"tf":1.0},"78":{"tf":1.0}},"df":4,"c":{"docs":{"26":{"tf":1.0},"66":{"tf":1.0}},"df":2},"f":{"docs":{},"df":0,"i":{"docs":{"106":{"tf":1.0},"157":{"tf":1.0},"162":{"tf":1.0},"24":{"tf":1.0},"63":{"tf":1.0}},"df":5}}}}},"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{"80":{"tf":1.0}},"df":1}}}}}},"n":{"docs":{"191":{"tf":1.0}},"df":1,"(":{"docs":{},"df":0,"f":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"f":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,".":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{"191":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}},"g":{"docs":{},"df":0,"l":{"docs":{"102":{"tf":1.0},"11":{"tf":1.4142135623730951},"112":{"tf":1.0},"114":{"tf":1.0},"121":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":1.0},"131":{"tf":1.0},"133":{"tf":1.0},"151":{"tf":1.0},"178":{"tf":1.0},"196":{"tf":1.0},"2":{"tf":1.0},"66":{"tf":1.0},"68":{"tf":1.0},"74":{"tf":1.0},"76":{"tf":1.0},"78":{"tf":1.0},"86":{"tf":1.0},"99":{"tf":1.0}},"df":20,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"100":{"tf":1.4142135623730951},"101":{"tf":1.4142135623730951},"127":{"tf":1.4142135623730951},"97":{"tf":1.4142135623730951}},"df":4}}}}}},"u":{"docs":{"191":{"tf":1.4142135623730951}},"df":1,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"o":{"docs":{},"df":0,"f":{"docs":{},"df":0,"_":{"docs":{},"df":0,"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"191":{"tf":1.7320508075688772}},"df":1,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"191":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}},"t":{"docs":{"53":{"tf":1.0}},"df":1,"e":{"docs":{"199":{"tf":1.0}},"df":1},"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"164":{"tf":1.0}},"df":1}}}},"z":{"docs":{},"df":0,"e":{"docs":{"112":{"tf":1.0},"113":{"tf":1.0},"123":{"tf":1.0},"133":{"tf":1.7320508075688772},"137":{"tf":2.0},"142":{"tf":1.0},"151":{"tf":1.0},"153":{"tf":1.0},"157":{"tf":1.0},"159":{"tf":1.0},"173":{"tf":1.0},"186":{"tf":1.0},"187":{"tf":1.4142135623730951},"188":{"tf":2.23606797749979},"189":{"tf":1.4142135623730951},"190":{"tf":2.23606797749979},"21":{"tf":1.0},"62":{"tf":3.0},"68":{"tf":2.8284271247461903},"78":{"tf":1.4142135623730951}},"df":20,"_":{"docs":{},"df":0,"o":{"docs":{},"df":0,"f":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"<":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"111":{"tf":1.0}},"df":1}},"g":{"docs":{},"df":0,"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"<":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"111":{"tf":1.0}},"df":1}}}}}}}}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"111":{"tf":1.0}},"df":1}}}}},"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{"111":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}},"k":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{"64":{"tf":1.0},"68":{"tf":1.0},"92":{"tf":1.0}},"df":3}}},"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"e":{"docs":{},"df":0,"p":{"docs":{"165":{"tf":1.4142135623730951}},"df":1}}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{"16":{"tf":1.0}},"df":1}},"d":{"docs":{},"df":0,"e":{"docs":{"9":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"w":{"docs":{"187":{"tf":1.0},"77":{"tf":1.0}},"df":2,"e":{"docs":{},"df":0,"r":{"docs":{"163":{"tf":1.0}},"df":1}}}}},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{"151":{"tf":1.7320508075688772},"186":{"tf":1.0},"190":{"tf":1.0},"62":{"tf":1.0},"67":{"tf":1.0},"99":{"tf":1.4142135623730951}},"df":6,"e":{"docs":{},"df":0,"r":{"docs":{"187":{"tf":1.0},"190":{"tf":1.0}},"df":2}}}}},"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{"58":{"tf":1.0}},"df":1}}},"p":{"docs":{"131":{"tf":1.0}},"df":1}},"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{},"df":0,"o":{"docs":{},"df":0,"t":{"docs":{"59":{"tf":1.0},"83":{"tf":1.0}},"df":2}}}}}},"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"112":{"tf":1.0},"62":{"tf":1.0}},"df":2}}}}}},"o":{"docs":{},"df":0,"f":{"docs":{},"df":0,"t":{"docs":{},"df":0,"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"102":{"tf":1.0},"103":{"tf":1.0},"114":{"tf":1.4142135623730951},"121":{"tf":1.0},"128":{"tf":1.0},"14":{"tf":1.0},"2":{"tf":1.0},"24":{"tf":1.4142135623730951},"26":{"tf":1.0},"27":{"tf":1.0},"28":{"tf":1.0},"72":{"tf":1.0},"80":{"tf":1.4142135623730951},"84":{"tf":1.0},"86":{"tf":1.4142135623730951},"87":{"tf":1.4142135623730951},"95":{"tf":1.4142135623730951},"97":{"tf":1.4142135623730951}},"df":18}}}}},"l":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"121":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":1.0},"26":{"tf":1.0},"30":{"tf":1.0},"33":{"tf":1.0}},"df":6}},"v":{"docs":{"123":{"tf":1.0},"184":{"tf":1.0}},"df":2}},"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"4":{"docs":{},"df":0,"2":{"docs":{"133":{"tf":1.0},"134":{"tf":1.0}},"df":2}},"h":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"81":{"tf":1.0}},"df":1}}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{"127":{"tf":1.0}},"df":1}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"99":{"tf":1.0}},"df":1}}}}}}}}}}},"h":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"127":{"tf":1.0}},"df":1}}},"t":{"docs":{},"df":0,"h":{"docs":{"125":{"tf":1.0},"127":{"tf":1.0},"3":{"tf":1.0},"44":{"tf":1.0},"63":{"tf":1.0},"83":{"tf":1.0},"86":{"tf":1.0},"87":{"tf":1.0}},"df":8},"i":{"docs":{},"df":0,"m":{"docs":{"119":{"tf":1.4142135623730951},"121":{"tf":1.0},"188":{"tf":1.0},"196":{"tf":1.0},"78":{"tf":1.0}},"df":5}}},"w":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"65":{"tf":1.0}},"df":1}}}}}},"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"120":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"t":{"docs":{"127":{"tf":1.4142135623730951},"13":{"tf":1.0},"15":{"tf":1.0},"69":{"tf":2.0}},"df":4}},"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"104":{"tf":1.0},"96":{"tf":1.0}},"df":2}},"r":{"docs":{},"df":0,"c":{"docs":{"130":{"tf":1.0},"160":{"tf":1.0},"175":{"tf":1.0},"27":{"tf":1.0},"33":{"tf":1.0},"54":{"tf":1.0},"60":{"tf":1.0},"61":{"tf":1.0},"64":{"tf":1.0},"68":{"tf":1.7320508075688772},"78":{"tf":1.0}},"df":11}}}},"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{"112":{"tf":1.0},"130":{"tf":1.0},"187":{"tf":1.0},"62":{"tf":1.0},"66":{"tf":1.0},"87":{"tf":1.7320508075688772}},"df":6}},"r":{"docs":{},"df":0,"s":{"docs":{"159":{"tf":1.0}},"df":1}},"w":{"docs":{},"df":0,"n":{"docs":{"15":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"114":{"tf":1.0},"122":{"tf":1.0},"124":{"tf":1.0},"13":{"tf":1.4142135623730951},"24":{"tf":1.0},"54":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0},"73":{"tf":1.0},"76":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0}},"df":12}},"f":{"docs":{"100":{"tf":1.0},"11":{"tf":1.0},"114":{"tf":1.0},"117":{"tf":1.0},"124":{"tf":1.0},"126":{"tf":1.0},"155":{"tf":1.4142135623730951},"164":{"tf":1.0},"165":{"tf":1.0},"166":{"tf":1.4142135623730951},"183":{"tf":1.0},"192":{"tf":1.0},"193":{"tf":1.0},"29":{"tf":1.0},"31":{"tf":1.0},"41":{"tf":1.0},"5":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.0},"72":{"tf":1.0},"78":{"tf":1.0},"82":{"tf":1.4142135623730951},"84":{"tf":1.0}},"df":24,"i":{"docs":{"115":{"tf":1.0},"166":{"tf":1.0},"19":{"tf":1.0},"63":{"tf":1.0},"77":{"tf":1.4142135623730951},"92":{"tf":1.0}},"df":6}}}},"e":{"docs":{},"df":0,"d":{"docs":{"117":{"tf":1.0},"189":{"tf":1.4142135623730951},"2":{"tf":1.0},"53":{"tf":2.0},"68":{"tf":2.0}},"df":5}},"n":{"docs":{},"df":0,"d":{"docs":{"86":{"tf":1.0}},"df":1}}},"i":{"docs":{"102":{"tf":1.0},"11":{"tf":1.0},"115":{"tf":1.0},"4":{"tf":1.0},"5":{"tf":1.4142135623730951},"88":{"tf":1.7320508075688772}},"df":6,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"102":{"tf":1.0}},"df":1}}}}},"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"122":{"tf":1.0},"153":{"tf":1.0},"76":{"tf":1.0}},"df":3,"(":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"153":{"tf":1.0}},"df":1}}}}}}}},"o":{"docs":{},"df":0,"t":{"docs":{"122":{"tf":1.0},"93":{"tf":1.0}},"df":2}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"93":{"tf":1.0}},"df":1}}}}},"q":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"o":{"docs":{},"df":0,"f":{"docs":{},"df":0,"_":{"docs":{},"df":0,"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"191":{"tf":1.7320508075688772}},"df":1,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"191":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}},"f":{"docs":{"191":{"tf":1.0}},"df":1,"(":{"docs":{},"df":0,"f":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"f":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"191":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}},"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"191":{"tf":1.4142135623730951}},"df":1,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"t":{"docs":{"191":{"tf":1.0}},"df":1}}}}}}}}},"r":{"docs":{},"df":0,"c":{"docs":{},"df":0,"/":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"b":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{"63":{"tf":1.0}},"df":1}},"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"/":{"docs":{},"df":0,"n":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"/":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"n":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{"64":{"tf":1.0}},"df":1,"s":{"docs":{},"df":0,":":{"docs":{},"df":0,"2":{"docs":{},"df":0,"5":{"docs":{},"df":0,"4":{"docs":{"64":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{"60":{"tf":1.0}},"df":1,"s":{"docs":{},"df":0,":":{"docs":{},"df":0,"1":{"docs":{},"df":0,"2":{"docs":{},"df":0,":":{"docs":{},"df":0,"1":{"docs":{},"df":0,"3":{"docs":{"79":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}},"t":{"docs":{"11":{"tf":1.0},"24":{"tf":1.0},"34":{"tf":1.4142135623730951},"44":{"tf":2.0},"53":{"tf":1.7320508075688772},"68":{"tf":1.4142135623730951},"69":{"tf":1.0},"77":{"tf":1.0},"93":{"tf":1.7320508075688772}},"df":9,".":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{"93":{"tf":1.0}},"df":1}}}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"(":{"docs":{},"df":0,"0":{"docs":{},"df":0,"x":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"_":{"docs":{},"df":0,"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"f":{"docs":{"93":{"tf":1.0}},"df":1}}}}}}}}}}},"1":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{"93":{"tf":1.0}},"df":1}}}},"2":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{"93":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}},"7":{"docs":{},"df":0,"2":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{},"df":0,"5":{"docs":{},"df":0,"x":{"docs":{},"df":0,"x":{"docs":{"12":{"tf":1.0}},"df":1}}}}}}},"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"183":{"tf":1.0}},"df":1}},"l":{"docs":{"159":{"tf":1.0},"171":{"tf":1.0},"181":{"tf":1.0}},"df":3}},"c":{"docs":{},"df":0,"k":{"docs":{"134":{"tf":1.0},"137":{"tf":2.23606797749979},"15":{"tf":1.0},"17":{"tf":1.0},"187":{"tf":1.0},"5":{"tf":1.0},"61":{"tf":1.7320508075688772},"78":{"tf":1.0},"83":{"tf":1.0}},"df":9}},"g":{"docs":{},"df":0,"e":{"docs":{"16":{"tf":1.0}},"df":1}},"n":{"docs":{},"df":0,"d":{"docs":{"162":{"tf":1.0},"9":{"tf":1.0}},"df":2,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{"114":{"tf":1.0},"13":{"tf":1.0},"133":{"tf":1.0},"14":{"tf":1.7320508075688772},"191":{"tf":1.7320508075688772},"20":{"tf":1.0},"30":{"tf":1.0},"60":{"tf":2.0},"62":{"tf":1.0},"70":{"tf":1.0},"78":{"tf":1.4142135623730951}},"df":11}}}}},"r":{"docs":{},"df":0,"t":{"docs":{"104":{"tf":1.0},"106":{"tf":1.0},"127":{"tf":1.0},"133":{"tf":2.23606797749979},"156":{"tf":1.0},"188":{"tf":1.0},"5":{"tf":1.0},"55":{"tf":1.0},"56":{"tf":1.0},"62":{"tf":1.4142135623730951},"67":{"tf":1.4142135623730951},"68":{"tf":2.6457513110645907},"70":{"tf":1.7320508075688772},"77":{"tf":1.0}},"df":14,"e":{"docs":{},"df":0,"r":{"docs":{"104":{"tf":1.4142135623730951}},"df":1}},"u":{"docs":{},"df":0,"p":{"docs":{"64":{"tf":1.0}},"df":1}}}},"t":{"docs":{},"df":0,"e":{"docs":{"102":{"tf":1.0},"103":{"tf":1.4142135623730951},"104":{"tf":2.0},"105":{"tf":1.7320508075688772},"106":{"tf":1.4142135623730951},"107":{"tf":2.0},"108":{"tf":1.4142135623730951},"109":{"tf":2.0},"110":{"tf":2.0},"111":{"tf":1.4142135623730951},"112":{"tf":1.4142135623730951},"113":{"tf":1.0},"122":{"tf":1.7320508075688772},"123":{"tf":1.7320508075688772},"124":{"tf":1.7320508075688772},"125":{"tf":1.7320508075688772},"126":{"tf":2.0},"127":{"tf":2.0},"128":{"tf":1.0},"142":{"tf":1.4142135623730951},"146":{"tf":1.0},"155":{"tf":2.8284271247461903},"191":{"tf":1.0},"28":{"tf":1.0},"31":{"tf":1.0},"67":{"tf":1.0},"76":{"tf":1.4142135623730951},"81":{"tf":1.0},"83":{"tf":1.4142135623730951},"84":{"tf":1.0},"92":{"tf":1.0},"94":{"tf":1.0}},"df":32,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"158":{"tf":1.0},"160":{"tf":1.4142135623730951},"173":{"tf":1.0},"187":{"tf":1.4142135623730951},"78":{"tf":1.0},"80":{"tf":1.0}},"df":6}}}}},"i":{"docs":{},"df":0,"c":{"docs":{"100":{"tf":1.4142135623730951},"103":{"tf":1.4142135623730951},"122":{"tf":2.0},"123":{"tf":1.0},"124":{"tf":1.4142135623730951},"125":{"tf":2.6457513110645907},"126":{"tf":1.7320508075688772},"127":{"tf":2.0},"128":{"tf":1.7320508075688772},"133":{"tf":1.0},"134":{"tf":1.0},"137":{"tf":1.0},"153":{"tf":1.0},"159":{"tf":1.0},"161":{"tf":1.4142135623730951},"165":{"tf":1.0},"173":{"tf":1.0},"175":{"tf":2.0},"177":{"tf":1.0},"178":{"tf":1.7320508075688772},"180":{"tf":1.0},"61":{"tf":1.0},"62":{"tf":1.4142135623730951},"76":{"tf":1.0},"80":{"tf":2.449489742783178},"81":{"tf":1.4142135623730951},"84":{"tf":1.4142135623730951},"92":{"tf":1.0},"93":{"tf":1.0},"98":{"tf":1.4142135623730951},"99":{"tf":1.0}},"df":31,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"b":{"docs":{"180":{"tf":1.0}},"df":1}}}}},"u":{"docs":{"108":{"tf":1.4142135623730951},"90":{"tf":1.0}},"df":2}},"y":{"docs":{"126":{"tf":1.0},"70":{"tf":1.0}},"df":2}},"d":{"docs":{"132":{"tf":1.4142135623730951},"133":{"tf":1.0},"16":{"tf":1.4142135623730951},"17":{"tf":1.0},"60":{"tf":1.0},"78":{"tf":1.0}},"df":6,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{"168":{"tf":1.7320508075688772}},"df":1}}},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"k":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"p":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"d":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{"155":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}},"p":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"177":{"tf":1.0}},"df":1}}}}}}}}}}}}}}},"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"d":{"docs":{},"df":0,"r":{"docs":{},"df":0,"_":{"docs":{},"df":0,"o":{"docs":{},"df":0,"f":{"docs":{"166":{"tf":1.0}},"df":1,"!":{"docs":{},"df":0,"(":{"docs":{},"df":0,"v":{"docs":{},"df":0,".":{"docs":{},"df":0,"i":{"docs":{"166":{"tf":1.0}},"df":1},"x":{"docs":{"166":{"tf":1.0}},"df":1},"z":{"docs":{"166":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}},"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{"63":{"tf":1.0},"77":{"tf":1.0}},"df":2}}},"i":{"docs":{},"df":0,"n":{"docs":{"63":{"tf":1.0}},"df":1}},"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"b":{"docs":{"168":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"63":{"tf":1.0},"81":{"tf":1.4142135623730951}},"df":2,".":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"81":{"tf":1.0}},"df":1}}}}}},"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"81":{"tf":1.0}},"df":1}}}}}}}}}}},"e":{"docs":{},"df":0,"p":{"docs":{"105":{"tf":1.0},"106":{"tf":1.7320508075688772},"174":{"tf":1.0},"175":{"tf":1.7320508075688772},"187":{"tf":1.0},"188":{"tf":1.0},"28":{"tf":1.0},"32":{"tf":1.0},"61":{"tf":1.0},"64":{"tf":1.0},"67":{"tf":1.4142135623730951},"68":{"tf":2.8284271247461903},"69":{"tf":1.0},"84":{"tf":1.0}},"df":14,"i":{"docs":{"68":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{"123":{"tf":1.0},"125":{"tf":1.0},"126":{"tf":1.0},"127":{"tf":1.0},"159":{"tf":1.0},"164":{"tf":1.0},"170":{"tf":1.0},"80":{"tf":1.0},"92":{"tf":1.0}},"df":9}}},"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"k":{"docs":{"53":{"tf":1.4142135623730951},"68":{"tf":1.4142135623730951}},"df":2}}}},"m":{"docs":{"34":{"tf":1.0}},"df":1,"3":{"docs":{},"df":0,"2":{"docs":{"34":{"tf":1.0}},"df":1,"f":{"docs":{},"df":0,"1":{"docs":{},"df":0,"0":{"docs":{},"df":0,"3":{"docs":{"11":{"tf":1.0}},"df":1}}},"3":{"docs":{"69":{"tf":1.0},"70":{"tf":1.0}},"df":2,"0":{"docs":{},"df":0,"3":{"docs":{},"df":0,"v":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"6":{"docs":{"11":{"tf":1.4142135623730951},"66":{"tf":1.0},"67":{"tf":1.0}},"df":3}}}}},"x":{"docs":{"69":{"tf":1.4142135623730951}},"df":1}},"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"38":{"tf":1.0},"44":{"tf":1.4142135623730951},"54":{"tf":1.0},"66":{"tf":1.0},"68":{"tf":1.4142135623730951},"69":{"tf":1.0},"7":{"tf":1.4142135623730951},"70":{"tf":1.0}},"df":8}}}}}}}}},"x":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{"53":{"tf":1.0},"68":{"tf":1.0}},"df":2}}}}}},"4":{"docs":{},"df":0,"0":{"docs":{},"df":0,"5":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"s":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"127":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},":":{"docs":{},"df":0,":":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"m":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{},"df":0,"f":{"docs":{},"df":0,"4":{"docs":{},"df":0,"0":{"docs":{},"df":0,"5":{"docs":{"127":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}}},"8":{"docs":{"34":{"tf":1.0}},"df":1},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"11":{"tf":1.0},"34":{"tf":1.4142135623730951},"44":{"tf":1.0},"7":{"tf":1.0}},"df":4}}}}}}}}}}}}},"o":{"docs":{},"df":0,"p":{"docs":{"81":{"tf":1.0},"93":{"tf":1.0}},"df":2},"r":{"docs":{},"df":0,"e":{"docs":{"109":{"tf":1.4142135623730951},"112":{"tf":1.0},"122":{"tf":1.4142135623730951},"124":{"tf":1.4142135623730951},"127":{"tf":1.0},"134":{"tf":1.0},"137":{"tf":1.0},"61":{"tf":1.0},"62":{"tf":1.0}},"df":9}}},"r":{"docs":{"168":{"tf":1.4142135623730951}},"df":1,"b":{"docs":{"62":{"tf":1.4142135623730951}},"df":1},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"26":{"tf":1.0}},"df":1}}}}},"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{"155":{"tf":1.0}},"df":1}}}},"t":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"43":{"tf":1.0}},"df":1}}}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"124":{"tf":1.0}},"df":1}},"l":{"docs":{},"df":0,"i":{"docs":{"164":{"tf":1.0}},"df":1}}}},"n":{"docs":{},"df":0,"g":{"docs":{"132":{"tf":1.0},"16":{"tf":1.0},"168":{"tf":1.7320508075688772},"62":{"tf":1.4142135623730951},"93":{"tf":1.0}},"df":5}},"p":{"docs":{"112":{"tf":1.0}},"df":1}},"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{"105":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"r":{"docs":{"123":{"tf":1.0}},"df":1}},"l":{"docs":{},"df":0,"i":{"docs":{"133":{"tf":1.0}},"df":1}}}}},"u":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"104":{"tf":1.4142135623730951},"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":3.0},"112":{"tf":1.0},"125":{"tf":1.0},"126":{"tf":1.0},"127":{"tf":1.0},"133":{"tf":1.0},"146":{"tf":1.4142135623730951},"153":{"tf":2.23606797749979},"154":{"tf":1.7320508075688772},"155":{"tf":2.8284271247461903},"166":{"tf":2.6457513110645907},"173":{"tf":2.23606797749979},"188":{"tf":1.0},"69":{"tf":1.0},"71":{"tf":1.4142135623730951},"76":{"tf":1.4142135623730951},"78":{"tf":1.0},"91":{"tf":1.7320508075688772},"92":{"tf":1.0},"93":{"tf":2.0},"99":{"tf":1.0}},"df":24,"\'":{"docs":{"166":{"tf":1.0}},"df":1},"u":{"docs":{},"df":0,"r":{"docs":{"100":{"tf":1.4142135623730951},"101":{"tf":1.4142135623730951},"105":{"tf":1.0},"107":{"tf":1.7320508075688772},"108":{"tf":1.0},"109":{"tf":1.4142135623730951},"112":{"tf":1.0},"113":{"tf":1.4142135623730951},"132":{"tf":1.0},"71":{"tf":1.0},"76":{"tf":1.7320508075688772},"91":{"tf":1.4142135623730951},"92":{"tf":1.0},"94":{"tf":1.0},"96":{"tf":1.0},"99":{"tf":2.23606797749979}},"df":16}}}}}},"u":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{"70":{"tf":1.0}},"df":1,"o":{"docs":{"24":{"tf":1.0},"26":{"tf":1.0},"29":{"tf":1.0},"39":{"tf":1.0}},"df":4,"/":{"docs":{},"df":0,"?":{"docs":{},"df":0,"s":{"docs":{},"df":0,"k":{"docs":{},"df":0,"u":{"docs":{},"df":0,"=":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"d":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"s":{"docs":{},"df":0,"&":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"=":{"docs":{},"df":0,"1":{"docs":{},"df":0,"6":{"docs":{"39":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}},"f":{"docs":{},"df":0,"f":{"docs":{"68":{"tf":1.0}},"df":1}}},"y":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{"78":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"b":{"docs":{"72":{"tf":1.0},"73":{"tf":1.0},"74":{"tf":1.4142135623730951},"75":{"tf":1.4142135623730951}},"df":4,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"20":{"tf":1.0},"21":{"tf":1.0}},"df":2}}}}}}},"j":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"151":{"tf":1.0},"70":{"tf":1.0}},"df":2}}}},"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"80":{"tf":1.0}},"df":1}}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"16":{"tf":1.0},"60":{"tf":1.0}},"df":2}},"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"55":{"tf":1.0}},"df":1}}}}}},"t":{"docs":{},"df":0,"l":{"docs":{"120":{"tf":1.0},"122":{"tf":1.0}},"df":2}}},"c":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{"124":{"tf":1.0},"126":{"tf":1.0}},"df":2,"e":{"docs":{},"df":0,"d":{"docs":{"134":{"tf":1.0}},"df":1}},"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"f":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"63":{"tf":1.0}},"df":1}}}}}}}}},"h":{"docs":{"0":{"tf":1.0},"11":{"tf":1.0},"114":{"tf":1.0},"115":{"tf":1.0},"117":{"tf":1.4142135623730951},"118":{"tf":1.0},"121":{"tf":1.0},"122":{"tf":1.0},"126":{"tf":1.7320508075688772},"128":{"tf":1.0},"13":{"tf":1.0},"130":{"tf":1.0},"132":{"tf":1.0},"146":{"tf":1.0},"157":{"tf":1.0},"158":{"tf":1.0},"159":{"tf":1.0},"16":{"tf":1.0},"162":{"tf":1.0},"163":{"tf":1.0},"165":{"tf":1.0},"168":{"tf":1.4142135623730951},"169":{"tf":1.0},"170":{"tf":1.0},"173":{"tf":1.0},"174":{"tf":1.0},"175":{"tf":1.4142135623730951},"176":{"tf":1.4142135623730951},"177":{"tf":1.0},"182":{"tf":1.0},"20":{"tf":1.4142135623730951},"24":{"tf":1.4142135623730951},"28":{"tf":1.0},"30":{"tf":1.0},"31":{"tf":1.0},"35":{"tf":1.0},"4":{"tf":1.7320508075688772},"64":{"tf":1.0},"69":{"tf":1.0},"78":{"tf":1.0},"80":{"tf":1.0},"86":{"tf":1.0},"87":{"tf":1.0}},"df":43}},"d":{"docs":{},"df":0,"o":{"docs":{"43":{"tf":2.0},"44":{"tf":1.0},"47":{"tf":1.7320508075688772},"53":{"tf":1.0}},"df":4}},"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"162":{"tf":1.0}},"df":1,"i":{"docs":{"124":{"tf":1.0}},"df":1}}}}},"g":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"62":{"tf":1.0}},"df":1}},"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"133":{"tf":1.0},"3":{"tf":1.0},"5":{"tf":1.0},"7":{"tf":1.0}},"df":4}}}}},"i":{"docs":{},"df":0,"t":{"docs":{"121":{"tf":1.0},"69":{"tf":1.0}},"df":2,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"162":{"tf":1.0},"17":{"tf":1.0}},"df":2}}}}},"m":{"docs":{"163":{"tf":1.0}},"df":1,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{"125":{"tf":1.0}},"df":1}}}}}},"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"186":{"tf":1.4142135623730951},"77":{"tf":1.4142135623730951}},"df":2}},"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"11":{"tf":1.0},"124":{"tf":1.0},"129":{"tf":1.4142135623730951},"130":{"tf":1.0},"144":{"tf":1.0},"158":{"tf":1.0},"159":{"tf":1.0},"161":{"tf":1.0},"162":{"tf":1.0},"170":{"tf":1.0},"179":{"tf":1.0},"189":{"tf":1.0},"19":{"tf":1.4142135623730951},"190":{"tf":1.0},"191":{"tf":1.4142135623730951},"193":{"tf":1.0},"21":{"tf":1.4142135623730951},"24":{"tf":1.0},"26":{"tf":1.0},"27":{"tf":1.4142135623730951},"30":{"tf":1.0},"31":{"tf":1.4142135623730951},"32":{"tf":1.0},"34":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"36":{"tf":1.0},"38":{"tf":1.4142135623730951},"77":{"tf":1.0}},"df":28}}}}},"r":{"docs":{},"df":0,"e":{"docs":{"123":{"tf":1.0},"125":{"tf":1.0},"126":{"tf":1.0},"127":{"tf":1.0},"38":{"tf":1.0},"52":{"tf":1.0},"67":{"tf":1.0}},"df":7},"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"86":{"tf":1.0}},"df":1}}}}}},"s":{"docs":{},"df":0,"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{"96":{"tf":1.0}},"df":1}}}}}},"v":{"docs":{},"df":0,"d":{"docs":{"197":{"tf":1.0},"72":{"tf":1.0},"73":{"tf":1.0},"74":{"tf":1.0},"84":{"tf":1.0}},"df":5,"2":{"docs":{},"df":0,"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.0},"127":{"tf":1.4142135623730951},"147":{"tf":1.0},"197":{"tf":1.0},"72":{"tf":1.0},"84":{"tf":1.4142135623730951}},"df":8}}}}}}},"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"120":{"tf":1.0},"124":{"tf":1.0},"130":{"tf":1.0}},"df":3}},"d":{"docs":{"27":{"tf":1.0},"30":{"tf":1.0},"32":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.0}},"df":5},"i":{"docs":{},"df":0,"m":{"docs":{"53":{"tf":1.0},"68":{"tf":1.0}},"df":2},"t":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"63":{"tf":1.4142135623730951},"65":{"tf":1.0}},"df":2}}}}},"y":{"docs":{},"df":0,"m":{"docs":{},"df":0,"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{"164":{"tf":1.0},"182":{"tf":1.0},"187":{"tf":1.0},"61":{"tf":1.0},"64":{"tf":1.0},"68":{"tf":1.0}},"df":6}}},"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{"131":{"tf":1.0}},"df":1}}}}},"n":{"docs":{},"df":0,"c":{"docs":{"103":{"tf":1.0},"125":{"tf":2.6457513110645907},"126":{"tf":1.7320508075688772},"127":{"tf":1.4142135623730951},"129":{"tf":1.0},"133":{"tf":1.0}},"df":6,"h":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"128":{"tf":1.0},"129":{"tf":1.0},"201":{"tf":1.0},"80":{"tf":1.0}},"df":4,"i":{"docs":{},"df":0,"s":{"docs":{"121":{"tf":1.0},"123":{"tf":1.4142135623730951},"126":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.0}},"df":5}}}}}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"x":{"docs":{"129":{"tf":1.0},"160":{"tf":1.4142135623730951},"176":{"tf":1.0}},"df":3}}}},"s":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"l":{"docs":{"76":{"tf":1.0}},"df":1,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"c":{"docs":{},"df":0,"r":{"docs":{},"df":0,"y":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"q":{"docs":{},"df":0,"u":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"y":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"_":{"docs":{},"df":0,"1":{"docs":{},"df":0,"6":{"docs":{},"df":0,"m":{"docs":{},"df":0,"h":{"docs":{},"df":0,"z":{"docs":{"76":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}},"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"76":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}},"s":{"docs":{},"df":0,"y":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"(":{"docs":{},"df":0,"s":{"docs":{},"df":0,"y":{"docs":{},"df":0,"s":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"l":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"f":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"q":{"docs":{},"df":0,"u":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"y":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"_":{"docs":{},"df":0,"8":{"docs":{},"df":0,"0":{"docs":{},"df":0,"_":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"m":{"docs":{},"df":0,"h":{"docs":{},"df":0,"z":{"docs":{"76":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"f":{"docs":{"117":{"tf":1.0}},"df":1},"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"t":{"docs":{"13":{"tf":1.0},"78":{"tf":1.0}},"df":2}}}},"t":{"docs":{"71":{"tf":1.7320508075688772},"72":{"tf":1.0},"81":{"tf":1.0}},"df":3,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{"81":{"tf":1.0}},"df":1}}}}}}}}}},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"81":{"tf":1.0}},"df":1}}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{"81":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"s":{"docs":{},"df":0,"y":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"k":{"docs":{},"df":0,"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"81":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"(":{"docs":{},"df":0,"1":{"docs":{},"df":0,"2":{"docs":{},"df":0,"_":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"_":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{"81":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}},"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"b":{"docs":{"90":{"tf":1.0}},"df":1}}}},"s":{"docs":{},"df":0,"r":{"docs":{"90":{"tf":1.0}},"df":1}},"v":{"docs":{},"df":0,"r":{"docs":{"90":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"v":{"docs":{},"df":0,"r":{"docs":{"90":{"tf":1.0}},"df":1}}}},"e":{"docs":{},"df":0,"m":{"docs":{"0":{"tf":1.0},"102":{"tf":1.0},"103":{"tf":1.4142135623730951},"105":{"tf":1.4142135623730951},"109":{"tf":1.0},"110":{"tf":1.0},"114":{"tf":1.7320508075688772},"117":{"tf":1.0},"119":{"tf":1.0},"12":{"tf":1.0},"120":{"tf":1.0},"123":{"tf":1.4142135623730951},"13":{"tf":1.7320508075688772},"130":{"tf":1.0},"131":{"tf":1.4142135623730951},"133":{"tf":1.0},"15":{"tf":1.0},"155":{"tf":1.0},"16":{"tf":1.0},"160":{"tf":1.7320508075688772},"161":{"tf":1.4142135623730951},"162":{"tf":1.0},"164":{"tf":1.0},"169":{"tf":1.0},"175":{"tf":1.0},"176":{"tf":1.0},"177":{"tf":1.4142135623730951},"178":{"tf":1.0},"179":{"tf":1.4142135623730951},"180":{"tf":1.0},"183":{"tf":1.0},"184":{"tf":1.4142135623730951},"19":{"tf":1.0},"199":{"tf":1.0},"22":{"tf":1.0},"24":{"tf":1.0},"26":{"tf":1.0},"27":{"tf":1.4142135623730951},"30":{"tf":1.0},"31":{"tf":1.0},"32":{"tf":1.4142135623730951},"4":{"tf":1.0},"43":{"tf":2.0},"62":{"tf":1.4142135623730951},"63":{"tf":2.449489742783178},"64":{"tf":1.7320508075688772},"68":{"tf":1.4142135623730951},"69":{"tf":1.4142135623730951},"70":{"tf":1.0},"77":{"tf":2.0},"78":{"tf":1.0},"79":{"tf":1.0},"80":{"tf":1.7320508075688772},"81":{"tf":1.7320508075688772},"83":{"tf":1.0},"86":{"tf":1.7320508075688772},"87":{"tf":1.0}},"df":57,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{"93":{"tf":2.449489742783178}},"df":1,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{"93":{"tf":1.7320508075688772}},"df":1}}}}}}}}}}}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"69":{"tf":1.0},"71":{"tf":1.4142135623730951},"80":{"tf":2.0},"81":{"tf":2.0},"90":{"tf":1.0},"91":{"tf":1.7320508075688772},"92":{"tf":2.449489742783178}},"df":7,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"v":{"docs":{},"df":0,"r":{"docs":{"91":{"tf":1.0}},"df":1}}}}},".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{"71":{"tf":1.0}},"df":1}}}}}}}}},"v":{"docs":{},"df":0,"r":{"docs":{"92":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"92":{"tf":1.0}},"df":1}}}}}}}},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"71":{"tf":1.0}},"df":1}}}}}}}}}}}},"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"71":{"tf":1.0}},"df":1}}}}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"s":{"docs":{},"df":0,"y":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"s":{"docs":{},"df":0,"y":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"k":{"docs":{},"df":0,"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"71":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"(":{"docs":{},"df":0,"1":{"docs":{},"df":0,"_":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{"71":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}},"t":{"docs":{"126":{"tf":1.0},"165":{"tf":2.449489742783178}},"df":2,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"158":{"tf":1.0},"61":{"tf":1.0},"62":{"tf":1.4142135623730951},"91":{"tf":1.0}},"df":4,"e":{"docs":{},"df":0,"—":{"docs":{},"df":0,"i":{"docs":{"84":{"tf":1.0}},"df":1}}}}},"g":{"docs":{},"df":0,"+":{"docs":{},"df":0,"=":{"docs":{},"df":0,"\\"":{"docs":{},"df":0,"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"44":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}},"i":{"docs":{},"df":0,"l":{"docs":{"61":{"tf":1.0},"67":{"tf":1.0},"81":{"tf":1.0}},"df":3}},"k":{"docs":{},"df":0,"e":{"docs":{"1":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.0},"11":{"tf":1.4142135623730951},"112":{"tf":1.0},"122":{"tf":1.0},"123":{"tf":1.0},"126":{"tf":1.4142135623730951},"127":{"tf":1.0},"129":{"tf":1.0},"138":{"tf":1.0},"15":{"tf":1.0},"151":{"tf":1.0},"158":{"tf":1.0},"161":{"tf":1.0},"165":{"tf":1.0},"173":{"tf":1.0},"44":{"tf":1.0},"60":{"tf":1.0},"64":{"tf":1.0},"69":{"tf":1.0},"70":{"tf":1.0},"72":{"tf":1.0},"74":{"tf":1.0},"75":{"tf":1.0},"77":{"tf":1.0},"88":{"tf":1.0}},"df":27,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"99":{"tf":1.4142135623730951}},"df":1,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"99":{"tf":1.0}},"df":1}}}}}}}}}}}},"n":{"docs":{"125":{"tf":1.0},"160":{"tf":1.0},"165":{"tf":1.0},"173":{"tf":1.0}},"df":4}}},"l":{"docs":{},"df":0,"k":{"docs":{"87":{"tf":1.0}},"df":1}},"r":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"12":{"tf":1.0},"124":{"tf":1.0},"162":{"tf":1.4142135623730951},"171":{"tf":1.0},"177":{"tf":1.4142135623730951},"178":{"tf":1.0},"180":{"tf":1.0},"181":{"tf":1.0},"183":{"tf":1.0},"187":{"tf":1.0},"20":{"tf":1.0},"26":{"tf":1.0},"30":{"tf":1.0},"31":{"tf":1.0},"32":{"tf":1.0},"38":{"tf":3.0},"4":{"tf":1.0},"53":{"tf":1.4142135623730951},"55":{"tf":1.0},"60":{"tf":1.0},"61":{"tf":3.605551275463989},"62":{"tf":1.0},"63":{"tf":1.4142135623730951},"64":{"tf":1.4142135623730951},"66":{"tf":1.4142135623730951},"67":{"tf":3.1622776601683795},"68":{"tf":2.6457513110645907},"84":{"tf":1.0}},"df":28,"(":{"docs":{"63":{"tf":1.0}},"df":1},".":{"docs":{},"df":0,"\'":{"docs":{},"df":0,"c":{"docs":{},"df":0,"f":{"docs":{},"df":0,"g":{"docs":{},"df":0,"(":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"(":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"190":{"tf":1.0},"68":{"tf":1.0}},"df":2}}}}}}}}}}}}}}}}}}}},"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"b":{"docs":{},"df":0,"v":{"docs":{},"df":0,"7":{"docs":{},"df":0,"m":{"docs":{"63":{"tf":1.0},"68":{"tf":1.0}},"df":2}}}}}}}}},"/":{"docs":{},"df":0,"$":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"/":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"g":{"docs":{},"df":0,"/":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"p":{"docs":{"62":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"m":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{},"df":0,"f":{"docs":{},"df":0,"3":{"docs":{},"df":0,"x":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"f":{"docs":{},"df":0,"g":{"docs":{"53":{"tf":1.7320508075688772},"68":{"tf":1.0}},"df":2}}}}}}}}}}}},"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"b":{"docs":{},"df":0,"v":{"docs":{},"df":0,"6":{"docs":{},"df":0,"m":{"docs":{"162":{"tf":1.0}},"df":1}},"7":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{"68":{"tf":1.4142135623730951}},"df":1}},"m":{"docs":{"62":{"tf":1.0},"63":{"tf":1.7320508075688772},"64":{"tf":1.4142135623730951}},"df":3}}}}}}}}},"_":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"158":{"tf":1.0}},"df":1}}}},"o":{"docs":{"190":{"tf":1.0},"68":{"tf":1.0}},"df":2}}}}}},"s":{"docs":{},"df":0,"k":{"docs":{"128":{"tf":1.7320508075688772},"161":{"tf":1.0},"165":{"tf":1.4142135623730951},"70":{"tf":1.0},"86":{"tf":1.0}},"df":5}}},"c":{"docs":{},"df":0,"p":{"docs":{"64":{"tf":1.4142135623730951},"68":{"tf":1.0}},"df":2,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"3":{"docs":{},"df":0,"3":{"docs":{},"df":0,"3":{"docs":{},"df":0,"3":{"docs":{"64":{"tf":1.4142135623730951}},"df":1}}}}}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"5":{"tf":1.0}},"df":1}},"m":{"docs":{"8":{"tf":1.0}},"df":1}},"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"n":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"69":{"tf":1.0},"71":{"tf":1.0},"90":{"tf":1.0}},"df":3},"q":{"docs":{},"df":0,"u":{"docs":{"78":{"tf":1.0},"80":{"tf":1.0}},"df":2}}}}}},"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{"113":{"tf":1.0},"174":{"tf":1.0}},"df":2}}}},"l":{"docs":{},"df":0,"l":{"docs":{"125":{"tf":1.0},"162":{"tf":1.0},"166":{"tf":1.0},"180":{"tf":1.0},"183":{"tf":1.0},"44":{"tf":1.0},"63":{"tf":2.23606797749979},"64":{"tf":1.7320508075688772},"68":{"tf":1.0},"83":{"tf":1.0},"91":{"tf":1.0}},"df":11}},"m":{"docs":{},"df":0,"p":{"docs":{"75":{"tf":1.4142135623730951}},"df":1,"2":{"docs":{"75":{"tf":1.4142135623730951}},"df":1},"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"118":{"tf":1.0}},"df":1}}}}}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"190":{"tf":1.0},"20":{"tf":2.0},"40":{"tf":1.0},"56":{"tf":1.0},"57":{"tf":1.0},"59":{"tf":1.0},"61":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.4142135623730951},"77":{"tf":1.0}},"df":10}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"75":{"tf":1.0}},"df":1,"l":{"docs":{},"df":0,"i":{"docs":{"123":{"tf":1.0},"155":{"tf":1.0}},"df":2}}}}}}}}},"n":{"docs":{},"df":0,"d":{"docs":{"190":{"tf":1.0}},"df":1}},"r":{"docs":{},"df":0,"m":{"docs":{"12":{"tf":1.0},"189":{"tf":1.0},"192":{"tf":1.0},"9":{"tf":1.4142135623730951}},"df":4,"i":{"docs":{},"df":0,"n":{"docs":{"119":{"tf":1.0},"174":{"tf":1.0},"53":{"tf":1.0},"64":{"tf":2.0},"68":{"tf":1.7320508075688772},"77":{"tf":1.4142135623730951},"81":{"tf":1.0}},"df":7}}}},"s":{"docs":{},"df":0,"t":{"docs":{"117":{"tf":1.0},"133":{"tf":1.0},"19":{"tf":2.0},"191":{"tf":1.4142135623730951},"27":{"tf":1.0},"58":{"tf":1.0},"77":{"tf":1.0},"78":{"tf":1.0}},"df":8}},"x":{"docs":{},"df":0,"a":{"docs":{"69":{"tf":1.0},"72":{"tf":1.0}},"df":2},"t":{"docs":{"160":{"tf":1.0},"188":{"tf":1.4142135623730951},"19":{"tf":1.0},"61":{"tf":1.4142135623730951},"62":{"tf":1.7320508075688772},"63":{"tf":1.4142135623730951},"68":{"tf":1.4142135623730951},"9":{"tf":1.0}},"df":8}}},"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"k":{"docs":{"124":{"tf":1.0},"22":{"tf":1.0}},"df":2}},"t":{"docs":{},"df":0,"’":{"docs":{"127":{"tf":1.0},"131":{"tf":1.0},"132":{"tf":1.0},"184":{"tf":1.0},"188":{"tf":1.0},"47":{"tf":1.0},"52":{"tf":1.0},"60":{"tf":1.0},"61":{"tf":1.0},"64":{"tf":2.0},"71":{"tf":1.0},"72":{"tf":1.0},"8":{"tf":1.0},"91":{"tf":1.0}},"df":14,"d":{"docs":{"60":{"tf":1.0}},"df":1},"l":{"docs":{},"df":0,"l":{"docs":{"60":{"tf":1.0}},"df":1}}}}},"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"_":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"98":{"tf":1.0}},"df":1,".":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{"98":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}},"m":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"v":{"docs":{"134":{"tf":1.0}},"df":1}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"125":{"tf":1.4142135623730951},"126":{"tf":1.0},"127":{"tf":1.0},"159":{"tf":1.0}},"df":4}}},"’":{"docs":{"121":{"tf":1.4142135623730951},"187":{"tf":1.0},"188":{"tf":1.0},"189":{"tf":1.0},"50":{"tf":1.0},"66":{"tf":1.0},"71":{"tf":1.0},"75":{"tf":1.0},"77":{"tf":1.0},"78":{"tf":1.0},"80":{"tf":1.0},"92":{"tf":1.4142135623730951}},"df":12,"r":{"docs":{"115":{"tf":1.0}},"df":1}}}}},"i":{"docs":{},"df":0,"n":{"docs":{"69":{"tf":1.0}},"df":1,"g":{"docs":{"14":{"tf":1.4142135623730951},"15":{"tf":1.0},"156":{"tf":1.0},"165":{"tf":1.0},"192":{"tf":1.0},"2":{"tf":1.0},"53":{"tf":1.0},"63":{"tf":1.0},"73":{"tf":1.0},"76":{"tf":1.4142135623730951}},"df":10}},"r":{"docs":{},"df":0,"d":{"docs":{"63":{"tf":1.0},"92":{"tf":1.0}},"df":2}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{"28":{"tf":1.0}},"df":1}}}}},"s":{"docs":{},"df":0,"e":{"docs":{"115":{"tf":1.0},"188":{"tf":1.0},"20":{"tf":1.0},"3":{"tf":1.0},"44":{"tf":1.0},"53":{"tf":1.0},"64":{"tf":1.0},"68":{"tf":1.0},"72":{"tf":1.0},"76":{"tf":1.0},"78":{"tf":1.0},"84":{"tf":1.0}},"df":12}},"u":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{"125":{"tf":1.0},"127":{"tf":1.0}},"df":2,"t":{"docs":{"106":{"tf":1.0}},"df":1}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"122":{"tf":1.0},"123":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":2.23606797749979},"126":{"tf":2.6457513110645907},"127":{"tf":1.0},"13":{"tf":1.0},"130":{"tf":1.4142135623730951},"131":{"tf":1.4142135623730951},"15":{"tf":1.0},"78":{"tf":1.4142135623730951}},"df":11,"1":{"docs":{"93":{"tf":1.0}},"df":1},"2":{"docs":{"93":{"tf":1.0}},"df":1}}},"e":{"docs":{"116":{"tf":1.0},"117":{"tf":1.0},"189":{"tf":1.0},"68":{"tf":1.0}},"df":4},"s":{"docs":{},"df":0,"h":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"d":{"docs":{"190":{"tf":2.449489742783178}},"df":1,"=":{"docs":{},"df":0,"1":{"docs":{},"df":0,"2":{"docs":{},"df":0,"3":{"docs":{"190":{"tf":1.0}},"df":1}}}}}}}}}},"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{"105":{"tf":1.0},"11":{"tf":1.4142135623730951},"110":{"tf":1.0},"122":{"tf":1.0},"14":{"tf":1.0},"176":{"tf":1.0},"187":{"tf":1.0},"26":{"tf":1.0},"28":{"tf":1.0},"29":{"tf":1.0},"32":{"tf":1.0},"54":{"tf":1.0},"86":{"tf":1.0},"92":{"tf":1.4142135623730951}},"df":14,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"99":{"tf":1.0}},"df":1}}}}}}}},"u":{"docs":{"136":{"tf":1.0},"80":{"tf":1.4142135623730951}},"df":2,"m":{"docs":{},"df":0,"b":{"docs":{},"df":0,"v":{"docs":{},"df":0,"6":{"docs":{"124":{"tf":1.0}},"df":1,"m":{"docs":{"162":{"tf":1.0},"38":{"tf":1.0},"61":{"tf":1.0},"67":{"tf":1.0}},"df":4}},"7":{"docs":{"124":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"m":{"docs":{"38":{"tf":1.7320508075688772},"61":{"tf":1.4142135623730951},"67":{"tf":2.0}},"df":3}},"m":{"docs":{"38":{"tf":1.0},"61":{"tf":2.23606797749979},"63":{"tf":1.0},"67":{"tf":1.0}},"df":4}},"8":{"docs":{},"df":0,"m":{"docs":{},"df":0,".":{"docs":{},"df":0,"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{"38":{"tf":1.0}},"df":1}}},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"38":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"76":{"tf":1.0}},"df":1,"/":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"49":{"tf":1.0}},"df":1}}}}}}}}},"g":{"docs":{},"df":0,"h":{"docs":{},"df":0,"t":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"87":{"tf":1.0}},"df":1}}}}},"m":{"docs":{"127":{"tf":1.4142135623730951}},"df":1,".":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"(":{"docs":{},"df":0,"1":{"docs":{},"df":0,".":{"docs":{},"df":0,"h":{"docs":{},"df":0,"z":{"docs":{"127":{"tf":1.0}},"df":1}}}}}}}}}}},"2":{"docs":{"127":{"tf":1.4142135623730951}},"df":1},"e":{"docs":{"102":{"tf":1.0},"103":{"tf":1.4142135623730951},"108":{"tf":1.0},"110":{"tf":1.0},"111":{"tf":1.0},"112":{"tf":1.0},"120":{"tf":1.4142135623730951},"122":{"tf":1.0},"123":{"tf":1.0},"124":{"tf":1.4142135623730951},"127":{"tf":2.0},"128":{"tf":1.7320508075688772},"129":{"tf":1.4142135623730951},"130":{"tf":1.4142135623730951},"138":{"tf":2.449489742783178},"151":{"tf":1.0},"154":{"tf":1.0},"157":{"tf":1.4142135623730951},"158":{"tf":1.7320508075688772},"159":{"tf":1.4142135623730951},"161":{"tf":1.4142135623730951},"164":{"tf":1.0},"166":{"tf":1.0},"173":{"tf":1.0},"175":{"tf":1.4142135623730951},"186":{"tf":1.0},"189":{"tf":1.0},"30":{"tf":1.0},"32":{"tf":1.0},"60":{"tf":1.0},"61":{"tf":1.0},"63":{"tf":1.0},"64":{"tf":1.0},"68":{"tf":1.4142135623730951},"76":{"tf":1.0},"81":{"tf":1.0},"83":{"tf":1.4142135623730951},"86":{"tf":1.0},"91":{"tf":1.0},"92":{"tf":1.4142135623730951},"94":{"tf":1.0},"95":{"tf":1.0}},"df":42,"r":{"docs":{"11":{"tf":1.0},"119":{"tf":1.0},"122":{"tf":1.4142135623730951},"123":{"tf":1.7320508075688772},"124":{"tf":1.0},"125":{"tf":1.0},"126":{"tf":1.0},"127":{"tf":2.449489742783178},"130":{"tf":1.0},"146":{"tf":1.0},"80":{"tf":1.0},"81":{"tf":1.4142135623730951},"90":{"tf":1.0}},"df":13,"(":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"0":{"docs":{"146":{"tf":1.0}},"df":1}}}}}}},"0":{"docs":{"146":{"tf":1.7320508075688772}},"df":1},"2":{"docs":{"84":{"tf":1.0}},"df":1,"a":{"docs":{"84":{"tf":1.4142135623730951}},"df":1}},"s":{"docs":{},"df":0,"/":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"n":{"docs":{"115":{"tf":1.0}},"df":1}}}}}}}}}}}}}},"p":{"docs":{"156":{"tf":1.0},"160":{"tf":1.0},"174":{"tf":1.4142135623730951},"77":{"tf":1.0}},"df":4},"v":{"docs":{},"df":0,"a":{"docs":{"69":{"tf":1.0}},"df":1}}},"l":{"docs":{},"df":0,";":{"docs":{},"df":0,"d":{"docs":{},"df":0,"r":{"docs":{"9":{"tf":1.0}},"df":1}}}},"m":{"docs":{},"df":0,"4":{"docs":{},"df":0,"c":{"docs":{},"df":0,"1":{"docs":{},"df":0,"2":{"docs":{},"df":0,"3":{"docs":{"69":{"tf":1.0},"72":{"tf":1.0}},"df":2,"x":{"docs":{"69":{"tf":1.0},"72":{"tf":1.4142135623730951},"73":{"tf":1.0},"74":{"tf":1.0}},"df":4,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"s":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{"72":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"72":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}},"_":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"76":{"tf":1.0}},"df":1,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"p":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"u":{"docs":{},"df":0,"d":{"docs":{"76":{"tf":1.0}},"df":1}}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"{":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{"76":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}},"y":{"docs":{},"df":0,"s":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"l":{"docs":{"76":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}},"p":{"docs":{},"df":0,"/":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"p":{"docs":{"57":{"tf":1.0},"67":{"tf":1.0}},"df":2}}}}}},"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"a":{"docs":{},"df":0,"y":{"docs":{"86":{"tf":1.0}},"df":1}},"o":{"docs":{"155":{"tf":2.0},"61":{"tf":1.0}},"df":2}},"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{"101":{"tf":1.0},"119":{"tf":1.0},"183":{"tf":1.0}},"df":3}}}},"k":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"125":{"tf":1.7320508075688772}},"df":1}}},"l":{"docs":{},"df":0,"d":{"docs":{"182":{"tf":1.0},"80":{"tf":1.0}},"df":2}},"o":{"docs":{},"df":0,"k":{"docs":{"53":{"tf":1.0},"68":{"tf":1.0}},"df":2},"l":{"docs":{"137":{"tf":1.0},"172":{"tf":1.0},"174":{"tf":1.0},"175":{"tf":1.0},"184":{"tf":1.0},"19":{"tf":2.0},"21":{"tf":2.449489742783178},"24":{"tf":1.4142135623730951},"26":{"tf":1.0},"27":{"tf":1.0},"31":{"tf":1.0},"36":{"tf":1.0},"37":{"tf":1.0},"39":{"tf":1.4142135623730951},"45":{"tf":1.0},"49":{"tf":1.4142135623730951},"53":{"tf":1.0},"55":{"tf":1.0},"63":{"tf":1.0},"65":{"tf":1.0}},"df":20,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"19":{"tf":1.0},"21":{"tf":1.0},"30":{"tf":1.0},"61":{"tf":1.4142135623730951}},"df":4}}}}},"s":{"docs":{},"df":0,"\\\\":{"docs":{},"df":0,"o":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"d":{"docs":{},"df":0,"\\\\":{"docs":{},"df":0,"0":{"docs":{},"df":0,".":{"docs":{},"df":0,"1":{"docs":{},"df":0,"0":{"docs":{},"df":0,".":{"docs":{},"df":0,"0":{"docs":{"50":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}},"p":{"docs":{"147":{"tf":1.4142135623730951},"158":{"tf":1.0},"188":{"tf":2.0},"195":{"tf":1.0}},"df":4,"i":{"docs":{},"df":0,"c":{"docs":{"114":{"tf":1.0},"125":{"tf":1.0},"131":{"tf":1.0},"3":{"tf":1.0},"5":{"tf":1.4142135623730951},"7":{"tf":1.4142135623730951}},"df":6}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"62":{"tf":1.0}},"df":1}}},"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{"3":{"tf":1.0}},"df":1}}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{"30":{"tf":1.7320508075688772},"86":{"tf":1.0}},"df":2,"3":{"docs":{},"df":0,"2":{"docs":{"30":{"tf":1.4142135623730951}},"df":1}}},"k":{"docs":{"112":{"tf":1.0},"126":{"tf":1.0},"127":{"tf":1.0},"128":{"tf":1.0},"67":{"tf":1.0},"81":{"tf":1.0},"98":{"tf":1.0}},"df":7,"e":{"docs":{},"df":0,"r":{"docs":{"169":{"tf":1.0},"170":{"tf":1.0},"8":{"tf":1.0}},"df":3}}}},"d":{"docs":{},"df":0,"e":{"docs":{"119":{"tf":1.0}},"df":1},"i":{"docs":{},"df":0,"t":{"docs":{"114":{"tf":1.0},"130":{"tf":1.0},"161":{"tf":1.0}},"df":3,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"161":{"tf":1.0}},"df":1}}}}}},"i":{"docs":{},"df":0,"t":{"docs":{"103":{"tf":1.0},"115":{"tf":2.6457513110645907},"117":{"tf":1.7320508075688772},"118":{"tf":1.7320508075688772},"125":{"tf":1.7320508075688772},"126":{"tf":1.0},"133":{"tf":1.0},"142":{"tf":1.7320508075688772},"148":{"tf":1.4142135623730951},"150":{"tf":1.0},"155":{"tf":2.449489742783178},"195":{"tf":1.0},"69":{"tf":1.0},"76":{"tf":1.7320508075688772}},"df":14}},"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"68":{"tf":1.4142135623730951}},"df":1}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"m":{"docs":{"160":{"tf":1.0},"168":{"tf":1.0},"183":{"tf":1.0},"80":{"tf":1.4142135623730951}},"df":4}}}},"i":{"docs":{},"df":0,"t":{"docs":{"105":{"tf":1.0},"109":{"tf":1.0},"110":{"tf":1.0},"122":{"tf":1.0}},"df":4}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"172":{"tf":1.4142135623730951},"173":{"tf":1.0},"6":{"tf":1.4142135623730951}},"df":3}}},"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"76":{"tf":1.0}},"df":1,"t":{"docs":{"118":{"tf":1.0},"200":{"tf":1.0},"201":{"tf":1.0}},"df":3}}}},"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"27":{"tf":1.0},"53":{"tf":2.0},"63":{"tf":1.0},"68":{"tf":2.0}},"df":4}}}}}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"s":{"docs":{"177":{"tf":1.0}},"df":1}}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"103":{"tf":1.0}},"df":1}},"e":{"docs":{"106":{"tf":1.0},"158":{"tf":1.0}},"df":2}},"i":{"docs":{"103":{"tf":1.0},"110":{"tf":1.0},"137":{"tf":1.0},"139":{"tf":1.0},"187":{"tf":1.0},"190":{"tf":1.4142135623730951},"2":{"tf":1.0},"53":{"tf":1.4142135623730951},"54":{"tf":1.0},"79":{"tf":1.4142135623730951},"99":{"tf":1.0}},"df":11,"a":{"docs":{},"df":0,"l":{"docs":{"115":{"tf":1.0}},"df":1}},"c":{"docs":{},"df":0,"k":{"docs":{"187":{"tf":1.0}},"df":1,"i":{"docs":{"126":{"tf":1.0},"170":{"tf":1.0}},"df":2}}},"g":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"122":{"tf":1.0},"136":{"tf":1.0},"175":{"tf":1.0},"80":{"tf":1.0},"81":{"tf":1.0},"83":{"tf":1.4142135623730951},"84":{"tf":1.4142135623730951}},"df":7}}}},"p":{"docs":{},"df":0,"l":{"docs":{"61":{"tf":1.4142135623730951}},"df":1}}},"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"8":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"e":{"docs":{"100":{"tf":1.0},"131":{"tf":1.0},"164":{"tf":1.0},"165":{"tf":1.4142135623730951},"187":{"tf":1.0}},"df":5},"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"62":{"tf":1.4142135623730951}},"df":1}}}},"s":{"docs":{},"df":0,"t":{"docs":{"93":{"tf":1.0}},"df":1}}},"y":{"docs":{},"df":0,"_":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"v":{"docs":{"136":{"tf":1.0}},"df":1}}}}}}}}},"t":{"docs":{},"df":0,"l":{"docs":{"5":{"tf":1.0}},"df":1}},"u":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{"146":{"tf":1.0}},"df":1}},"r":{"docs":{},"df":0,"n":{"docs":{"105":{"tf":1.0},"109":{"tf":1.0},"126":{"tf":1.0},"13":{"tf":1.0},"158":{"tf":1.0},"16":{"tf":1.0},"68":{"tf":1.0},"70":{"tf":1.0}},"df":8}},"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"55":{"tf":1.0}},"df":1}}}}},"w":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{"122":{"tf":1.0}},"df":1}}},"o":{"docs":{"101":{"tf":1.4142135623730951},"104":{"tf":1.0},"11":{"tf":1.0},"12":{"tf":1.0},"123":{"tf":1.0},"131":{"tf":1.0},"132":{"tf":1.0},"134":{"tf":1.0},"160":{"tf":1.0},"166":{"tf":1.7320508075688772},"168":{"tf":1.0},"171":{"tf":1.0},"179":{"tf":1.0},"190":{"tf":1.4142135623730951},"53":{"tf":1.0},"61":{"tf":1.0},"64":{"tf":1.0},"75":{"tf":1.0},"87":{"tf":1.4142135623730951},"92":{"tf":1.0},"96":{"tf":1.0}},"df":21}},"y":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{"103":{"tf":1.4142135623730951},"104":{"tf":1.0},"105":{"tf":1.7320508075688772},"109":{"tf":1.4142135623730951},"11":{"tf":1.0},"110":{"tf":1.0},"111":{"tf":1.4142135623730951},"112":{"tf":1.7320508075688772},"113":{"tf":1.0},"115":{"tf":1.4142135623730951},"117":{"tf":2.0},"118":{"tf":1.4142135623730951},"124":{"tf":1.0},"125":{"tf":1.4142135623730951},"126":{"tf":1.4142135623730951},"127":{"tf":1.0},"134":{"tf":1.4142135623730951},"142":{"tf":2.23606797749979},"146":{"tf":1.4142135623730951},"148":{"tf":1.4142135623730951},"150":{"tf":1.0},"153":{"tf":1.0},"154":{"tf":1.0},"155":{"tf":2.23606797749979},"165":{"tf":1.0},"166":{"tf":2.23606797749979},"168":{"tf":3.0},"172":{"tf":1.0},"173":{"tf":2.23606797749979},"174":{"tf":1.0},"180":{"tf":1.4142135623730951},"61":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.0},"69":{"tf":1.0},"73":{"tf":1.0},"80":{"tf":1.4142135623730951},"84":{"tf":1.0}},"df":38,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{"173":{"tf":1.0}},"df":1}}},"n":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{"134":{"tf":1.0}},"df":1}}},"s":{"docs":{},"df":0,"t":{"docs":{"104":{"tf":1.4142135623730951}},"df":1}}},"i":{"docs":{},"df":0,"c":{"docs":{"107":{"tf":1.0},"114":{"tf":1.0},"115":{"tf":1.0},"117":{"tf":1.0},"118":{"tf":1.0},"121":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.0},"173":{"tf":1.4142135623730951},"174":{"tf":1.0},"32":{"tf":1.0},"74":{"tf":1.0},"75":{"tf":1.0},"76":{"tf":1.0},"80":{"tf":1.0},"84":{"tf":1.0},"86":{"tf":1.0},"87":{"tf":1.0},"90":{"tf":1.0}},"df":19}},"o":{"docs":{"8":{"tf":1.0}},"df":1}}}},"u":{"docs":{"166":{"tf":1.4142135623730951}},"df":1,".":{"docs":{},"df":0,"i":{"docs":{"166":{"tf":1.0}},"df":1},"x":{"docs":{"166":{"tf":1.0}},"df":1},"z":{"docs":{"166":{"tf":1.0}},"df":1}},"1":{"docs":{},"df":0,"6":{"docs":{"166":{"tf":2.8284271247461903}},"df":1}},"3":{"docs":{},"df":0,"2":{"docs":{"100":{"tf":1.0},"101":{"tf":1.4142135623730951},"104":{"tf":2.0},"122":{"tf":1.0},"123":{"tf":1.0},"126":{"tf":1.0},"159":{"tf":1.0},"168":{"tf":1.7320508075688772},"80":{"tf":1.4142135623730951},"81":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.4142135623730951},"91":{"tf":2.0},"92":{"tf":1.0},"93":{"tf":1.4142135623730951}},"df":15}},"6":{"docs":{},"df":0,"4":{"docs":{"168":{"tf":1.0}},"df":1}},"8":{"docs":{"133":{"tf":1.7320508075688772},"134":{"tf":1.4142135623730951},"154":{"tf":1.4142135623730951},"166":{"tf":2.0}},"df":4},"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"5":{"tf":1.4142135623730951},"76":{"tf":1.7320508075688772}},"df":2}}},"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{"40":{"tf":1.0},"43":{"tf":1.4142135623730951}},"df":2}}}}},"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"v":{"docs":{"44":{"tf":1.0},"53":{"tf":1.0}},"df":2,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"m":{"docs":{"44":{"tf":1.0}},"df":1}}}}}},"h":{"docs":{"75":{"tf":1.0}},"df":1},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{},"df":0,"_":{"docs":{},"df":0,"t":{"docs":{"75":{"tf":1.4142135623730951}},"df":1}}}}}}},"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{"137":{"tf":1.0}},"df":1}}}},"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"53":{"tf":1.4142135623730951},"68":{"tf":1.4142135623730951},"98":{"tf":1.0}},"df":3}},"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"n":{"docs":{"166":{"tf":1.0}},"df":1}}}}},"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{"75":{"tf":1.0}},"df":1}}}},"o":{"docs":{},"df":0,"m":{"docs":{"63":{"tf":1.4142135623730951},"68":{"tf":1.4142135623730951}},"df":2},"n":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"104":{"tf":1.0},"109":{"tf":1.4142135623730951}},"df":2}}}}}}}},"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"107":{"tf":1.0},"122":{"tf":1.0},"78":{"tf":1.0},"80":{"tf":1.4142135623730951},"83":{"tf":1.0}},"df":5}}},"r":{"docs":{"117":{"tf":1.0},"147":{"tf":1.0},"164":{"tf":1.0},"28":{"tf":1.0},"53":{"tf":1.0},"68":{"tf":1.0},"9":{"tf":2.0}},"df":7,"l":{"docs":{},"df":0,"i":{"docs":{"108":{"tf":1.0},"164":{"tf":1.0},"76":{"tf":1.0}},"df":3}},"s":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"144":{"tf":1.0}},"df":1}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"160":{"tf":1.0},"183":{"tf":1.0},"192":{"tf":1.0},"77":{"tf":1.0}},"df":4}}}}}}}},"e":{"docs":{},"df":0,"x":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"107":{"tf":1.0},"146":{"tf":1.0},"151":{"tf":1.0}},"df":3}}}}}},"f":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"5":{"tf":1.0}},"df":1}}}}}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{"94":{"tf":1.0}},"df":1}}}}}},"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"l":{"docs":{"68":{"tf":1.0},"82":{"tf":1.0}},"df":2}}}}},"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"166":{"tf":1.0}},"df":1}},"t":{"docs":{"117":{"tf":1.0},"188":{"tf":1.0},"194":{"tf":1.0},"87":{"tf":1.0}},"df":4},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"s":{"docs":{"200":{"tf":1.0},"201":{"tf":1.0}},"df":2}}}},"x":{"docs":{"62":{"tf":1.0}},"df":1}},"k":{"docs":{},"df":0,"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"n":{"docs":{"62":{"tf":1.4142135623730951}},"df":1}}}}},"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"78":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"k":{"docs":{"122":{"tf":1.0},"26":{"tf":1.0},"86":{"tf":1.0}},"df":3},"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"35":{"tf":1.0}},"df":1}}}},"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"126":{"tf":1.4142135623730951}},"df":1}}}},"o":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{"187":{"tf":1.4142135623730951},"188":{"tf":1.0}},"df":2}}}}},"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"u":{"docs":{},"df":0,"g":{"docs":{"44":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{"189":{"tf":1.7320508075688772}},"df":1}}},"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"f":{"docs":{"101":{"tf":1.4142135623730951},"122":{"tf":2.0},"123":{"tf":1.7320508075688772},"125":{"tf":3.3166247903554},"126":{"tf":1.7320508075688772},"133":{"tf":2.0},"164":{"tf":1.0},"165":{"tf":2.23606797749979},"173":{"tf":1.0},"64":{"tf":1.0},"72":{"tf":1.7320508075688772},"80":{"tf":1.7320508075688772},"83":{"tf":1.0},"91":{"tf":1.0},"92":{"tf":2.449489742783178},"93":{"tf":1.7320508075688772},"98":{"tf":1.4142135623730951},"99":{"tf":2.0}},"df":18,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"125":{"tf":1.0},"126":{"tf":1.4142135623730951}},"df":2,"l":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"125":{"tf":1.0}},"df":1}}},"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{},"df":0,"(":{"docs":{},"df":0,"0":{"docs":{},"df":0,"x":{"docs":{},"df":0,"2":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{},"df":0,"_":{"docs":{},"df":0,"0":{"docs":{},"df":0,"1":{"docs":{},"df":0,"0":{"docs":{},"df":0,"0":{"docs":{"133":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}},"<":{"docs":{},"df":0,"u":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{"125":{"tf":1.0}},"df":1}},"s":{"docs":{"133":{"tf":1.0}},"df":1}}}}}}}}}},"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"n":{"docs":{"168":{"tf":1.4142135623730951}},"df":1}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"133":{"tf":1.0}},"df":1}}}}},"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"123":{"tf":1.4142135623730951},"127":{"tf":1.0},"165":{"tf":1.4142135623730951},"71":{"tf":1.0},"77":{"tf":1.0}},"df":5}}},"u":{"docs":{},"df":0,"s":{"docs":{"158":{"tf":1.0},"78":{"tf":1.0}},"df":2}},"w":{"docs":{},"df":0,"i":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{"127":{"tf":1.0}},"df":1}}}},"n":{"docs":{},"df":0,"d":{"docs":{"78":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"127":{"tf":1.7320508075688772},"136":{"tf":1.0},"191":{"tf":1.0}},"df":3}}}},"z":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{"59":{"tf":1.0}},"df":1}}}},"p":{"docs":{"108":{"tf":1.0},"115":{"tf":1.0},"12":{"tf":1.0},"120":{"tf":1.0},"125":{"tf":1.0},"133":{"tf":1.0},"137":{"tf":1.0},"139":{"tf":1.0},"15":{"tf":1.0},"2":{"tf":1.4142135623730951},"3":{"tf":1.4142135623730951},"64":{"tf":1.0},"68":{"tf":1.4142135623730951},"72":{"tf":1.0},"76":{"tf":2.23606797749979},"87":{"tf":1.4142135623730951},"90":{"tf":1.0},"93":{"tf":1.0},"99":{"tf":1.0}},"df":19,"/":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"n":{"docs":{"72":{"tf":1.0}},"df":1}}}}},"d":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"127":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.0},"67":{"tf":1.0}},"df":4}}},"f":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"134":{"tf":1.0}},"df":1}}}}},"o":{"docs":{},"df":0,"n":{"docs":{"64":{"tf":1.0},"87":{"tf":1.0}},"df":2}},"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"87":{"tf":1.0}},"df":1}}}},"s":{"docs":{"0":{"tf":1.0},"100":{"tf":1.4142135623730951},"101":{"tf":1.0},"103":{"tf":1.7320508075688772},"104":{"tf":1.7320508075688772},"107":{"tf":1.0},"108":{"tf":1.4142135623730951},"109":{"tf":2.23606797749979},"111":{"tf":1.4142135623730951},"112":{"tf":1.0},"113":{"tf":1.4142135623730951},"115":{"tf":2.0},"117":{"tf":1.4142135623730951},"119":{"tf":1.0},"12":{"tf":1.4142135623730951},"122":{"tf":1.4142135623730951},"123":{"tf":2.0},"124":{"tf":2.0},"125":{"tf":3.0},"126":{"tf":3.3166247903554},"127":{"tf":4.358898943540674},"128":{"tf":1.7320508075688772},"129":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.4142135623730951},"132":{"tf":1.7320508075688772},"133":{"tf":4.123105625617661},"134":{"tf":1.7320508075688772},"136":{"tf":1.7320508075688772},"137":{"tf":1.7320508075688772},"138":{"tf":1.0},"14":{"tf":1.7320508075688772},"140":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":1.0},"144":{"tf":1.0},"15":{"tf":1.0},"151":{"tf":1.0},"155":{"tf":1.7320508075688772},"156":{"tf":1.4142135623730951},"157":{"tf":1.7320508075688772},"158":{"tf":2.0},"159":{"tf":1.4142135623730951},"16":{"tf":1.0},"160":{"tf":1.7320508075688772},"161":{"tf":1.7320508075688772},"162":{"tf":1.4142135623730951},"163":{"tf":1.4142135623730951},"164":{"tf":2.0},"165":{"tf":2.0},"166":{"tf":2.449489742783178},"167":{"tf":1.4142135623730951},"168":{"tf":1.0},"169":{"tf":1.0},"17":{"tf":1.7320508075688772},"170":{"tf":1.0},"171":{"tf":1.7320508075688772},"172":{"tf":1.0},"173":{"tf":2.23606797749979},"174":{"tf":2.449489742783178},"175":{"tf":1.7320508075688772},"176":{"tf":1.4142135623730951},"177":{"tf":1.4142135623730951},"178":{"tf":1.0},"179":{"tf":1.4142135623730951},"181":{"tf":1.4142135623730951},"182":{"tf":1.0},"183":{"tf":1.7320508075688772},"184":{"tf":1.4142135623730951},"187":{"tf":1.7320508075688772},"188":{"tf":1.4142135623730951},"189":{"tf":1.0},"19":{"tf":1.7320508075688772},"190":{"tf":3.0},"191":{"tf":2.23606797749979},"192":{"tf":1.0},"197":{"tf":1.0},"199":{"tf":1.0},"2":{"tf":1.7320508075688772},"20":{"tf":1.0},"21":{"tf":1.7320508075688772},"22":{"tf":1.4142135623730951},"27":{"tf":1.0},"29":{"tf":1.0},"30":{"tf":1.0},"31":{"tf":1.0},"32":{"tf":1.0},"33":{"tf":1.0},"34":{"tf":1.0},"38":{"tf":1.4142135623730951},"4":{"tf":1.0},"40":{"tf":1.0},"41":{"tf":1.0},"43":{"tf":1.7320508075688772},"44":{"tf":2.0},"45":{"tf":1.0},"46":{"tf":1.0},"50":{"tf":1.0},"53":{"tf":2.6457513110645907},"54":{"tf":1.4142135623730951},"55":{"tf":1.4142135623730951},"56":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":3.0},"61":{"tf":1.7320508075688772},"62":{"tf":2.23606797749979},"63":{"tf":3.0},"64":{"tf":2.6457513110645907},"66":{"tf":1.4142135623730951},"67":{"tf":2.0},"68":{"tf":3.605551275463989},"69":{"tf":2.23606797749979},"7":{"tf":1.0},"70":{"tf":1.0},"71":{"tf":2.23606797749979},"72":{"tf":2.8284271247461903},"75":{"tf":1.7320508075688772},"76":{"tf":2.449489742783178},"77":{"tf":3.7416573867739413},"78":{"tf":3.605551275463989},"79":{"tf":1.7320508075688772},"80":{"tf":2.449489742783178},"81":{"tf":2.6457513110645907},"82":{"tf":1.0},"83":{"tf":2.449489742783178},"84":{"tf":2.8284271247461903},"86":{"tf":1.4142135623730951},"87":{"tf":1.7320508075688772},"88":{"tf":1.0},"9":{"tf":1.4142135623730951},"91":{"tf":1.0},"92":{"tf":1.4142135623730951},"93":{"tf":1.0},"95":{"tf":1.0},"99":{"tf":1.0}},"df":135,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"115":{"tf":1.0}},"df":1}},"g":{"docs":{"13":{"tf":1.0},"137":{"tf":2.0},"174":{"tf":1.0},"188":{"tf":1.0},"38":{"tf":1.0}},"df":5,"e":{"docs":{},"df":0,"f":{"docs":{},"df":0,"a":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{"62":{"tf":1.0}},"df":1}}}}}}},"r":{"docs":{},"df":0,"t":{"docs":{"11":{"tf":1.0}},"df":1}}},"b":{"docs":{"11":{"tf":2.23606797749979},"33":{"tf":1.0},"5":{"tf":1.0},"52":{"tf":1.0},"53":{"tf":2.0}},"df":5},"e":{"docs":{},"df":0,"r":{"docs":{"11":{"tf":1.4142135623730951},"114":{"tf":1.0},"116":{"tf":1.0},"117":{"tf":1.0},"119":{"tf":1.0},"133":{"tf":1.7320508075688772},"155":{"tf":1.0},"158":{"tf":1.4142135623730951},"160":{"tf":1.0},"44":{"tf":1.4142135623730951},"53":{"tf":1.0},"69":{"tf":1.0},"76":{"tf":1.0},"78":{"tf":1.4142135623730951},"83":{"tf":1.0},"93":{"tf":1.7320508075688772}},"df":16,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"r":{"docs":{},"df":0,"w":{"docs":{"44":{"tf":1.0}},"df":1}}},"y":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,":":{"docs":{},"df":0,"r":{"docs":{},"df":0,"w":{"docs":{"44":{"tf":1.0}},"df":1}}}}}}},"’":{"docs":{"174":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"z":{"docs":{"133":{"tf":1.0},"159":{"tf":1.0}},"df":2}},"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"102":{"tf":1.0},"120":{"tf":1.0},"122":{"tf":1.0},"126":{"tf":1.0},"127":{"tf":1.0},"13":{"tf":1.0},"164":{"tf":1.0},"165":{"tf":1.0},"166":{"tf":1.0},"180":{"tf":1.0},"186":{"tf":1.0},"187":{"tf":1.0},"188":{"tf":1.0},"193":{"tf":1.0},"195":{"tf":1.0},"197":{"tf":1.4142135623730951},"63":{"tf":1.0},"84":{"tf":1.0}},"df":18}}}},"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"168":{"tf":1.0},"178":{"tf":1.0}},"df":2}}}},"v":{"docs":{"166":{"tf":2.23606797749979},"30":{"tf":1.0},"35":{"tf":1.0},"38":{"tf":1.4142135623730951},"49":{"tf":1.0},"50":{"tf":1.0},"62":{"tf":1.0}},"df":7,".":{"docs":{},"df":0,"i":{"docs":{"166":{"tf":1.7320508075688772}},"df":1},"x":{"docs":{"166":{"tf":2.0}},"df":1},"z":{"docs":{"166":{"tf":1.7320508075688772}},"df":1}},"0":{"docs":{},"df":0,".":{"docs":{},"df":0,"1":{"docs":{},"df":0,".":{"docs":{},"df":0,"0":{"docs":{"63":{"tf":1.0}},"df":1}},"0":{"docs":{},"df":0,".":{"docs":{},"df":0,"0":{"docs":{"19":{"tf":1.0}},"df":1}}}},"4":{"docs":{},"df":0,".":{"docs":{},"df":0,"x":{"docs":{"134":{"tf":1.4142135623730951}},"df":1}}},"5":{"docs":{},"df":0,".":{"docs":{},"df":0,"x":{"docs":{"100":{"tf":1.0}},"df":1}}},"9":{"docs":{},"df":0,".":{"docs":{},"df":0,"0":{"docs":{"19":{"tf":1.0}},"df":1}}}}},"1":{"docs":{},"df":0,"5":{"docs":{"53":{"tf":1.0},"68":{"tf":1.0}},"df":2}},"2":{"docs":{"53":{"tf":2.449489742783178},"68":{"tf":2.0}},"df":2,".":{"docs":{},"df":0,"0":{"docs":{"9":{"tf":1.0}},"df":1},"c":{"docs":{},"df":0,"f":{"docs":{},"df":0,"g":{"docs":{"53":{"tf":1.4142135623730951},"68":{"tf":1.4142135623730951}},"df":2}}}},"7":{"docs":{"53":{"tf":1.0},"68":{"tf":1.0}},"df":2}},"4":{"docs":{},"df":0,".":{"docs":{},"df":0,"0":{"docs":{"9":{"tf":1.4142135623730951}},"df":1}}},"a":{"docs":{},"df":0,"l":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0}},"df":2,"i":{"docs":{},"df":0,"d":{"docs":{"125":{"tf":1.0},"72":{"tf":1.0},"87":{"tf":1.0}},"df":3}},"u":{"docs":{"107":{"tf":1.4142135623730951},"108":{"tf":1.0},"112":{"tf":1.0},"125":{"tf":1.0},"126":{"tf":1.4142135623730951},"127":{"tf":1.4142135623730951},"151":{"tf":1.0},"164":{"tf":1.4142135623730951},"165":{"tf":1.0},"168":{"tf":1.0},"187":{"tf":1.0},"188":{"tf":1.0},"190":{"tf":1.4142135623730951},"28":{"tf":1.0},"61":{"tf":1.4142135623730951},"62":{"tf":1.7320508075688772},"72":{"tf":1.0},"74":{"tf":1.0},"75":{"tf":1.4142135623730951},"80":{"tf":1.0},"81":{"tf":1.0},"82":{"tf":1.4142135623730951},"83":{"tf":2.23606797749979},"90":{"tf":1.7320508075688772}},"df":24}},"r":{"docs":{},"df":0,"i":{"docs":{"114":{"tf":1.0},"70":{"tf":1.0}},"df":2,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"122":{"tf":1.7320508075688772},"123":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":1.4142135623730951},"126":{"tf":1.7320508075688772},"127":{"tf":2.23606797749979},"128":{"tf":1.0},"134":{"tf":1.0},"137":{"tf":1.0},"160":{"tf":1.0},"165":{"tf":2.0},"166":{"tf":1.0},"187":{"tf":1.4142135623730951},"28":{"tf":1.0},"31":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.0},"61":{"tf":1.7320508075688772},"62":{"tf":1.4142135623730951},"75":{"tf":1.0},"80":{"tf":2.6457513110645907},"81":{"tf":1.0},"84":{"tf":1.0},"87":{"tf":1.0},"91":{"tf":1.0},"92":{"tf":1.4142135623730951},"98":{"tf":1.7320508075688772},"99":{"tf":1.0}},"df":28}},"n":{"docs":{},"df":0,"t":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0},"22":{"tf":1.0},"63":{"tf":1.0}},"df":4}}},"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{"11":{"tf":1.0},"156":{"tf":1.0},"157":{"tf":1.0},"160":{"tf":1.0},"31":{"tf":1.0},"36":{"tf":1.0},"84":{"tf":1.0}},"df":7}}},"o":{"docs":{},"df":0,"u":{"docs":{"117":{"tf":1.4142135623730951},"118":{"tf":1.0},"119":{"tf":1.0},"120":{"tf":1.0},"129":{"tf":1.0},"13":{"tf":1.0},"140":{"tf":1.0},"157":{"tf":1.0},"163":{"tf":1.0},"26":{"tf":1.0},"27":{"tf":1.0},"28":{"tf":1.0},"30":{"tf":1.0},"35":{"tf":1.0},"69":{"tf":1.4142135623730951},"73":{"tf":1.0},"74":{"tf":1.0}},"df":17}}}}},"e":{"docs":{},"df":0,"c":{"docs":{"132":{"tf":1.0},"17":{"tf":1.0}},"df":2,".":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{"136":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"1":{"docs":{"136":{"tf":1.0}},"df":1}}}}}}}}}},":":{"docs":{},"df":0,":":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{"133":{"tf":1.0},"134":{"tf":1.0}},"df":2}}}}},"<":{"docs":{},"df":0,"_":{"docs":{"134":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"134":{"tf":1.0},"189":{"tf":1.0},"61":{"tf":1.0},"62":{"tf":1.0},"84":{"tf":1.0}},"df":5,"_":{"docs":{},"df":0,"t":{"docs":{"188":{"tf":1.4142135623730951},"61":{"tf":1.0},"62":{"tf":1.4142135623730951},"68":{"tf":1.4142135623730951}},"df":4}}}}}},"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"114":{"tf":1.0},"192":{"tf":1.0},"7":{"tf":1.4142135623730951},"84":{"tf":1.0}},"df":4}}}},"r":{"docs":{},"df":0,"i":{"docs":{"114":{"tf":1.0},"115":{"tf":1.0},"125":{"tf":1.4142135623730951},"151":{"tf":1.0},"157":{"tf":1.0},"159":{"tf":1.0},"163":{"tf":1.0},"184":{"tf":1.0},"72":{"tf":1.0},"8":{"tf":1.0},"91":{"tf":1.0},"94":{"tf":1.0}},"df":12,"f":{"docs":{},"df":0,"i":{"docs":{"49":{"tf":1.0},"50":{"tf":1.0},"68":{"tf":1.4142135623730951},"84":{"tf":1.0},"93":{"tf":1.0}},"df":5}}},"s":{"docs":{},"df":0,"a":{"docs":{"168":{"tf":1.0}},"df":1,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"31":{"tf":1.0},"35":{"tf":1.0},"36":{"tf":1.0}},"df":3}}}},"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"100":{"tf":1.0},"134":{"tf":1.4142135623730951},"166":{"tf":1.0},"183":{"tf":1.0},"19":{"tf":2.6457513110645907},"21":{"tf":1.0},"38":{"tf":1.0},"46":{"tf":1.0},"52":{"tf":1.0},"53":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.0},"62":{"tf":2.23606797749979},"64":{"tf":1.0},"68":{"tf":1.0},"77":{"tf":2.23606797749979},"84":{"tf":1.0}},"df":17}}}}}},"i":{"docs":{},"df":0,"a":{"docs":{"114":{"tf":1.0},"117":{"tf":2.23606797749979},"118":{"tf":1.0},"119":{"tf":1.0},"127":{"tf":1.4142135623730951},"176":{"tf":1.0},"30":{"tf":1.0},"31":{"tf":1.0},"34":{"tf":1.0},"80":{"tf":1.0},"86":{"tf":1.0}},"df":11},"c":{"docs":{},"df":0,"e":{"docs":{"168":{"tf":1.0}},"df":1}},"d":{"docs":{"53":{"tf":1.0},"68":{"tf":1.0}},"df":2,"e":{"docs":{},"df":0,"o":{"docs":{"193":{"tf":1.0},"195":{"tf":1.0},"197":{"tf":1.0},"72":{"tf":1.0},"86":{"tf":1.0}},"df":5}}},"e":{"docs":{},"df":0,"w":{"docs":{"199":{"tf":1.4142135623730951}},"df":1}},"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"139":{"tf":1.0},"87":{"tf":1.0}},"df":2}}}}},"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"124":{"tf":1.0},"98":{"tf":1.0}},"df":2}}},"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"24":{"tf":1.0},"26":{"tf":1.0},"29":{"tf":1.0},"39":{"tf":1.4142135623730951}},"df":4}}}}},"o":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{"165":{"tf":1.4142135623730951},"168":{"tf":1.0},"173":{"tf":1.0},"184":{"tf":1.0}},"df":4}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"165":{"tf":2.8284271247461903},"92":{"tf":1.7320508075688772},"95":{"tf":1.0}},"df":3,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"g":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"92":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"{":{"docs":{},"df":0,"r":{"docs":{},"df":0,"w":{"docs":{"92":{"tf":1.0},"93":{"tf":1.0}},"df":2}}}}}}}}}}}}}}}}}}},"t":{"docs":{"11":{"tf":1.0}},"df":1,"a":{"docs":{},"df":0,"g":{"docs":{"11":{"tf":1.0},"53":{"tf":1.0},"68":{"tf":1.0}},"df":3}}},"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"6":{"tf":1.0}},"df":1}}}}},"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"k":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{"86":{"tf":1.0}},"df":1}}}}}},"w":{"docs":{"107":{"tf":2.0},"108":{"tf":2.0},"109":{"tf":2.6457513110645907},"112":{"tf":1.0},"127":{"tf":1.7320508075688772},"74":{"tf":1.4142135623730951},"75":{"tf":1.7320508075688772}},"df":7,".":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"c":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"u":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"z":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"c":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"72":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"s":{"docs":{},"df":0,"(":{"docs":{},"df":0,"6":{"docs":{},"df":0,"4":{"docs":{"72":{"tf":1.0}},"df":1}}}}}}}}}}}}}}},"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"(":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0}},"df":2}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"72":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}},"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0}},"df":2}}}}}}}}}}}}}}}},".":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{"109":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"n":{"docs":{"109":{"tf":1.4142135623730951}},"df":1}}}}}}}}},"g":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"s":{"docs":{},"df":0,"y":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"0":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"_":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"72":{"tf":1.0},"74":{"tf":1.0},"75":{"tf":1.0}},"df":3}}}}}}}}}}}}}}}}}}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{},"df":0,"_":{"docs":{},"df":0,"z":{"docs":{"109":{"tf":1.0},"112":{"tf":1.0}},"df":2}}}}}},"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"_":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{"109":{"tf":1.0}},"df":1}}}},"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"109":{"tf":1.0}},"df":1}}}}}}}},"v":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"(":{"docs":{},"df":0,"v":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0}},"df":2}}}}}}}}}}}}}}}}}}}}}}}}},"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"s":{"docs":{},"df":0,"(":{"docs":{},"df":0,"2":{"docs":{},"df":0,"6":{"docs":{},"df":0,"3":{"docs":{"72":{"tf":1.0}},"df":1}}}}}}}}}}}}}}},"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"r":{"docs":{},"df":0,"1":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"_":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"127":{"tf":1.0}},"df":1}}}}}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"127":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}},"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,".":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"(":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0}},"df":2}}}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{"109":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}},"p":{"docs":{},"df":0,"w":{"docs":{},"df":0,"m":{"docs":{},"df":0,"4":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"(":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"72":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}},"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"126":{"tf":1.4142135623730951},"165":{"tf":1.4142135623730951},"64":{"tf":1.4142135623730951},"68":{"tf":1.0}},"df":4}},"l":{"docs":{},"df":0,"k":{"docs":{"105":{"tf":1.0},"110":{"tf":1.0},"54":{"tf":1.0}},"df":3}},"n":{"docs":{},"df":0,"t":{"docs":{"104":{"tf":1.0},"106":{"tf":1.4142135623730951},"11":{"tf":1.0},"125":{"tf":1.0},"132":{"tf":1.0},"160":{"tf":1.0},"180":{"tf":1.0},"183":{"tf":1.0},"188":{"tf":1.0},"190":{"tf":1.4142135623730951},"191":{"tf":1.0},"3":{"tf":1.0},"5":{"tf":1.0},"60":{"tf":1.0},"61":{"tf":1.4142135623730951},"64":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.0},"70":{"tf":1.0},"9":{"tf":1.0},"92":{"tf":1.0}},"df":21}},"r":{"docs":{},"df":0,"n":{"docs":{"64":{"tf":1.4142135623730951},"78":{"tf":1.0}},"df":2}},"s":{"docs":{},"df":0,"n":{"docs":{},"df":0,"\'":{"docs":{},"df":0,"t":{"docs":{"127":{"tf":1.0}},"df":1}}},"t":{"docs":{"108":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"130":{"tf":1.0}},"df":1,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"53":{"tf":1.4142135623730951},"68":{"tf":1.0}},"df":2}}}}}}}},"y":{"docs":{"101":{"tf":1.0},"104":{"tf":1.0},"105":{"tf":1.0},"109":{"tf":1.4142135623730951},"114":{"tf":1.4142135623730951},"15":{"tf":1.0},"155":{"tf":1.0},"189":{"tf":1.0},"190":{"tf":1.0},"60":{"tf":1.0},"64":{"tf":1.0},"68":{"tf":1.0},"72":{"tf":1.0},"76":{"tf":1.0},"84":{"tf":1.4142135623730951},"9":{"tf":1.0},"91":{"tf":1.0},"92":{"tf":1.4142135623730951}},"df":18}},"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"138":{"tf":1.4142135623730951}},"df":1}}},"e":{"docs":{},"df":0,"\'":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{"127":{"tf":1.4142135623730951}},"df":1}},"v":{"docs":{"127":{"tf":1.0}},"df":1}},"b":{"docs":{"20":{"tf":1.0}},"df":1,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"11":{"tf":1.0},"129":{"tf":1.0},"51":{"tf":1.0}},"df":3}}}},"l":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"0":{"tf":1.0},"8":{"tf":1.0}},"df":2}}},"l":{"docs":{"114":{"tf":1.0},"115":{"tf":1.4142135623730951},"123":{"tf":1.0},"130":{"tf":1.0},"146":{"tf":1.0},"16":{"tf":1.0},"160":{"tf":1.0},"69":{"tf":1.4142135623730951},"86":{"tf":1.0},"96":{"tf":1.4142135623730951}},"df":10}},"’":{"docs":{},"df":0,"d":{"docs":{"123":{"tf":1.0},"75":{"tf":1.0},"91":{"tf":1.0}},"df":3},"l":{"docs":{},"df":0,"l":{"docs":{"10":{"tf":1.0},"125":{"tf":1.0},"127":{"tf":1.7320508075688772},"131":{"tf":1.0},"132":{"tf":1.0},"133":{"tf":1.0},"19":{"tf":1.7320508075688772},"40":{"tf":1.0},"53":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.4142135623730951},"56":{"tf":1.0},"63":{"tf":1.0},"64":{"tf":1.0},"65":{"tf":1.0},"66":{"tf":1.4142135623730951},"67":{"tf":1.7320508075688772},"68":{"tf":1.4142135623730951},"99":{"tf":1.0}},"df":19}},"r":{"docs":{"122":{"tf":1.0},"123":{"tf":1.4142135623730951},"126":{"tf":1.4142135623730951},"69":{"tf":1.0},"72":{"tf":1.7320508075688772}},"df":5},"v":{"docs":{"112":{"tf":1.0},"122":{"tf":1.0},"125":{"tf":1.0},"126":{"tf":1.4142135623730951},"72":{"tf":1.0},"92":{"tf":1.4142135623730951}},"df":6}}},"f":{"docs":{},"df":0,"i":{"docs":{"127":{"tf":1.0},"165":{"tf":1.0}},"df":2}},"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"20":{"tf":1.0}},"df":1}}},"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"v":{"docs":{"122":{"tf":1.0}},"df":1}},"’":{"docs":{"137":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"115":{"tf":1.0}},"df":1}},"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"v":{"docs":{"120":{"tf":1.4142135623730951},"55":{"tf":1.0}},"df":2}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{"139":{"tf":1.0},"84":{"tf":1.0}},"df":2}}},"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"102":{"tf":1.4142135623730951},"123":{"tf":1.0},"134":{"tf":1.0},"137":{"tf":1.0},"164":{"tf":1.0},"168":{"tf":1.0},"190":{"tf":1.0},"88":{"tf":1.0},"92":{"tf":1.0}},"df":9}}}},"w":{"docs":{"127":{"tf":1.0}},"df":1}},"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"!":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"165":{"tf":1.0}},"df":1}}}}}}},"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"u":{"docs":{"165":{"tf":1.0}},"df":1}}}}}}},"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{"113":{"tf":1.0},"114":{"tf":1.0},"98":{"tf":1.0}},"df":3}},"s":{"docs":{},"df":0,"e":{"docs":{"62":{"tf":1.4142135623730951}},"df":1}}}},"i":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{"12":{"tf":1.0},"130":{"tf":1.0},"27":{"tf":1.0},"30":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.0},"63":{"tf":1.0}},"df":7},"t":{"docs":{},"df":0,"h":{"docs":{"90":{"tf":1.0}},"df":1}}},"k":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"a":{"docs":{"114":{"tf":1.4142135623730951},"97":{"tf":1.0}},"df":2}}}}}}},"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"174":{"tf":1.0},"39":{"tf":1.0},"41":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.0}},"df":5}}}},"r":{"docs":{},"df":0,"e":{"docs":{"27":{"tf":1.0},"34":{"tf":1.0},"77":{"tf":1.0}},"df":3}},"s":{"docs":{},"df":0,"h":{"docs":{"161":{"tf":1.0},"75":{"tf":1.0},"86":{"tf":1.0}},"df":3}},"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"<":{"docs":{},"df":0,"n":{"docs":{"155":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}},"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"<":{"docs":{},"df":0,"n":{"docs":{"155":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}},"{":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,",":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"}":{"docs":{},"df":0,"_":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"155":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{"133":{"tf":1.0},"173":{"tf":1.0},"196":{"tf":1.0},"64":{"tf":1.0},"7":{"tf":1.0},"73":{"tf":1.0},"74":{"tf":1.0},"77":{"tf":1.0},"80":{"tf":1.4142135623730951},"9":{"tf":1.4142135623730951}},"df":10}},"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"105":{"tf":1.4142135623730951},"122":{"tf":1.0},"123":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":1.4142135623730951},"126":{"tf":1.0},"128":{"tf":1.0},"133":{"tf":1.0},"14":{"tf":1.0},"155":{"tf":1.0},"173":{"tf":1.0},"188":{"tf":1.4142135623730951},"191":{"tf":1.0},"29":{"tf":1.0},"3":{"tf":1.0},"38":{"tf":1.0},"44":{"tf":1.0},"54":{"tf":1.0},"64":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.0},"76":{"tf":1.0},"77":{"tf":1.0},"78":{"tf":1.4142135623730951},"80":{"tf":1.0}},"df":25}}}}}},"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"\'":{"docs":{},"df":0,"t":{"docs":{"101":{"tf":1.0}},"df":1}},"’":{"docs":{},"df":0,"t":{"docs":{"122":{"tf":1.0},"125":{"tf":1.0},"15":{"tf":1.0},"187":{"tf":1.0},"52":{"tf":1.0},"53":{"tf":1.0},"60":{"tf":1.0},"64":{"tf":1.4142135623730951},"70":{"tf":1.0},"72":{"tf":1.0},"78":{"tf":1.0},"83":{"tf":1.4142135623730951}},"df":12}}},"r":{"docs":{},"df":0,"d":{"docs":{"11":{"tf":1.0},"55":{"tf":1.0},"88":{"tf":1.4142135623730951}},"df":3},"k":{"docs":{"10":{"tf":1.0},"101":{"tf":1.4142135623730951},"102":{"tf":1.0},"114":{"tf":1.0},"126":{"tf":1.0},"160":{"tf":1.0},"170":{"tf":1.0},"19":{"tf":1.0},"26":{"tf":1.0},"30":{"tf":1.0},"33":{"tf":1.4142135623730951},"34":{"tf":1.0},"5":{"tf":1.0},"52":{"tf":1.0},"53":{"tf":2.0},"63":{"tf":1.4142135623730951},"68":{"tf":1.0},"70":{"tf":1.0},"76":{"tf":1.0},"77":{"tf":1.0},"8":{"tf":1.0},"83":{"tf":1.4142135623730951},"88":{"tf":1.0},"9":{"tf":1.0},"92":{"tf":1.0},"93":{"tf":1.0}},"df":26,"f":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"26":{"tf":1.0}},"df":1}}}}},"l":{"docs":{},"df":0,"d":{"docs":{"63":{"tf":1.4142135623730951},"64":{"tf":2.0},"68":{"tf":1.4142135623730951},"69":{"tf":1.0},"77":{"tf":2.0},"86":{"tf":1.0},"94":{"tf":1.0}},"df":7,"!":{"docs":{},"df":0,"\\"":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"64":{"tf":1.4142135623730951},"67":{"tf":1.0},"68":{"tf":1.0},"77":{"tf":1.0}},"df":4}}}}}}}}},"\\\\":{"docs":{},"df":0,"r":{"docs":{},"df":0,"\\\\":{"docs":{},"df":0,"n":{"docs":{},"df":0,"\\"":{"docs":{},"df":0,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"76":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}},"r":{"docs":{},"df":0,"i":{"docs":{"121":{"tf":1.0},"93":{"tf":1.0}},"df":2}},"s":{"docs":{},"df":0,"t":{"docs":{"138":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"h":{"docs":{"123":{"tf":1.0},"158":{"tf":1.0},"160":{"tf":1.0},"163":{"tf":1.0},"165":{"tf":1.0}},"df":5}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"123":{"tf":1.0},"126":{"tf":1.0},"150":{"tf":1.0},"171":{"tf":1.0},"76":{"tf":1.4142135623730951},"80":{"tf":1.4142135623730951},"93":{"tf":1.0},"99":{"tf":1.0}},"df":8,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"125":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":1.4142135623730951},"69":{"tf":1.4142135623730951}},"df":4}}}}},"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{"107":{"tf":1.4142135623730951},"108":{"tf":1.0},"114":{"tf":1.0},"12":{"tf":1.0},"120":{"tf":1.0},"121":{"tf":1.0},"122":{"tf":1.0},"123":{"tf":1.4142135623730951},"124":{"tf":1.4142135623730951},"130":{"tf":1.0},"141":{"tf":1.4142135623730951},"156":{"tf":1.0},"159":{"tf":1.0},"164":{"tf":1.4142135623730951},"165":{"tf":1.4142135623730951},"17":{"tf":1.0},"174":{"tf":1.0},"183":{"tf":1.0},"2":{"tf":1.0},"28":{"tf":1.4142135623730951},"54":{"tf":1.0},"55":{"tf":1.0},"72":{"tf":1.0},"74":{"tf":1.4142135623730951},"75":{"tf":1.4142135623730951},"77":{"tf":1.4142135623730951},"87":{"tf":1.4142135623730951},"88":{"tf":2.0},"92":{"tf":2.6457513110645907},"94":{"tf":1.0},"95":{"tf":1.4142135623730951}},"df":31,"!":{"docs":{},"df":0,"(":{"docs":{},"df":0,"h":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"81":{"tf":1.0}},"df":1}}}}}}}}},"_":{"docs":{},"df":0,"b":{"docs":{},"df":0,"y":{"docs":{},"df":0,"t":{"docs":{"69":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"s":{"docs":{},"df":0,"(":{"docs":{},"df":0,"&":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"121":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}},"v":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"165":{"tf":1.0}},"df":1}}}}}}}},"l":{"docs":{},"df":0,"n":{"docs":{},"df":0,"!":{"docs":{},"df":0,"(":{"docs":{},"df":0,"h":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"83":{"tf":1.0}},"df":1}}}}}}},"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"76":{"tf":1.0}},"df":1}}}}}}}},"r":{"docs":{"2":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"126":{"tf":1.0},"127":{"tf":1.0},"147":{"tf":1.0},"173":{"tf":1.0},"176":{"tf":1.0},"4":{"tf":1.0},"69":{"tf":1.0},"72":{"tf":1.0},"9":{"tf":1.0},"93":{"tf":1.0}},"df":10}}}}},"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{"75":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"e":{"docs":{"108":{"tf":1.0}},"df":1}}}}},"x":{"docs":{"100":{"tf":1.4142135623730951},"104":{"tf":1.4142135623730951},"107":{"tf":1.0},"108":{"tf":1.0},"134":{"tf":1.0},"166":{"tf":3.1622776601683795},"173":{"tf":1.7320508075688772},"68":{"tf":2.23606797749979},"71":{"tf":1.0}},"df":9,"8":{"docs":{},"df":0,"6":{"docs":{},"df":0,"_":{"docs":{},"df":0,"6":{"docs":{},"df":0,"4":{"docs":{"21":{"tf":1.0}},"df":1}}}}},"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"g":{"docs":{},"df":0,"o":{"docs":{"162":{"tf":1.0}},"df":1}}}},"m":{"docs":{},"df":0,"l":{"docs":{"199":{"tf":1.0}},"df":1}},"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"50":{"tf":1.0}},"df":1}}},"s":{"docs":{},"df":0,"r":{"docs":{"83":{"tf":1.0}},"df":1}}},"s":{"docs":{"133":{"tf":1.0},"134":{"tf":1.4142135623730951},"79":{"tf":1.0}},"df":3,".":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"79":{"tf":1.0}},"df":1}}},"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{},"df":0,"(":{"docs":{},"df":0,"4":{"docs":{},"df":0,"2":{"docs":{"133":{"tf":1.0}},"df":1,")":{"docs":{},"df":0,".":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"134":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}},"[":{"docs":{},"df":0,"i":{"docs":{"79":{"tf":1.0}},"df":1}}}},"y":{"docs":{"166":{"tf":3.4641016151377544},"173":{"tf":1.7320508075688772},"68":{"tf":1.0}},"df":3,"e":{"docs":{"187":{"tf":1.0}},"df":1,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{},"df":0,"a":{"docs":{},"df":0,"y":{"docs":{"86":{"tf":1.0}},"df":1}}}}}}}},"i":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"d":{"docs":{"130":{"tf":1.0}},"df":1}}}},"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"\'":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{"68":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"_":{"docs":{},"df":0,"c":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"180":{"tf":1.0}},"df":1}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"f":{"docs":{"162":{"tf":1.0},"50":{"tf":1.0}},"df":2}}}}},"’":{"docs":{},"df":0,"d":{"docs":{"158":{"tf":1.0}},"df":1},"l":{"docs":{},"df":0,"l":{"docs":{"132":{"tf":1.0},"133":{"tf":1.0},"134":{"tf":1.0},"136":{"tf":1.4142135623730951},"188":{"tf":1.0},"189":{"tf":1.0},"43":{"tf":1.7320508075688772},"52":{"tf":1.0},"53":{"tf":1.0},"64":{"tf":1.0},"69":{"tf":1.0},"77":{"tf":1.4142135623730951},"81":{"tf":1.0},"83":{"tf":1.4142135623730951},"90":{"tf":1.0}},"df":15}},"r":{"docs":{"124":{"tf":1.0},"179":{"tf":1.0},"50":{"tf":1.0},"64":{"tf":1.0},"7":{"tf":1.0},"70":{"tf":1.4142135623730951}},"df":6},"v":{"docs":{"50":{"tf":1.0}},"df":1}}}}},"z":{"docs":{"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.7320508075688772},"137":{"tf":1.0},"166":{"tf":3.7416573867739413},"188":{"tf":1.4142135623730951},"190":{"tf":2.0}},"df":7,"e":{"docs":{},"df":0,"p":{"docs":{},"df":0,"h":{"docs":{},"df":0,"y":{"docs":{},"df":0,"r":{"docs":{"170":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"o":{"docs":{"111":{"tf":1.0},"112":{"tf":1.4142135623730951},"113":{"tf":1.0},"142":{"tf":1.0},"153":{"tf":1.4142135623730951},"187":{"tf":1.0},"189":{"tf":1.0},"190":{"tf":1.0},"61":{"tf":1.0},"62":{"tf":1.4142135623730951},"82":{"tf":1.0}},"df":11}}},"i":{"docs":{},"df":0,"p":{"docs":{"163":{"tf":1.0},"59":{"tf":1.0}},"df":2}},"s":{"docs":{},"df":0,"t":{"docs":{"142":{"tf":1.0}},"df":1}}}}},"breadcrumbs":{"root":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"111":{"tf":1.4142135623730951},"112":{"tf":1.0},"113":{"tf":1.0},"125":{"tf":1.0}},"df":4}}}}}}},"c":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"124":{"tf":1.0},"147":{"tf":1.0},"163":{"tf":1.0},"165":{"tf":1.0},"72":{"tf":1.0},"92":{"tf":1.0}},"df":6}}}}},"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"n":{"docs":{"166":{"tf":1.0}},"df":1}}},"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{"133":{"tf":1.0}},"df":1}}}},"p":{"docs":{},"df":0,"i":{"docs":{"181":{"tf":1.0}},"df":1},"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"x":{"docs":{"192":{"tf":1.4142135623730951},"193":{"tf":1.0},"194":{"tf":1.0},"195":{"tf":1.0},"196":{"tf":1.0},"197":{"tf":1.0},"198":{"tf":1.0},"199":{"tf":1.0},"200":{"tf":1.0},"201":{"tf":1.0}},"df":10}}}}},"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"119":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"91":{"tf":1.0}},"df":1}}},"p":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"144":{"tf":1.0},"151":{"tf":1.0}},"df":2}}}}}}},"r":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"71":{"tf":1.0}},"df":1}}}}}}}}},"m":{"docs":{"22":{"tf":1.0},"49":{"tf":1.0}},"df":2},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"y":{"docs":{"163":{"tf":1.0}},"df":1}}}},"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{"4":{"tf":1.0}},"df":1}}}}}},"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"124":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{"89":{"tf":1.4142135623730951},"90":{"tf":1.0},"91":{"tf":1.0},"92":{"tf":1.0},"93":{"tf":1.0}},"df":5}}}}}},"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"174":{"tf":1.0}},"df":1}}}}}}},"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{"14":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"21":{"tf":1.0},"39":{"tf":1.0}},"df":2}}}}}},"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{"70":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"k":{"docs":{"3":{"tf":1.0},"7":{"tf":1.0},"8":{"tf":1.0}},"df":3}},"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"94":{"tf":1.0},"95":{"tf":1.0},"96":{"tf":1.4142135623730951}},"df":3}}}}},"s":{"docs":{},"df":0,"p":{"docs":{"193":{"tf":1.0}},"df":1}},"u":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"d":{"docs":{"161":{"tf":1.0},"169":{"tf":1.0},"175":{"tf":1.0},"176":{"tf":1.0},"177":{"tf":1.0},"178":{"tf":1.0},"181":{"tf":1.0}},"df":7,".":{"docs":{},"df":0,"r":{"docs":{"176":{"tf":1.0}},"df":1}}}}}}},"c":{"docs":{"144":{"tf":1.0},"146":{"tf":1.0},"147":{"tf":1.0},"148":{"tf":1.0},"150":{"tf":1.0},"151":{"tf":1.0},"153":{"tf":1.0},"154":{"tf":1.0},"155":{"tf":1.0},"156":{"tf":1.4142135623730951},"157":{"tf":1.0},"158":{"tf":1.0},"159":{"tf":1.0},"160":{"tf":1.0},"161":{"tf":1.0},"162":{"tf":1.0},"163":{"tf":1.0},"164":{"tf":1.0},"165":{"tf":1.0},"166":{"tf":1.0},"167":{"tf":1.0},"171":{"tf":1.4142135623730951},"172":{"tf":1.0},"173":{"tf":1.4142135623730951},"174":{"tf":1.0},"175":{"tf":1.0},"176":{"tf":1.0},"177":{"tf":1.0},"178":{"tf":1.0},"179":{"tf":1.4142135623730951},"180":{"tf":1.0},"181":{"tf":1.4142135623730951},"182":{"tf":1.0},"183":{"tf":1.4142135623730951},"184":{"tf":1.0},"91":{"tf":1.0}},"df":36,"/":{"docs":{},"df":0,"c":{"docs":{"175":{"tf":1.0},"178":{"tf":1.0}},"df":2}},"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"’":{"docs":{},"df":0,"t":{"docs":{"98":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"g":{"docs":{},"df":0,"o":{"docs":{"20":{"tf":1.0},"21":{"tf":1.0},"39":{"tf":1.0},"40":{"tf":1.0},"57":{"tf":1.0}},"df":5}}},"s":{"docs":{},"df":0,"e":{"docs":{"138":{"tf":1.0}},"df":1}}},"c":{"docs":{"178":{"tf":1.0}},"df":1},"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"94":{"tf":1.0},"95":{"tf":1.0},"96":{"tf":1.4142135623730951}},"df":3}},"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"142":{"tf":1.4142135623730951}},"df":1}}}}}}},"i":{"docs":{},"df":0,"p":{"docs":{"27":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{"158":{"tf":1.0},"175":{"tf":1.0},"178":{"tf":1.0},"29":{"tf":1.0}},"df":4}},"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"132":{"tf":1.4142135623730951},"133":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0},"136":{"tf":1.0},"137":{"tf":1.0},"138":{"tf":1.0},"139":{"tf":1.0}},"df":8}}}}},"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"110":{"tf":1.0},"158":{"tf":1.0},"159":{"tf":1.0},"162":{"tf":1.0},"61":{"tf":1.0}},"df":5}},"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"81":{"tf":1.0}},"df":1}}},"u":{"docs":{},"df":0,"t":{"docs":{"159":{"tf":1.0}},"df":1}}}},"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{"120":{"tf":1.4142135623730951},"121":{"tf":1.4142135623730951},"122":{"tf":1.0},"123":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":1.0},"126":{"tf":1.0},"127":{"tf":1.0},"128":{"tf":1.0},"129":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.0}},"df":12}}}},"f":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"67":{"tf":1.0}},"df":1}}}}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"150":{"tf":1.0}},"df":1}}}}}}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"x":{"docs":{},"df":0,"t":{"docs":{"184":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"108":{"tf":1.4142135623730951},"109":{"tf":1.0},"110":{"tf":1.0}},"df":3}}},"i":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"8":{"tf":1.0}},"df":1}}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{"131":{"tf":1.0}},"df":1}},"s":{"docs":{},"df":0,"t":{"docs":{"111":{"tf":1.4142135623730951},"112":{"tf":1.0},"113":{"tf":1.0}},"df":3}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{"144":{"tf":1.4142135623730951},"147":{"tf":1.0},"178":{"tf":1.0},"70":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0},"76":{"tf":1.0}},"df":7}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"56":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"123":{"tf":1.0}},"df":1}}}},"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"162":{"tf":1.0},"61":{"tf":1.0}},"df":2}}}},"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"150":{"tf":1.0}},"df":1}}}},"d":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{"102":{"tf":1.0},"122":{"tf":1.0}},"df":2,"t":{"docs":{},"df":0,"y":{"docs":{},"df":0,"p":{"docs":{"173":{"tf":1.0}},"df":1}}}}}},"e":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"g":{"docs":{"23":{"tf":1.0},"25":{"tf":1.0},"64":{"tf":1.0},"68":{"tf":1.0}},"df":4,"g":{"docs":{"27":{"tf":1.0},"28":{"tf":1.0},"31":{"tf":1.0}},"df":3}}}},"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"151":{"tf":1.0}},"df":1}}},"f":{"docs":{},"df":0,"a":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{"153":{"tf":1.0},"82":{"tf":1.0}},"df":2}}}},"i":{"docs":{},"df":0,"n":{"docs":{"172":{"tf":1.0}},"df":1}}},"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"188":{"tf":1.0}},"df":1}}}},"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"n":{"docs":{"108":{"tf":1.4142135623730951},"109":{"tf":1.0},"110":{"tf":1.0},"140":{"tf":1.4142135623730951},"141":{"tf":1.4142135623730951},"142":{"tf":1.4142135623730951},"143":{"tf":1.0},"144":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":1.0},"147":{"tf":1.0},"148":{"tf":1.0},"149":{"tf":1.0},"150":{"tf":1.0},"151":{"tf":1.0},"152":{"tf":1.0},"153":{"tf":1.0},"154":{"tf":1.0},"155":{"tf":1.0}},"df":19}}},"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"146":{"tf":1.0}},"df":1}}}}}}}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"p":{"docs":{"156":{"tf":1.4142135623730951},"157":{"tf":1.0},"158":{"tf":1.0},"159":{"tf":1.0},"160":{"tf":1.0},"161":{"tf":1.0},"162":{"tf":1.0},"163":{"tf":1.0},"164":{"tf":1.0},"165":{"tf":1.0},"166":{"tf":1.0},"167":{"tf":1.0}},"df":12}}}}}},"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{"25":{"tf":1.0}},"df":1,"r":{"docs":{"118":{"tf":1.0},"52":{"tf":1.0}},"df":2}}}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{"49":{"tf":1.0}},"df":1}},"s":{"docs":{"139":{"tf":1.0}},"df":1}},"m":{"docs":{},"df":0,"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{"129":{"tf":1.0}},"df":1}}}},"e":{"docs":{},"df":0,"d":{"docs":{"1":{"tf":1.0},"115":{"tf":1.0},"116":{"tf":1.0},"148":{"tf":1.0},"156":{"tf":1.4142135623730951},"157":{"tf":1.0},"158":{"tf":1.0},"159":{"tf":1.0},"160":{"tf":1.0},"161":{"tf":1.0},"162":{"tf":1.0},"163":{"tf":1.0},"164":{"tf":1.0},"165":{"tf":1.0},"166":{"tf":1.0},"167":{"tf":1.0},"23":{"tf":1.0}},"df":17}}}},"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{"155":{"tf":1.0}},"df":1}}},"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"12":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0}},"df":3}}}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{"154":{"tf":1.4142135623730951}},"df":1}},"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"136":{"tf":1.0}},"df":1}}}},"x":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{"79":{"tf":1.0},"81":{"tf":1.0}},"df":2}}}},"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{"80":{"tf":1.4142135623730951},"81":{"tf":1.0},"82":{"tf":1.4142135623730951},"83":{"tf":1.0}},"df":4}}}},"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"138":{"tf":1.0}},"df":1}}}},"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"100":{"tf":1.0}},"df":1}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{"150":{"tf":1.0},"29":{"tf":1.0}},"df":2}},"r":{"docs":{},"df":0,"n":{"docs":{"177":{"tf":1.0},"183":{"tf":1.0}},"df":2}}}}}},"f":{"docs":{},"df":0,"3":{"docs":{"11":{"tf":1.0}},"df":1},"a":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{"83":{"tf":1.0}},"df":1}}}},"i":{"docs":{},"df":0,"r":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"89":{"tf":1.4142135623730951},"90":{"tf":1.0},"91":{"tf":1.0},"92":{"tf":1.0},"93":{"tf":1.0}},"df":5}}}},"p":{"docs":{},"df":0,"u":{"docs":{"194":{"tf":1.0}},"df":1}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"e":{"docs":{"146":{"tf":1.0}},"df":1}}},"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"110":{"tf":1.0},"173":{"tf":1.0},"191":{"tf":1.4142135623730951}},"df":3}}}}}}}},"g":{"docs":{},"df":0,"d":{"docs":{},"df":0,"b":{"docs":{"31":{"tf":1.0},"49":{"tf":1.0}},"df":2}},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"174":{"tf":1.0},"20":{"tf":1.0},"40":{"tf":1.0},"57":{"tf":1.0}},"df":4}}},"t":{"docs":{"54":{"tf":1.4142135623730951},"55":{"tf":1.0},"56":{"tf":1.0},"57":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"61":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.0},"64":{"tf":1.0},"65":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.0},"70":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0},"73":{"tf":1.0},"74":{"tf":1.0},"75":{"tf":1.0},"76":{"tf":1.0},"77":{"tf":1.0},"78":{"tf":1.0},"79":{"tf":1.0},"80":{"tf":1.0},"81":{"tf":1.0},"82":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0}},"df":31}},"i":{"docs":{},"df":0,"t":{"docs":{"20":{"tf":1.0},"58":{"tf":1.0}},"df":2}},"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"122":{"tf":1.0},"94":{"tf":1.0},"98":{"tf":1.0}},"df":3}}},"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"192":{"tf":1.4142135623730951},"193":{"tf":1.0},"194":{"tf":1.0},"195":{"tf":1.0},"196":{"tf":1.0},"197":{"tf":1.0},"198":{"tf":1.0},"199":{"tf":1.0},"200":{"tf":1.0},"201":{"tf":1.0}},"df":10}}}}}}},"n":{"docs":{},"df":0,"u":{"docs":{"31":{"tf":1.0}},"df":1}},"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{"152":{"tf":1.4142135623730951},"153":{"tf":1.0},"154":{"tf":1.0},"155":{"tf":1.0}},"df":4}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"184":{"tf":1.0}},"df":1}}}}}},"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{"103":{"tf":1.4142135623730951},"104":{"tf":1.0},"105":{"tf":1.0},"106":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.0},"110":{"tf":1.0},"111":{"tf":1.0},"112":{"tf":1.0},"113":{"tf":1.0}},"df":11}}}}}}}},"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"115":{"tf":1.0},"116":{"tf":1.0},"117":{"tf":1.0},"141":{"tf":1.4142135623730951},"142":{"tf":1.4142135623730951},"143":{"tf":1.0},"144":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":1.0},"147":{"tf":1.4142135623730951},"148":{"tf":1.7320508075688772},"149":{"tf":1.0},"150":{"tf":1.0},"151":{"tf":1.0},"152":{"tf":1.0},"153":{"tf":1.0},"154":{"tf":1.0},"155":{"tf":1.0},"195":{"tf":1.0},"76":{"tf":1.0}},"df":20},"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"l":{"docs":{"136":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"r":{"docs":{"82":{"tf":1.0},"83":{"tf":1.0}},"df":2}}}}},"r":{"docs":{},"df":0,"d":{"docs":{"83":{"tf":1.0}},"df":1,"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"10":{"tf":1.4142135623730951},"102":{"tf":1.0},"107":{"tf":1.0},"11":{"tf":1.0},"65":{"tf":1.4142135623730951},"66":{"tf":1.4142135623730951},"67":{"tf":1.0},"68":{"tf":1.0}},"df":8}}}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"134":{"tf":1.0}},"df":1}}}}}}},"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"b":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{"46":{"tf":1.0}},"df":1}}}}}},"s":{"docs":{},"df":0,"t":{"docs":{"13":{"tf":1.0}},"df":1}}}},"i":{"docs":{},"df":0,"2":{"docs":{},"df":0,"c":{"docs":{"196":{"tf":1.0}},"df":1}},"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"117":{"tf":1.0},"148":{"tf":1.0}},"df":2}}}}}}}},"n":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"151":{"tf":1.4142135623730951}},"df":1}}},"s":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"62":{"tf":1.0}},"df":1}}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"37":{"tf":1.4142135623730951},"38":{"tf":1.0},"39":{"tf":1.0},"40":{"tf":1.0},"41":{"tf":1.0},"42":{"tf":1.0},"43":{"tf":1.0},"44":{"tf":1.0},"45":{"tf":1.0},"46":{"tf":1.4142135623730951},"47":{"tf":1.4142135623730951},"48":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.0},"51":{"tf":1.0},"52":{"tf":1.0},"53":{"tf":1.7320508075688772}},"df":17}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"150":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"41":{"tf":1.0}},"df":1}}}}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"f":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{"152":{"tf":1.0},"172":{"tf":1.0},"174":{"tf":1.0}},"df":3}}},"o":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"145":{"tf":1.4142135623730951},"146":{"tf":1.0},"147":{"tf":1.0},"148":{"tf":1.0},"168":{"tf":1.4142135623730951},"169":{"tf":1.4142135623730951},"170":{"tf":1.4142135623730951},"171":{"tf":1.0},"172":{"tf":1.0},"173":{"tf":1.0},"174":{"tf":1.0},"175":{"tf":1.0},"176":{"tf":1.0},"177":{"tf":1.0},"178":{"tf":1.0},"179":{"tf":1.0},"180":{"tf":1.0},"181":{"tf":1.0},"182":{"tf":1.0},"183":{"tf":1.0},"184":{"tf":1.0}},"df":21}}}},"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{"84":{"tf":1.4142135623730951}},"df":1}}}}}},"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"u":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"0":{"tf":1.4142135623730951},"1":{"tf":1.0},"10":{"tf":1.0},"11":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"15":{"tf":1.0},"16":{"tf":1.0},"17":{"tf":1.0},"18":{"tf":1.0},"19":{"tf":1.0},"2":{"tf":1.0},"20":{"tf":1.0},"21":{"tf":1.0},"22":{"tf":1.0},"23":{"tf":1.0},"24":{"tf":1.0},"25":{"tf":1.0},"26":{"tf":1.0},"27":{"tf":1.0},"28":{"tf":1.0},"29":{"tf":1.0},"3":{"tf":1.0},"30":{"tf":1.0},"31":{"tf":1.0},"32":{"tf":1.0},"33":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.0},"36":{"tf":1.0},"37":{"tf":1.0},"38":{"tf":1.0},"39":{"tf":1.0},"4":{"tf":1.0},"40":{"tf":1.0},"41":{"tf":1.0},"42":{"tf":1.0},"43":{"tf":1.0},"44":{"tf":1.0},"45":{"tf":1.0},"46":{"tf":1.0},"47":{"tf":1.0},"48":{"tf":1.0},"49":{"tf":1.0},"5":{"tf":1.0},"50":{"tf":1.0},"51":{"tf":1.0},"52":{"tf":1.0},"53":{"tf":1.0},"6":{"tf":1.0},"7":{"tf":1.0},"8":{"tf":1.0},"9":{"tf":1.0}},"df":54}}}}}}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"163":{"tf":1.0}},"df":1}}}},"j":{"docs":{"35":{"tf":1.0}},"df":1},"k":{"docs":{},"df":0,"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"66":{"tf":1.0}},"df":1}}}},"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"b":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"100":{"tf":1.0}},"df":1}}}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"d":{"docs":{"15":{"tf":1.0}},"df":1}}}},"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"87":{"tf":1.0}},"df":1}}},"k":{"docs":{"184":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.0},"36":{"tf":1.0},"52":{"tf":1.0}},"df":5},"u":{"docs":{},"df":0,"x":{"docs":{"42":{"tf":1.4142135623730951},"43":{"tf":1.0},"44":{"tf":1.0}},"df":3}}},"t":{"docs":{},"df":0,"t":{"docs":{},"df":0,"l":{"docs":{"171":{"tf":1.4142135623730951},"172":{"tf":1.0},"173":{"tf":1.0},"174":{"tf":1.0},"175":{"tf":1.0},"176":{"tf":1.0},"177":{"tf":1.0},"178":{"tf":1.0},"179":{"tf":1.4142135623730951},"180":{"tf":1.0},"181":{"tf":1.0},"182":{"tf":1.0},"183":{"tf":1.0},"184":{"tf":1.0}},"df":14}}}}},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"106":{"tf":1.4142135623730951},"107":{"tf":1.0}},"df":2}}},"o":{"docs":{"45":{"tf":1.4142135623730951},"46":{"tf":1.0},"47":{"tf":1.0}},"df":3},"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"47":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"o":{"docs":{"160":{"tf":1.0}},"df":1}}},"p":{"docs":{"69":{"tf":1.4142135623730951},"70":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0},"73":{"tf":1.0},"74":{"tf":1.0},"75":{"tf":1.0},"76":{"tf":1.0},"88":{"tf":1.0}},"df":9},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"9":{"tf":1.0}},"df":1}}},"h":{"docs":{"191":{"tf":1.4142135623730951}},"df":1}}},"c":{"docs":{},"df":0,"u":{"docs":{"36":{"tf":1.0}},"df":1}},"e":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"10":{"tf":1.0}},"df":1}},"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"136":{"tf":1.0},"137":{"tf":1.0},"69":{"tf":1.4142135623730951},"70":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0},"73":{"tf":1.0},"74":{"tf":1.0},"75":{"tf":1.0},"76":{"tf":1.0},"87":{"tf":1.0},"88":{"tf":1.0}},"df":12}}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"14":{"tf":1.0}},"df":1}},"h":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{"146":{"tf":1.0},"151":{"tf":1.0},"154":{"tf":1.0}},"df":3}}}}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{"71":{"tf":1.0}},"df":1}}}},"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{"75":{"tf":1.0}},"df":1}}}}},"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{"131":{"tf":1.0}},"df":1}}}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"122":{"tf":1.0},"94":{"tf":1.0}},"df":2}}},"e":{"docs":{},"df":0,"x":{"docs":{"126":{"tf":1.0}},"df":1}}}}},"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{"143":{"tf":1.4142135623730951},"144":{"tf":1.7320508075688772}},"df":2}}},"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"113":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"l":{"docs":{"182":{"tf":1.0}},"df":1}}}}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"d":{"docs":{"12":{"tf":1.4142135623730951},"13":{"tf":1.0},"14":{"tf":1.0},"15":{"tf":1.0},"16":{"tf":1.0},"17":{"tf":1.0},"18":{"tf":1.0},"191":{"tf":1.0}},"df":8}}}},"n":{"docs":{"56":{"tf":1.0}},"df":1,"e":{"docs":{"49":{"tf":1.0}},"df":1}}}},"o":{"docs":{},"df":0,"f":{"docs":{},"df":0,"f":{"docs":{"135":{"tf":1.0}},"df":1}},"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"27":{"tf":1.0}},"df":1,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"d":{"docs":{"27":{"tf":1.0},"50":{"tf":1.0}},"df":2}}}},"r":{"docs":{"130":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{"186":{"tf":1.4142135623730951},"187":{"tf":1.4142135623730951},"188":{"tf":1.4142135623730951},"189":{"tf":1.4142135623730951},"190":{"tf":1.4142135623730951}},"df":5}}}},"s":{"docs":{"41":{"tf":1.0}},"df":1},"u":{"docs":{},"df":0,"t":{"docs":{"136":{"tf":1.0}},"df":1}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{"17":{"tf":1.0},"24":{"tf":1.0},"60":{"tf":1.0}},"df":3}}}}}}}},"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{"147":{"tf":1.0},"197":{"tf":1.0},"72":{"tf":1.0}},"df":3,"k":{"docs":{"166":{"tf":1.0}},"df":1,"a":{"docs":{},"df":0,"g":{"docs":{"43":{"tf":1.0}},"df":1}}}},"n":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"78":{"tf":1.4142135623730951},"79":{"tf":1.0}},"df":2}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"155":{"tf":1.0}},"df":1}}}}},"t":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"n":{"docs":{"140":{"tf":1.4142135623730951},"141":{"tf":1.4142135623730951},"142":{"tf":1.4142135623730951},"143":{"tf":1.0},"144":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":1.0},"147":{"tf":1.0},"148":{"tf":1.0},"149":{"tf":1.0},"150":{"tf":1.0},"151":{"tf":1.0},"152":{"tf":1.0},"153":{"tf":1.0},"154":{"tf":1.0},"155":{"tf":1.0}},"df":16}}}}}},"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"m":{"docs":{"191":{"tf":1.4142135623730951}},"df":1}}}},"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"100":{"tf":1.0},"101":{"tf":1.0},"102":{"tf":1.0},"106":{"tf":1.4142135623730951},"107":{"tf":1.0},"127":{"tf":1.0},"72":{"tf":1.0},"85":{"tf":1.4142135623730951},"86":{"tf":1.4142135623730951},"87":{"tf":1.0},"88":{"tf":1.4142135623730951},"89":{"tf":1.0},"90":{"tf":1.0},"91":{"tf":1.0},"92":{"tf":1.0},"93":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0},"96":{"tf":1.0},"97":{"tf":1.0},"98":{"tf":1.0},"99":{"tf":1.0}},"df":22}}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{"153":{"tf":1.4142135623730951},"154":{"tf":1.7320508075688772},"155":{"tf":1.4142135623730951}},"df":3}},"o":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"164":{"tf":1.0}},"df":1}}}}},"r":{"docs":{},"df":0,"t":{"docs":{"154":{"tf":1.0}},"df":1,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"114":{"tf":1.4142135623730951},"115":{"tf":1.0},"116":{"tf":1.0},"117":{"tf":1.0},"118":{"tf":1.0},"119":{"tf":1.0}},"df":6}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"149":{"tf":1.4142135623730951},"150":{"tf":1.0},"151":{"tf":1.0}},"df":3}}}},"p":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"157":{"tf":1.0}},"df":1}}}}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"q":{"docs":{},"df":0,"u":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"4":{"tf":1.0}},"df":1}}}}}}}}},"o":{"docs":{},"df":0,"b":{"docs":{},"df":0,"e":{"docs":{"26":{"tf":1.0},"29":{"tf":1.0},"32":{"tf":1.0},"33":{"tf":1.0}},"df":4}},"g":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"104":{"tf":1.4142135623730951},"105":{"tf":1.0},"56":{"tf":1.0},"60":{"tf":1.0}},"df":4}}}},"j":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"180":{"tf":1.0},"184":{"tf":1.0}},"df":2}}}},"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{"146":{"tf":1.0},"154":{"tf":1.0}},"df":2}}}}}},"q":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{"22":{"tf":1.0},"51":{"tf":1.0},"55":{"tf":1.4142135623730951},"56":{"tf":1.0},"57":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"61":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.0},"64":{"tf":1.0}},"df":12}}}},"r":{"docs":{},"df":0,"e":{"docs":{"9":{"tf":1.0}},"df":1,"a":{"docs":{},"df":0,"d":{"docs":{"73":{"tf":1.0}},"df":1},"l":{"docs":{"130":{"tf":1.0},"87":{"tf":1.0}},"df":2}},"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"152":{"tf":1.0}},"df":1}}}}}}},"e":{"docs":{},"df":0,"x":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"147":{"tf":1.4142135623730951}},"df":1}}}}}},"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"164":{"tf":1.0}},"df":1}}},"g":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"147":{"tf":1.0},"69":{"tf":1.4142135623730951},"70":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0},"73":{"tf":1.0},"74":{"tf":1.0},"75":{"tf":1.0},"76":{"tf":1.0},"90":{"tf":1.0}},"df":10}}}},"p":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"107":{"tf":1.0}},"df":1}}}}}}},"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"c":{"docs":{"167":{"tf":1.0},"5":{"tf":1.0}},"df":2}}}}}},"s":{"docs":{"26":{"tf":1.0},"29":{"tf":1.0}},"df":2},"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"128":{"tf":1.0}},"df":1}},"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"170":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{"44":{"tf":1.0},"95":{"tf":1.0}},"df":2}},"n":{"docs":{"63":{"tf":1.0}},"df":1,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{"15":{"tf":1.0}},"df":1}}}},"s":{"docs":{},"df":0,"t":{"docs":{"1":{"tf":1.0},"12":{"tf":1.0},"171":{"tf":1.4142135623730951},"172":{"tf":1.0},"173":{"tf":1.0},"174":{"tf":1.0},"175":{"tf":1.0},"176":{"tf":1.4142135623730951},"177":{"tf":1.0},"178":{"tf":1.0},"179":{"tf":1.4142135623730951},"180":{"tf":1.0},"181":{"tf":1.0},"182":{"tf":1.0},"183":{"tf":1.0},"184":{"tf":1.0},"23":{"tf":1.0},"38":{"tf":1.0},"56":{"tf":1.0},"89":{"tf":1.0},"90":{"tf":1.0},"91":{"tf":1.0},"92":{"tf":1.0},"93":{"tf":1.0},"99":{"tf":1.0}},"df":25,"i":{"docs":{"33":{"tf":1.0},"93":{"tf":1.0}},"df":2}}}}},"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{"110":{"tf":1.0}},"df":1}}}}},"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{"2":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{"176":{"tf":1.0}},"df":1}}}}},"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"123":{"tf":1.0}},"df":1}}}}},"e":{"docs":{"18":{"tf":1.0}},"df":1},"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"158":{"tf":1.0}},"df":1}}}},"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"h":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"77":{"tf":1.4142135623730951}},"df":1}}}}}},"n":{"docs":{},"df":0,"d":{"docs":{"125":{"tf":1.0}},"df":1}},"t":{"docs":{"180":{"tf":1.0}},"df":1}},"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{"127":{"tf":1.0}},"df":1}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"100":{"tf":1.0},"101":{"tf":1.0},"102":{"tf":1.0},"97":{"tf":1.4142135623730951},"98":{"tf":1.0},"99":{"tf":1.0}},"df":6}}}}}}},"z":{"docs":{},"df":0,"e":{"docs":{"112":{"tf":1.0},"153":{"tf":1.0},"159":{"tf":1.0},"186":{"tf":1.4142135623730951},"187":{"tf":1.0},"188":{"tf":1.0},"189":{"tf":1.0},"190":{"tf":1.4142135623730951}},"df":8}}},"o":{"docs":{},"df":0,"f":{"docs":{},"df":0,"t":{"docs":{},"df":0,"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"25":{"tf":1.0}},"df":1}}}}}},"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{"87":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{},"df":0,"f":{"docs":{"41":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"d":{"docs":{"186":{"tf":1.4142135623730951},"187":{"tf":1.0},"188":{"tf":1.0},"189":{"tf":1.4142135623730951},"190":{"tf":1.0}},"df":5}}},"i":{"docs":{"198":{"tf":1.0}},"df":1}},"t":{"docs":{"34":{"tf":1.0},"52":{"tf":1.0}},"df":2,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{"56":{"tf":1.0}},"df":1}}}}},"r":{"docs":{},"df":0,"t":{"docs":{"54":{"tf":1.4142135623730951},"55":{"tf":1.0},"56":{"tf":1.0},"57":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"61":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.0},"64":{"tf":1.0},"65":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.0},"70":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0},"73":{"tf":1.0},"74":{"tf":1.0},"75":{"tf":1.0},"76":{"tf":1.0},"77":{"tf":1.0},"78":{"tf":1.0},"79":{"tf":1.0},"80":{"tf":1.0},"81":{"tf":1.0},"82":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0}},"df":31}},"t":{"docs":{},"df":0,"e":{"docs":{"106":{"tf":1.4142135623730951},"107":{"tf":1.0},"109":{"tf":1.0},"155":{"tf":1.4142135623730951},"94":{"tf":1.0}},"df":5},"i":{"docs":{},"df":0,"c":{"docs":{"103":{"tf":1.4142135623730951},"104":{"tf":1.0},"105":{"tf":1.0},"106":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.0},"110":{"tf":1.0},"111":{"tf":1.0},"112":{"tf":1.0},"113":{"tf":1.0}},"df":11}}}},"m":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{},"df":0,"f":{"docs":{},"df":0,"3":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"11":{"tf":1.0}},"df":1}}}}}}}}}}}}}},"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{"105":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{"29":{"tf":1.0}},"df":1}}}}},"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"16":{"tf":1.0}},"df":1}}}}},"p":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"100":{"tf":1.0}},"df":1}}}}}},"v":{"docs":{},"df":0,"d":{"docs":{"199":{"tf":1.0}},"df":1}},"y":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{"125":{"tf":1.0}},"df":1}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{"130":{"tf":1.0},"161":{"tf":1.0},"169":{"tf":1.0},"177":{"tf":1.0},"22":{"tf":1.0}},"df":5}}}}}},"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{"110":{"tf":1.0},"130":{"tf":1.0},"138":{"tf":1.0},"158":{"tf":1.0},"159":{"tf":1.0}},"df":5}},"p":{"docs":{"156":{"tf":1.4142135623730951},"157":{"tf":1.0},"158":{"tf":1.0},"159":{"tf":1.0},"160":{"tf":1.0},"161":{"tf":1.0},"162":{"tf":1.0},"163":{"tf":1.0},"164":{"tf":1.0},"165":{"tf":1.0},"166":{"tf":1.0},"167":{"tf":1.0}},"df":12}},"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{"19":{"tf":1.4142135623730951},"20":{"tf":1.0},"21":{"tf":1.0},"22":{"tf":1.0},"23":{"tf":1.4142135623730951},"24":{"tf":1.0},"25":{"tf":1.4142135623730951},"26":{"tf":1.0},"27":{"tf":1.0},"28":{"tf":1.0},"29":{"tf":1.0},"30":{"tf":1.0},"31":{"tf":1.0},"32":{"tf":1.0},"33":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.0},"36":{"tf":1.0},"37":{"tf":1.0},"46":{"tf":1.0},"47":{"tf":1.0}},"df":21,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"38":{"tf":1.0}},"df":1}}}}}}},"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"185":{"tf":1.4142135623730951},"186":{"tf":1.0},"187":{"tf":1.0},"188":{"tf":1.0},"189":{"tf":1.0},"190":{"tf":1.0},"191":{"tf":1.0}},"df":7}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{"30":{"tf":1.0}},"df":1}}}},"d":{"docs":{},"df":0,"e":{"docs":{"135":{"tf":1.0}},"df":1,"o":{"docs":{},"df":0,"f":{"docs":{},"df":0,"f":{"docs":{"186":{"tf":1.4142135623730951},"187":{"tf":1.0},"188":{"tf":1.0},"189":{"tf":1.0},"190":{"tf":1.0}},"df":5}}}}},"i":{"docs":{},"df":0,"t":{"docs":{"148":{"tf":1.4142135623730951},"150":{"tf":1.0}},"df":2}},"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"6":{"tf":1.0}},"df":1}}}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"102":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"177":{"tf":1.0}},"df":1}}}}}},"y":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{"105":{"tf":1.0},"109":{"tf":1.0},"112":{"tf":1.0},"146":{"tf":1.0},"148":{"tf":1.0},"153":{"tf":1.0},"154":{"tf":1.0},"155":{"tf":1.0},"166":{"tf":1.0}},"df":9,"s":{"docs":{},"df":0,"t":{"docs":{"104":{"tf":1.4142135623730951},"105":{"tf":1.0}},"df":2}}}}}},"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"200":{"tf":1.0}},"df":1}}},"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"v":{"docs":{"44":{"tf":1.0}},"df":1}}},"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"185":{"tf":1.4142135623730951},"186":{"tf":1.0},"187":{"tf":1.0},"188":{"tf":1.0},"189":{"tf":1.0},"190":{"tf":1.0},"191":{"tf":1.0}},"df":7}}}}},"p":{"docs":{"180":{"tf":1.0}},"df":1},"s":{"docs":{"133":{"tf":1.0},"134":{"tf":1.0},"139":{"tf":1.0},"150":{"tf":1.0},"57":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.0},"7":{"tf":1.0},"72":{"tf":1.0},"76":{"tf":1.0},"9":{"tf":1.0},"98":{"tf":1.0}},"df":12,"a":{"docs":{},"df":0,"g":{"docs":{"137":{"tf":1.0}},"df":1},"r":{"docs":{},"df":0,"t":{"docs":{"201":{"tf":1.0}},"df":1}}},"b":{"docs":{"52":{"tf":1.0}},"df":1},"e":{"docs":{},"df":0,"r":{"docs":{"116":{"tf":1.0}},"df":1}}}},"v":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{"98":{"tf":1.0}},"df":1}}}}}}}},"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{"53":{"tf":1.4142135623730951}},"df":1}}}}},"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"29":{"tf":1.0}},"df":1}}}}},"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"165":{"tf":1.0},"92":{"tf":1.0}},"df":2}}}}}},"s":{"docs":{"163":{"tf":1.0},"164":{"tf":1.0}},"df":2}},"w":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"138":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"48":{"tf":1.4142135623730951},"49":{"tf":1.0},"50":{"tf":1.0},"51":{"tf":1.0},"52":{"tf":1.0}},"df":5}}}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"138":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"173":{"tf":1.0}},"df":1,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"146":{"tf":1.0},"93":{"tf":1.0}},"df":2}}}}},"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{"74":{"tf":1.0}},"df":1}}}}},"z":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{"111":{"tf":1.4142135623730951},"112":{"tf":1.4142135623730951},"113":{"tf":1.0},"153":{"tf":1.0}},"df":4}}},"s":{"docs":{},"df":0,"t":{"docs":{"153":{"tf":1.0}},"df":1}}}}},"title":{"root":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"111":{"tf":1.0},"125":{"tf":1.0}},"df":2}}}}}}},"c":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"124":{"tf":1.0},"147":{"tf":1.0},"163":{"tf":1.0},"165":{"tf":1.0},"72":{"tf":1.0},"92":{"tf":1.0}},"df":6}}}}},"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"n":{"docs":{"166":{"tf":1.0}},"df":1}}},"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{"133":{"tf":1.0}},"df":1}}}},"p":{"docs":{},"df":0,"i":{"docs":{"181":{"tf":1.0}},"df":1},"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"x":{"docs":{"192":{"tf":1.0}},"df":1}}}}},"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"119":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"91":{"tf":1.0}},"df":1}}},"p":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"144":{"tf":1.0},"151":{"tf":1.0}},"df":2}}}}}}},"r":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"71":{"tf":1.0}},"df":1}}}}}}}}},"m":{"docs":{"22":{"tf":1.0},"49":{"tf":1.0}},"df":2},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"y":{"docs":{"163":{"tf":1.0}},"df":1}}}},"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{"4":{"tf":1.0}},"df":1}}}}}},"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"124":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{"89":{"tf":1.0}},"df":1}}}}}},"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"174":{"tf":1.0}},"df":1}}}}}}},"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{"14":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"21":{"tf":1.0},"39":{"tf":1.0}},"df":2}}}}}},"o":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{"70":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"k":{"docs":{"3":{"tf":1.0},"7":{"tf":1.0},"8":{"tf":1.0}},"df":3}},"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"96":{"tf":1.0}},"df":1}}}}},"s":{"docs":{},"df":0,"p":{"docs":{"193":{"tf":1.0}},"df":1}},"u":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"d":{"docs":{"161":{"tf":1.0},"169":{"tf":1.0},"175":{"tf":1.0},"176":{"tf":1.0},"177":{"tf":1.0},"178":{"tf":1.0},"181":{"tf":1.0}},"df":7,".":{"docs":{},"df":0,"r":{"docs":{"176":{"tf":1.0}},"df":1}}}}}}},"c":{"docs":{"144":{"tf":1.0},"146":{"tf":1.0},"147":{"tf":1.0},"148":{"tf":1.0},"150":{"tf":1.0},"151":{"tf":1.0},"153":{"tf":1.0},"154":{"tf":1.0},"155":{"tf":1.0},"156":{"tf":1.0},"171":{"tf":1.0},"173":{"tf":1.0},"179":{"tf":1.0},"181":{"tf":1.0},"183":{"tf":1.0},"91":{"tf":1.0}},"df":16,"/":{"docs":{},"df":0,"c":{"docs":{"175":{"tf":1.0},"178":{"tf":1.0}},"df":2}},"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"’":{"docs":{},"df":0,"t":{"docs":{"98":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"g":{"docs":{},"df":0,"o":{"docs":{"20":{"tf":1.0},"21":{"tf":1.0},"39":{"tf":1.0},"40":{"tf":1.0},"57":{"tf":1.0}},"df":5}}},"s":{"docs":{},"df":0,"e":{"docs":{"138":{"tf":1.0}},"df":1}}},"c":{"docs":{"178":{"tf":1.0}},"df":1},"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"96":{"tf":1.0}},"df":1}},"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"142":{"tf":1.0}},"df":1}}}}}}},"i":{"docs":{},"df":0,"p":{"docs":{"27":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{"158":{"tf":1.0},"175":{"tf":1.0},"178":{"tf":1.0},"29":{"tf":1.0}},"df":4}},"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"132":{"tf":1.0}},"df":1}}}}},"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"110":{"tf":1.0},"158":{"tf":1.0},"159":{"tf":1.0},"162":{"tf":1.0},"61":{"tf":1.0}},"df":5}},"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"81":{"tf":1.0}},"df":1}}},"u":{"docs":{},"df":0,"t":{"docs":{"159":{"tf":1.0}},"df":1}}}},"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{"120":{"tf":1.0},"121":{"tf":1.0}},"df":2}}}},"f":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"67":{"tf":1.0}},"df":1}}}}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"150":{"tf":1.0}},"df":1}}}}}}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"x":{"docs":{},"df":0,"t":{"docs":{"184":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"108":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"8":{"tf":1.0}},"df":1}}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{"131":{"tf":1.0}},"df":1}},"s":{"docs":{},"df":0,"t":{"docs":{"111":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{"144":{"tf":1.4142135623730951},"147":{"tf":1.0},"178":{"tf":1.0},"70":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0},"76":{"tf":1.0}},"df":7}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"56":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"123":{"tf":1.0}},"df":1}}}},"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"162":{"tf":1.0},"61":{"tf":1.0}},"df":2}}}},"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"150":{"tf":1.0}},"df":1}}}},"d":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{"102":{"tf":1.0},"122":{"tf":1.0}},"df":2,"t":{"docs":{},"df":0,"y":{"docs":{},"df":0,"p":{"docs":{"173":{"tf":1.0}},"df":1}}}}}},"e":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"g":{"docs":{"23":{"tf":1.0},"25":{"tf":1.0},"64":{"tf":1.0},"68":{"tf":1.0}},"df":4,"g":{"docs":{"27":{"tf":1.0},"28":{"tf":1.0},"31":{"tf":1.0}},"df":3}}}},"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"151":{"tf":1.0}},"df":1}}},"f":{"docs":{},"df":0,"a":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{"153":{"tf":1.0},"82":{"tf":1.0}},"df":2}}}},"i":{"docs":{},"df":0,"n":{"docs":{"172":{"tf":1.0}},"df":1}}},"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"188":{"tf":1.0}},"df":1}}}},"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"n":{"docs":{"108":{"tf":1.0},"140":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":1.0}},"df":4}}},"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"146":{"tf":1.0}},"df":1}}}}}}}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"p":{"docs":{"156":{"tf":1.0}},"df":1}}}}}},"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{"25":{"tf":1.0}},"df":1,"r":{"docs":{"118":{"tf":1.0},"52":{"tf":1.0}},"df":2}}}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{"49":{"tf":1.0}},"df":1}},"s":{"docs":{"139":{"tf":1.0}},"df":1}},"m":{"docs":{},"df":0,"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{"129":{"tf":1.0}},"df":1}}}},"e":{"docs":{},"df":0,"d":{"docs":{"1":{"tf":1.0},"115":{"tf":1.0},"116":{"tf":1.0},"148":{"tf":1.0},"156":{"tf":1.0},"23":{"tf":1.0}},"df":6}}}},"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{"155":{"tf":1.0}},"df":1}}},"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"12":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0}},"df":3}}}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{"154":{"tf":1.4142135623730951}},"df":1}},"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"136":{"tf":1.0}},"df":1}}}},"x":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{"79":{"tf":1.0},"81":{"tf":1.0}},"df":2}}}},"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{"80":{"tf":1.0},"82":{"tf":1.0}},"df":2}}}},"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"138":{"tf":1.0}},"df":1}}}},"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"100":{"tf":1.0}},"df":1}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{"150":{"tf":1.0},"29":{"tf":1.0}},"df":2}},"r":{"docs":{},"df":0,"n":{"docs":{"177":{"tf":1.0},"183":{"tf":1.0}},"df":2}}}}}},"f":{"docs":{},"df":0,"3":{"docs":{"11":{"tf":1.0}},"df":1},"a":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{"83":{"tf":1.0}},"df":1}}}},"i":{"docs":{},"df":0,"r":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"89":{"tf":1.0}},"df":1}}}},"p":{"docs":{},"df":0,"u":{"docs":{"194":{"tf":1.0}},"df":1}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"e":{"docs":{"146":{"tf":1.0}},"df":1}}},"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"110":{"tf":1.0},"173":{"tf":1.0},"191":{"tf":1.0}},"df":3}}}}}}}},"g":{"docs":{},"df":0,"d":{"docs":{},"df":0,"b":{"docs":{"31":{"tf":1.0},"49":{"tf":1.0}},"df":2}},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"174":{"tf":1.0},"20":{"tf":1.0},"40":{"tf":1.0},"57":{"tf":1.0}},"df":4}}},"t":{"docs":{"54":{"tf":1.0}},"df":1}},"i":{"docs":{},"df":0,"t":{"docs":{"20":{"tf":1.0},"58":{"tf":1.0}},"df":2}},"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"122":{"tf":1.0},"94":{"tf":1.0},"98":{"tf":1.0}},"df":3}}},"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"192":{"tf":1.0}},"df":1}}}}}}},"n":{"docs":{},"df":0,"u":{"docs":{"31":{"tf":1.0}},"df":1}},"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{"152":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"184":{"tf":1.0}},"df":1}}}}}},"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{"103":{"tf":1.0}},"df":1}}}}}}}},"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"115":{"tf":1.0},"116":{"tf":1.0},"117":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":1.0},"147":{"tf":1.0},"148":{"tf":1.4142135623730951},"195":{"tf":1.0},"76":{"tf":1.0}},"df":9},"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"l":{"docs":{"136":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"r":{"docs":{"82":{"tf":1.0},"83":{"tf":1.0}},"df":2}}}}},"r":{"docs":{},"df":0,"d":{"docs":{"83":{"tf":1.0}},"df":1,"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"10":{"tf":1.0},"102":{"tf":1.0},"107":{"tf":1.0},"65":{"tf":1.0},"66":{"tf":1.0}},"df":5}}}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"134":{"tf":1.0}},"df":1}}}}}}},"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"b":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{"46":{"tf":1.0}},"df":1}}}}}},"s":{"docs":{},"df":0,"t":{"docs":{"13":{"tf":1.0}},"df":1}}}},"i":{"docs":{},"df":0,"2":{"docs":{},"df":0,"c":{"docs":{"196":{"tf":1.0}},"df":1}},"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"117":{"tf":1.0},"148":{"tf":1.0}},"df":2}}}}}}}},"n":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"151":{"tf":1.4142135623730951}},"df":1}}},"s":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"62":{"tf":1.0}},"df":1}}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"37":{"tf":1.0},"46":{"tf":1.0},"47":{"tf":1.0},"53":{"tf":1.0}},"df":4}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"150":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"41":{"tf":1.0}},"df":1}}}}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"f":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{"152":{"tf":1.0},"172":{"tf":1.0},"174":{"tf":1.0}},"df":3}}},"o":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"145":{"tf":1.0},"168":{"tf":1.0},"169":{"tf":1.0},"170":{"tf":1.0}},"df":4}}}},"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{"84":{"tf":1.0}},"df":1}}}}}},"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"u":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"0":{"tf":1.0}},"df":1}}}}}}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"163":{"tf":1.0}},"df":1}}}},"j":{"docs":{"35":{"tf":1.0}},"df":1},"k":{"docs":{},"df":0,"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"66":{"tf":1.0}},"df":1}}}},"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"b":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"100":{"tf":1.0}},"df":1}}}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"d":{"docs":{"15":{"tf":1.0}},"df":1}}}},"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"87":{"tf":1.0}},"df":1}}},"k":{"docs":{"184":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.0},"36":{"tf":1.0},"52":{"tf":1.0}},"df":5},"u":{"docs":{},"df":0,"x":{"docs":{"42":{"tf":1.0}},"df":1}}},"t":{"docs":{},"df":0,"t":{"docs":{},"df":0,"l":{"docs":{"171":{"tf":1.0},"179":{"tf":1.0}},"df":2}}}}},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"106":{"tf":1.0}},"df":1}}},"o":{"docs":{"45":{"tf":1.0}},"df":1},"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"47":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"o":{"docs":{"160":{"tf":1.0}},"df":1}}},"p":{"docs":{"69":{"tf":1.0},"88":{"tf":1.0}},"df":2},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"9":{"tf":1.0}},"df":1}}},"h":{"docs":{"191":{"tf":1.0}},"df":1}}},"c":{"docs":{},"df":0,"u":{"docs":{"36":{"tf":1.0}},"df":1}},"e":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"10":{"tf":1.0}},"df":1}},"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"136":{"tf":1.0},"137":{"tf":1.0},"69":{"tf":1.0},"87":{"tf":1.0},"88":{"tf":1.0}},"df":5}}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"14":{"tf":1.0}},"df":1}},"h":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{"146":{"tf":1.0},"151":{"tf":1.0},"154":{"tf":1.0}},"df":3}}}}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{"71":{"tf":1.0}},"df":1}}}},"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{"75":{"tf":1.0}},"df":1}}}}},"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{"131":{"tf":1.0}},"df":1}}}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"122":{"tf":1.0},"94":{"tf":1.0}},"df":2}}},"e":{"docs":{},"df":0,"x":{"docs":{"126":{"tf":1.0}},"df":1}}}}},"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{"143":{"tf":1.0},"144":{"tf":1.4142135623730951}},"df":2}}},"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"113":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"l":{"docs":{"182":{"tf":1.0}},"df":1}}}}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"d":{"docs":{"12":{"tf":1.0},"191":{"tf":1.0}},"df":2}}}},"n":{"docs":{"56":{"tf":1.0}},"df":1,"e":{"docs":{"49":{"tf":1.0}},"df":1}}}},"o":{"docs":{},"df":0,"f":{"docs":{},"df":0,"f":{"docs":{"135":{"tf":1.0}},"df":1}},"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"27":{"tf":1.0}},"df":1,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"d":{"docs":{"27":{"tf":1.0},"50":{"tf":1.0}},"df":2}}}},"r":{"docs":{"130":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{"186":{"tf":1.0},"187":{"tf":1.0},"188":{"tf":1.0},"189":{"tf":1.0},"190":{"tf":1.0}},"df":5}}}},"s":{"docs":{"41":{"tf":1.0}},"df":1},"u":{"docs":{},"df":0,"t":{"docs":{"136":{"tf":1.0}},"df":1}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{"17":{"tf":1.0},"24":{"tf":1.0},"60":{"tf":1.0}},"df":3}}}}}}}},"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{"147":{"tf":1.0},"197":{"tf":1.0},"72":{"tf":1.0}},"df":3,"k":{"docs":{"166":{"tf":1.0}},"df":1,"a":{"docs":{},"df":0,"g":{"docs":{"43":{"tf":1.0}},"df":1}}}},"n":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"78":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"155":{"tf":1.0}},"df":1}}}}},"t":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"n":{"docs":{"140":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":1.0}},"df":3}}}}}},"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"m":{"docs":{"191":{"tf":1.0}},"df":1}}}},"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"106":{"tf":1.0},"127":{"tf":1.0},"72":{"tf":1.0},"85":{"tf":1.0},"86":{"tf":1.0},"88":{"tf":1.0}},"df":6}}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{"153":{"tf":1.4142135623730951},"154":{"tf":1.7320508075688772},"155":{"tf":1.4142135623730951}},"df":3}},"o":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"164":{"tf":1.0}},"df":1}}}}},"r":{"docs":{},"df":0,"t":{"docs":{"154":{"tf":1.0}},"df":1,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"114":{"tf":1.0}},"df":1}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"149":{"tf":1.0}},"df":1}}}},"p":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"157":{"tf":1.0}},"df":1}}}}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"q":{"docs":{},"df":0,"u":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"4":{"tf":1.0}},"df":1}}}}}}}}},"o":{"docs":{},"df":0,"b":{"docs":{},"df":0,"e":{"docs":{"26":{"tf":1.0},"29":{"tf":1.0},"32":{"tf":1.0},"33":{"tf":1.0}},"df":4}},"g":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"104":{"tf":1.0},"56":{"tf":1.0},"60":{"tf":1.0}},"df":3}}}},"j":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"180":{"tf":1.0},"184":{"tf":1.0}},"df":2}}}},"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{"146":{"tf":1.0},"154":{"tf":1.0}},"df":2}}}}}},"q":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{"22":{"tf":1.0},"51":{"tf":1.0},"55":{"tf":1.0}},"df":3}}}},"r":{"docs":{},"df":0,"e":{"docs":{"9":{"tf":1.0}},"df":1,"a":{"docs":{},"df":0,"d":{"docs":{"73":{"tf":1.0}},"df":1},"l":{"docs":{"130":{"tf":1.0},"87":{"tf":1.0}},"df":2}},"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"152":{"tf":1.0}},"df":1}}}}}}},"e":{"docs":{},"df":0,"x":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"147":{"tf":1.4142135623730951}},"df":1}}}}}},"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"164":{"tf":1.0}},"df":1}}},"g":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"147":{"tf":1.0},"69":{"tf":1.0},"90":{"tf":1.0}},"df":3}}}},"p":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"107":{"tf":1.0}},"df":1}}}}}}},"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"c":{"docs":{"167":{"tf":1.0},"5":{"tf":1.0}},"df":2}}}}}},"s":{"docs":{"26":{"tf":1.0},"29":{"tf":1.0}},"df":2},"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"128":{"tf":1.0}},"df":1}},"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"170":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{"44":{"tf":1.0},"95":{"tf":1.0}},"df":2}},"n":{"docs":{"63":{"tf":1.0}},"df":1,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{"15":{"tf":1.0}},"df":1}}}},"s":{"docs":{},"df":0,"t":{"docs":{"1":{"tf":1.0},"12":{"tf":1.0},"171":{"tf":1.0},"176":{"tf":1.0},"179":{"tf":1.0},"23":{"tf":1.0},"38":{"tf":1.0},"56":{"tf":1.0},"99":{"tf":1.0}},"df":9,"i":{"docs":{"33":{"tf":1.0},"93":{"tf":1.0}},"df":2}}}}},"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{"110":{"tf":1.0}},"df":1}}}}},"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{"2":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{"176":{"tf":1.0}},"df":1}}}}},"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"123":{"tf":1.0}},"df":1}}}}},"e":{"docs":{"18":{"tf":1.0}},"df":1},"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"158":{"tf":1.0}},"df":1}}}},"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"h":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"77":{"tf":1.0}},"df":1}}}}}},"n":{"docs":{},"df":0,"d":{"docs":{"125":{"tf":1.0}},"df":1}},"t":{"docs":{"180":{"tf":1.0}},"df":1}},"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{"127":{"tf":1.0}},"df":1}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"97":{"tf":1.0}},"df":1}}}}}}},"z":{"docs":{},"df":0,"e":{"docs":{"112":{"tf":1.0},"153":{"tf":1.0},"159":{"tf":1.0},"186":{"tf":1.0},"190":{"tf":1.0}},"df":5}}},"o":{"docs":{},"df":0,"f":{"docs":{},"df":0,"t":{"docs":{},"df":0,"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"25":{"tf":1.0}},"df":1}}}}}},"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{"87":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{},"df":0,"f":{"docs":{"41":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"d":{"docs":{"186":{"tf":1.0},"189":{"tf":1.0}},"df":2}}},"i":{"docs":{"198":{"tf":1.0}},"df":1}},"t":{"docs":{"34":{"tf":1.0},"52":{"tf":1.0}},"df":2,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{"56":{"tf":1.0}},"df":1}}}}},"r":{"docs":{},"df":0,"t":{"docs":{"54":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"e":{"docs":{"106":{"tf":1.0},"109":{"tf":1.0},"155":{"tf":1.4142135623730951},"94":{"tf":1.0}},"df":4},"i":{"docs":{},"df":0,"c":{"docs":{"103":{"tf":1.0}},"df":1}}}},"m":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{},"df":0,"f":{"docs":{},"df":0,"3":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"11":{"tf":1.0}},"df":1}}}}}}}}}}}}}},"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{"105":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{"29":{"tf":1.0}},"df":1}}}}},"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"16":{"tf":1.0}},"df":1}}}}},"p":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"100":{"tf":1.0}},"df":1}}}}}},"v":{"docs":{},"df":0,"d":{"docs":{"199":{"tf":1.0}},"df":1}},"y":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{"125":{"tf":1.0}},"df":1}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{"130":{"tf":1.0},"161":{"tf":1.0},"169":{"tf":1.0},"177":{"tf":1.0},"22":{"tf":1.0}},"df":5}}}}}},"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{"110":{"tf":1.0},"130":{"tf":1.0},"138":{"tf":1.0},"158":{"tf":1.0},"159":{"tf":1.0}},"df":5}},"p":{"docs":{"156":{"tf":1.0}},"df":1}},"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{"19":{"tf":1.0},"23":{"tf":1.0},"25":{"tf":1.0},"37":{"tf":1.0},"46":{"tf":1.0},"47":{"tf":1.0}},"df":6,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"38":{"tf":1.0}},"df":1}}}}}}},"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"185":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"3":{"docs":{},"df":0,"2":{"docs":{"30":{"tf":1.0}},"df":1}}}},"d":{"docs":{},"df":0,"e":{"docs":{"135":{"tf":1.0}},"df":1,"o":{"docs":{},"df":0,"f":{"docs":{},"df":0,"f":{"docs":{"186":{"tf":1.0}},"df":1}}}}},"i":{"docs":{},"df":0,"t":{"docs":{"148":{"tf":1.4142135623730951},"150":{"tf":1.0}},"df":2}},"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"6":{"tf":1.0}},"df":1}}}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"102":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"177":{"tf":1.0}},"df":1}}}}}},"y":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{"105":{"tf":1.0},"109":{"tf":1.0},"112":{"tf":1.0},"146":{"tf":1.0},"148":{"tf":1.0},"153":{"tf":1.0},"154":{"tf":1.0},"155":{"tf":1.0},"166":{"tf":1.0}},"df":9,"s":{"docs":{},"df":0,"t":{"docs":{"104":{"tf":1.0}},"df":1}}}}}},"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"200":{"tf":1.0}},"df":1}}},"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"v":{"docs":{"44":{"tf":1.0}},"df":1}}},"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"185":{"tf":1.0}},"df":1}}}}},"p":{"docs":{"180":{"tf":1.0}},"df":1},"s":{"docs":{"133":{"tf":1.0},"134":{"tf":1.0},"139":{"tf":1.0},"150":{"tf":1.0},"57":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.0},"7":{"tf":1.0},"72":{"tf":1.0},"76":{"tf":1.0},"9":{"tf":1.0},"98":{"tf":1.0}},"df":12,"a":{"docs":{},"df":0,"g":{"docs":{"137":{"tf":1.0}},"df":1},"r":{"docs":{},"df":0,"t":{"docs":{"201":{"tf":1.0}},"df":1}}},"b":{"docs":{"52":{"tf":1.0}},"df":1},"e":{"docs":{},"df":0,"r":{"docs":{"116":{"tf":1.0}},"df":1}}}},"v":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{"98":{"tf":1.0}},"df":1}}}}}}}},"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{"53":{"tf":1.0}},"df":1}}}}},"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"29":{"tf":1.0}},"df":1}}}}},"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"165":{"tf":1.0},"92":{"tf":1.0}},"df":2}}}}}},"s":{"docs":{"163":{"tf":1.0},"164":{"tf":1.0}},"df":2}},"w":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"138":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"48":{"tf":1.0}},"df":1}}}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"138":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"173":{"tf":1.0}},"df":1,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"146":{"tf":1.0},"93":{"tf":1.0}},"df":2}}}}},"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{"74":{"tf":1.0}},"df":1}}}}},"z":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{"111":{"tf":1.0},"112":{"tf":1.0},"153":{"tf":1.0}},"df":3}}},"s":{"docs":{},"df":0,"t":{"docs":{"153":{"tf":1.0}},"df":1}}}}}},"documentStore":{"save":true,"docs":{"0":{"body":"Welcome to The Embedded Rust Book: An introductory book about using the Rust\\nProgramming Language on “Bare Metal” embedded systems, such as Microcontrollers.","breadcrumbs":"Introduction » Introduction","id":"0","title":"Introduction"},"1":{"body":"Embedded Rust is for everyone who wants to do embedded programming while taking advantage of the higher-level concepts and safety guarantees the Rust language provides.\\n(See also Who Rust Is For)","breadcrumbs":"Introduction » Who Embedded Rust is For","id":"1","title":"Who Embedded Rust is For"},"10":{"body":"Let’s get familiar with the hardware we’ll be working with.","breadcrumbs":"Introduction » Hardware » Meet Your Hardware","id":"10","title":"Meet Your Hardware"},"100":{"body":"Although we created our own Peripherals structure above, it is not necessary to do this for your code. the cortex_m crate contains a macro called singleton!() that will perform this action for you. use cortex_m::singleton; fn main() { // OK if `main` is executed only once let x: &\'static mut bool = singleton!(: bool = false).unwrap();\\n} cortex_m docs Additionally, if you use cortex-m-rtic, the entire process of defining and obtaining these peripherals are abstracted for you, and you are instead handed a Peripherals structure that contains a non- Option version of all of the items you define. // cortex-m-rtic v0.5.x\\n#[rtic::app(device = lm3s6965, peripherals = true)]\\nconst APP: () = { #[init] fn init(cx: init::Context) { static mut X: u32 = 0; // Cortex-M peripherals let core: cortex_m::Peripherals = cx.core; // Device specific peripherals let device: lm3s6965::Peripherals = cx.device; }\\n}","breadcrumbs":"Peripherals » Singletons » Existing library support","id":"100","title":"Existing library support"},"101":{"body":"But how do these Singletons make a noticeable difference in how our Rust code works? impl SerialPort { const SER_PORT_SPEED_REG: *mut u32 = 0x4000_1000 as _; fn read_speed( &self // <------ This is really, really important ) -> u32 { unsafe { ptr::read_volatile(Self::SER_PORT_SPEED_REG) } }\\n} There are two important factors in play here: Because we are using a singleton, there is only one way or place to obtain a SerialPort structure To call the read_speed() method, we must have ownership or a reference to a SerialPort structure These two factors put together means that it is only possible to access the hardware if we have appropriately satisfied the borrow checker, meaning that at no point do we have multiple mutable references to the same hardware! fn main() { // missing reference to `self`! Won\'t work. // SerialPort::read_speed(); let serial_1 = unsafe { PERIPHERALS.take_serial() }; // you can only read what you have access to let _ = serial_1.read_speed();\\n}","breadcrumbs":"Peripherals » Singletons » But why?","id":"101","title":"But why?"},"102":{"body":"Additionally, because some references are mutable, and some are immutable, it becomes possible to see whether a function or method could potentially modify the state of the hardware. For example, This is allowed to change hardware settings: fn setup_spi_port( spi: &mut SpiPort, cs_pin: &mut GpioPin\\n) -> Result<()> { // ...\\n} This isn’t: fn read_button(gpio: &GpioPin) -> bool { // ...\\n} This allows us to enforce whether code should or should not make changes to hardware at compile time, rather than at runtime. As a note, this generally only works across one application, but for bare metal systems, our software will be compiled into a single application, so this is not usually a restriction.","breadcrumbs":"Peripherals » Singletons » Treat your hardware like data","id":"102","title":"Treat your hardware like data"},"103":{"body":"Rust’s type system prevents data races at compile time (see Send and Sync traits). The type system can also be used to check other properties at\\ncompile time; reducing the need for runtime checks in some cases. When applied to embedded programs these static checks can be used, for\\nexample, to enforce that configuration of I/O interfaces is done properly. For\\ninstance, one can design an API where it is only possible to initialize a serial\\ninterface by first configuring the pins that will be used by the interface. One can also statically check that operations, like setting a pin low, can only\\nbe performed on correctly configured peripherals. For example, trying to change\\nthe output state of a pin configured in floating input mode would raise a\\ncompile error. And, as seen in the previous chapter, the concept of ownership can be applied\\nto peripherals to ensure that only certain parts of a program can modify a\\nperipheral. This access control makes software easier to reason about\\ncompared to the alternative of treating peripherals as global mutable state.","breadcrumbs":"Static Guarantees » Static Guarantees","id":"103","title":"Static Guarantees"},"104":{"body":"The concept of typestates describes the encoding of information about the current state of an object into the type of that object. Although this can sound a little arcane, if you have used the Builder Pattern in Rust, you have already started using Typestate Programming! pub mod foo_module { #[derive(Debug)] pub struct Foo { inner: u32, } pub struct FooBuilder { a: u32, b: u32, } impl FooBuilder { pub fn new(starter: u32) -> Self { Self { a: starter, b: starter, } } pub fn double_a(self) -> Self { Self { a: self.a * 2, b: self.b, } } pub fn into_foo(self) -> Foo { Foo { inner: self.a + self.b, } } }\\n} fn main() { let x = foo_module::FooBuilder::new(10) .double_a() .into_foo(); println!(\\"{:#?}\\", x);\\n} In this example, there is no direct way to create a Foo object. We must create a FooBuilder, and properly initialize it before we can obtain the Foo object we want. This minimal example encodes two states: FooBuilder, which represents an “unconfigured”, or “configuration in process” state Foo, which represents a “configured”, or “ready to use” state.","breadcrumbs":"Static Guarantees » Typestate Programming » Typestate Programming","id":"104","title":"Typestate Programming"},"105":{"body":"Because Rust has a Strong Type System, there is no easy way to magically create an instance of Foo, or to turn a FooBuilder into a Foo without calling the into_foo() method. Additionally, calling the into_foo() method consumes the original FooBuilder structure, meaning it can not be reused without the creation of a new instance. This allows us to represent the states of our system as types, and to include the necessary actions for state transitions into the methods that exchange one type for another. By creating a FooBuilder, and exchanging it for a Foo object, we have walked through the steps of a basic state machine.","breadcrumbs":"Static Guarantees » Typestate Programming » Strong Types","id":"105","title":"Strong Types"},"106":{"body":"The peripherals of a microcontroller can be thought of as set of state machines. For example, the configuration of a simplified GPIO pin could be represented as the following tree of states: Disabled Enabled Configured as Output Output: High Output: Low Configured as Input Input: High Resistance Input: Pulled Low Input: Pulled High If the peripheral starts in the Disabled mode, to move to the Input: High Resistance mode, we must perform the following steps: Disabled Enabled Configured as Input Input: High Resistance If we wanted to move from Input: High Resistance to Input: Pulled Low, we must perform the following steps: Input: High Resistance Input: Pulled Low Similarly, if we want to move a GPIO pin from configured as Input: Pulled Low to Output: High, we must perform the following steps: Input: Pulled Low Configured as Input Configured as Output Output: High","breadcrumbs":"Static Guarantees » Peripherals as State Machines » Peripherals as State Machines","id":"106","title":"Peripherals as State Machines"},"107":{"body":"Typically the states listed above are set by writing values to given registers mapped to a GPIO peripheral. Let’s define an imaginary GPIO Configuration Register to illustrate this: Name Bit Number(s) Value Meaning Notes enable 0 0 disabled Disables the GPIO 1 enabled Enables the GPIO direction 1 0 input Sets the direction to Input 1 output Sets the direction to Output input_mode 2..3 00 hi-z Sets the input as high resistance 01 pull-low Input pin is pulled low 10 pull-high Input pin is pulled high 11 n/a Invalid state. Do not set output_mode 4 0 set-low Output pin is driven low 1 set-high Output pin is driven high input_status 5 x in-val 0 if input is < 1.5v, 1 if input >= 1.5v We could expose the following structure in Rust to control this GPIO: /// GPIO interface\\nstruct GpioConfig { /// GPIO Configuration structure generated by svd2rust periph: GPIO_CONFIG,\\n} impl GpioConfig { pub fn set_enable(&mut self, is_enabled: bool) { self.periph.modify(|_r, w| { w.enable().set_bit(is_enabled) }); } pub fn set_direction(&mut self, is_output: bool) { self.periph.modify(|_r, w| { w.direction().set_bit(is_output) }); } pub fn set_input_mode(&mut self, variant: InputMode) { self.periph.modify(|_r, w| { w.input_mode().variant(variant) }); } pub fn set_output_mode(&mut self, is_high: bool) { self.periph.modify(|_r, w| { w.output_mode.set_bit(is_high) }); } pub fn get_input_status(&self) -> bool { self.periph.read().input_status().bit_is_set() }\\n} However, this would allow us to modify certain registers that do not make sense. For example, what happens if we set the output_mode field when our GPIO is configured as an input? In general, use of this structure would allow us to reach states not defined by our state machine above: e.g. an output that is pulled low, or an input that is set high. For some hardware, this may not matter. On other hardware, it could cause unexpected or undefined behavior! Although this interface is convenient to write, it doesn’t enforce the design contracts set out by our hardware implementation.","breadcrumbs":"Static Guarantees » Peripherals as State Machines » Hardware Representation","id":"107","title":"Hardware Representation"},"108":{"body":"In our last chapter, we wrote an interface that didn’t enforce design contracts. Let’s take another look at our imaginary GPIO configuration register: Name Bit Number(s) Value Meaning Notes enable 0 0 disabled Disables the GPIO 1 enabled Enables the GPIO direction 1 0 input Sets the direction to Input 1 output Sets the direction to Output input_mode 2..3 00 hi-z Sets the input as high resistance 01 pull-low Input pin is pulled low 10 pull-high Input pin is pulled high 11 n/a Invalid state. Do not set output_mode 4 0 set-low Output pin is driven low 1 set-high Output pin is driven high input_status 5 x in-val 0 if input is < 1.5v, 1 if input >= 1.5v If we instead checked the state before making use of the underlying hardware, enforcing our design contracts at runtime, we might write code that looks like this instead: /// GPIO interface\\nstruct GpioConfig { /// GPIO Configuration structure generated by svd2rust periph: GPIO_CONFIG,\\n} impl GpioConfig { pub fn set_enable(&mut self, is_enabled: bool) { self.periph.modify(|_r, w| { w.enable().set_bit(is_enabled) }); } pub fn set_direction(&mut self, is_output: bool) -> Result<(), ()> { if self.periph.read().enable().bit_is_clear() { // Must be enabled to set direction return Err(()); } self.periph.modify(|r, w| { w.direction().set_bit(is_output) }); Ok(()) } pub fn set_input_mode(&mut self, variant: InputMode) -> Result<(), ()> { if self.periph.read().enable().bit_is_clear() { // Must be enabled to set input mode return Err(()); } if self.periph.read().direction().bit_is_set() { // Direction must be input return Err(()); } self.periph.modify(|_r, w| { w.input_mode().variant(variant) }); Ok(()) } pub fn set_output_status(&mut self, is_high: bool) -> Result<(), ()> { if self.periph.read().enable().bit_is_clear() { // Must be enabled to set output status return Err(()); } if self.periph.read().direction().bit_is_clear() { // Direction must be output return Err(()); } self.periph.modify(|_r, w| { w.output_mode.set_bit(is_high) }); Ok(()) } pub fn get_input_status(&self) -> Result { if self.periph.read().enable().bit_is_clear() { // Must be enabled to get status return Err(()); } if self.periph.read().direction().bit_is_set() { // Direction must be input return Err(()); } Ok(self.periph.read().input_status().bit_is_set()) }\\n} Because we need to enforce the restrictions on the hardware, we end up doing a lot of runtime checking which wastes time and resources, and this code will be much less pleasant for the developer to use.","breadcrumbs":"Static Guarantees » Design Contracts » Design Contracts","id":"108","title":"Design Contracts"},"109":{"body":"But what if instead, we used Rust’s type system to enforce the state transition rules? Take this example: /// GPIO interface\\nstruct GpioConfig { /// GPIO Configuration structure generated by svd2rust periph: GPIO_CONFIG, enabled: ENABLED, direction: DIRECTION, mode: MODE,\\n} // Type states for MODE in GpioConfig\\nstruct Disabled;\\nstruct Enabled;\\nstruct Output;\\nstruct Input;\\nstruct PulledLow;\\nstruct PulledHigh;\\nstruct HighZ;\\nstruct DontCare; /// These functions may be used on any GPIO Pin\\nimpl GpioConfig { pub fn into_disabled(self) -> GpioConfig { self.periph.modify(|_r, w| w.enable.disabled()); GpioConfig { periph: self.periph, enabled: Disabled, direction: DontCare, mode: DontCare, } } pub fn into_enabled_input(self) -> GpioConfig { self.periph.modify(|_r, w| { w.enable.enabled() .direction.input() .input_mode.high_z() }); GpioConfig { periph: self.periph, enabled: Enabled, direction: Input, mode: HighZ, } } pub fn into_enabled_output(self) -> GpioConfig { self.periph.modify(|_r, w| { w.enable.enabled() .direction.output() .input_mode.set_high() }); GpioConfig { periph: self.periph, enabled: Enabled, direction: Output, mode: DontCare, } }\\n} /// This function may be used on an Output Pin\\nimpl GpioConfig { pub fn set_bit(&mut self, set_high: bool) { self.periph.modify(|_r, w| w.output_mode.set_bit(set_high)); }\\n} /// These methods may be used on any enabled input GPIO\\nimpl GpioConfig { pub fn bit_is_set(&self) -> bool { self.periph.read().input_status.bit_is_set() } pub fn into_input_high_z(self) -> GpioConfig { self.periph.modify(|_r, w| w.input_mode().high_z()); GpioConfig { periph: self.periph, enabled: Enabled, direction: Input, mode: HighZ, } } pub fn into_input_pull_down(self) -> GpioConfig { self.periph.modify(|_r, w| w.input_mode().pull_low()); GpioConfig { periph: self.periph, enabled: Enabled, direction: Input, mode: PulledLow, } } pub fn into_input_pull_up(self) -> GpioConfig { self.periph.modify(|_r, w| w.input_mode().pull_high()); GpioConfig { periph: self.periph, enabled: Enabled, direction: Input, mode: PulledHigh, } }\\n} Now let’s see what the code using this would look like: /* * Example 1: Unconfigured to High-Z input */\\nlet pin: GpioConfig = get_gpio(); // Can\'t do this, pin isn\'t enabled!\\n// pin.into_input_pull_down(); // Now turn the pin from unconfigured to a high-z input\\nlet input_pin = pin.into_enabled_input(); // Read from the pin\\nlet pin_state = input_pin.bit_is_set(); // Can\'t do this, input pins don\'t have this interface!\\n// input_pin.set_bit(true); /* * Example 2: High-Z input to Pulled Low input */\\nlet pulled_low = input_pin.into_input_pull_down();\\nlet pin_state = pulled_low.bit_is_set(); /* * Example 3: Pulled Low input to Output, set high */\\nlet output_pin = pulled_low.into_enabled_output();\\noutput_pin.set_bit(true); // Can\'t do this, output pins don\'t have this interface!\\n// output_pin.into_input_pull_down(); This is definitely a convenient way to store the state of the pin, but why do it this way? Why is this better than storing the state as an enum inside of our GpioConfig structure?","breadcrumbs":"Static Guarantees » Design Contracts » Type States","id":"109","title":"Type States"},"11":{"body":"What does this board contain? A STM32F303VCT6 microcontroller. This microcontroller has A single-core ARM Cortex-M4F processor with hardware support for single-precision floating point\\noperations and a maximum clock frequency of 72 MHz. 256 KiB of “Flash” memory. (1 KiB = 10 24 bytes) 48 KiB of RAM. A variety of integrated peripherals such as timers, I2C, SPI and USART. General purpose Input Output (GPIO) and other types of pins accessible through the two rows of headers along side the board. A Mini-USB interface accessible through the USB port labeled “USB USER”. An accelerometer as part of the LSM303DLHC chip. A magnetometer as part of the LSM303DLHC chip. A gyroscope as part of the L3GD20 chip. 8 user LEDs arranged in the shape of a compass. A second microcontroller: a STM32F103. This microcontroller is actually part of an on-board programmer / debugger and is connected to the Mini-USB port named “USB ST-LINK”. For a more detailed list of features and further specifications of the board take a look at the STMicroelectronics website. A word of caution: be careful if you want to apply external signals to the board. The microcontroller STM32F303VCT6 pins take a nominal voltage of 3.3 volts. For further information consult the 6.2 Absolute maximum ratings section in the manual","breadcrumbs":"Introduction » Hardware » STM32F3DISCOVERY (the “F3”)","id":"11","title":"STM32F3DISCOVERY (the “F3”)"},"110":{"body":"Because we are enforcing our design constraints entirely at compile time, this incurs no runtime cost. It is impossible to set an output mode when you have a pin in an input mode. Instead, you must walk through the states by converting it to an output pin, and then setting the output mode. Because of this, there is no runtime penalty due to checking the current state before executing a function. Also, because these states are enforced by the type system, there is no longer room for errors by consumers of this interface. If they try to perform an illegal state transition, the code will not compile!","breadcrumbs":"Static Guarantees » Design Contracts » Compile Time Functional Safety","id":"110","title":"Compile Time Functional Safety"},"111":{"body":"Type states are also an excellent example of Zero Cost Abstractions - the ability to move certain behaviors to compile time execution or analysis. These type states contain no actual data, and are instead used as markers. Since they contain no data, they have no actual representation in memory at runtime: use core::mem::size_of; let _ = size_of::(); // == 0\\nlet _ = size_of::(); // == 0\\nlet _ = size_of::(); // == 0\\nlet _ = size_of::>(); // == 0","breadcrumbs":"Static Guarantees » Zero Cost Abstractions » Zero Cost Abstractions","id":"111","title":"Zero Cost Abstractions"},"112":{"body":"struct Enabled; Structures defined like this are called Zero Sized Types, as they contain no actual data. Although these types act “real” at compile time - you can copy them, move them, take references to them, etc., however the optimizer will completely strip them away. In this snippet of code: pub fn into_input_high_z(self) -> GpioConfig { self.periph.modify(|_r, w| w.input_mode().high_z()); GpioConfig { periph: self.periph, enabled: Enabled, direction: Input, mode: HighZ, }\\n} The GpioConfig we return never exists at runtime. Calling this function will generally boil down to a single assembly instruction - storing a constant register value to a register location. This means that the type state interface we’ve developed is a zero cost abstraction - it uses no more CPU, RAM, or code space tracking the state of GpioConfig, and renders to the same machine code as a direct register access.","breadcrumbs":"Static Guarantees » Zero Cost Abstractions » Zero Sized Types","id":"112","title":"Zero Sized Types"},"113":{"body":"In general, these abstractions may be nested as deeply as you would like. As long as all components used are zero sized types, the whole structure will not exist at runtime. For complex or deeply nested structures, it may be tedious to define all possible combinations of state. In these cases, macros may be used to generate all implementations.","breadcrumbs":"Static Guarantees » Zero Cost Abstractions » Nesting","id":"113","title":"Nesting"},"114":{"body":"In embedded environments portability is a very important topic: Every vendor and even each family from a single manufacturer offers different peripherals and capabilities and similarly the ways to interact with the peripherals will vary. A common way to equalize such differences is via a layer called Hardware Abstraction layer or HAL. Hardware abstractions are sets of routines in software that emulate some platform-specific details, giving programs direct access to the hardware resources. They often allow programmers to write device-independent, high performance applications by providing standard operating system (OS) calls to hardware. Wikipedia: Hardware Abstraction Layer Embedded systems are a bit special in this regard since we typically do not have operating systems and user installable software but firmware images which are compiled as a whole as well as a number of other constraints. So while the traditional approach as defined by Wikipedia could potentially work it is likely not the most productive approach to ensure portability. How do we do this in Rust? Enter embedded-hal…","breadcrumbs":"Portability » Portability","id":"114","title":"Portability"},"115":{"body":"In a nutshell it is a set of traits which define implementation contracts between HAL implementations, drivers and applications (or firmwares). Those contracts include both capabilities (i.e. if a trait is implemented for a certain type, the HAL implementation provides a certain capability) and methods (i.e. if you can construct a type implementing a trait it is guaranteed that you have the methods specified in the trait available). A typical layering might look like this: Some of the defined traits in embedded-hal are: GPIO (input and output pins) Serial communication I2C SPI Timers/Countdowns Analog Digital Conversion The main reason for having the embedded-hal traits and crates implementing and using them is to keep complexity in check. If you consider that an application might have to implement the use of the peripheral in the hardware as well as the application and potentially drivers for additional hardware components, then it should be easy to see that the re-usability is very limited. Expressed mathematically, if M is the number of peripheral HAL implementations and N the number of drivers then if we were to reinvent the wheel for every application then we would end up with M*N implementations while by using the API provided by the embedded-hal traits will make the implementation complexity approach M+N. Of course there’re additional benefits to be had, such as less trial-and-error due to a well-defined and ready-to-use APIs.","breadcrumbs":"Portability » What is embedded-hal?","id":"115","title":"What is embedded-hal?"},"116":{"body":"As said above there are three main users of the HAL:","breadcrumbs":"Portability » Users of the embedded-hal","id":"116","title":"Users of the embedded-hal"},"117":{"body":"A HAL implementation provides the interfacing between the hardware and the users of the HAL traits. Typical implementations consist of three parts: One or more hardware specific types Functions to create and initialize such a type, often providing various configuration options (speed, operation mode, use pins, etc.) one or more trait impl of embedded-hal traits for that type Such a HAL implementation can come in various flavours: Via low-level hardware access, e.g. via registers Via operating system, e.g. by using the sysfs under Linux Via adapter, e.g. a mock of types for unit testing Via driver for hardware adapters, e.g. I2C multiplexer or GPIO expander","breadcrumbs":"Portability » HAL implementation","id":"117","title":"HAL implementation"},"118":{"body":"A driver implements a set of custom functionality for an internal or external component, connected to a peripheral implementing the embedded-hal traits. Typical examples for such drivers include various sensors (temperature, magnetometer, accelerometer, light), display devices (LED arrays, LCD displays) and actuators (motors, transmitters). A driver has to be initialized with an instance of type that implements a certain trait of the embedded-hal which is ensured via trait bound and provides its own type instance with a custom set of methods allowing to interact with the driven device.","breadcrumbs":"Portability » Driver","id":"118","title":"Driver"},"119":{"body":"The application binds the various parts together and ensures that the desired functionality is achieved. When porting between different systems, this is the part which requires the most adaptation efforts, since the application needs to correctly initialize the real hardware via the HAL implementation and the initialisation of different hardware differs, sometimes drastically so. Also the user choice often plays a big role, since components can be physically connected to different terminals, hardware buses sometimes need external hardware to match the configuration or there are different trade-offs to be made in the use of internal peripherals (e.g. multiple timers with different capabilities are available or peripherals conflict with others).","breadcrumbs":"Portability » Application","id":"119","title":"Application"},"12":{"body":"The term Embedded Programming is used for a wide range of different classes of programming.\\nRanging from programming 8-Bit MCUs (like the ST72325xx)\\nwith just a few KB of RAM and ROM, up to systems like the Raspberry Pi\\n( Model B 3+) which has a 32/64-bit\\n4-core Cortex-A53 @ 1.4 GHz and 1GB of RAM. Different restrictions/limitations will apply when writing code\\ndepending on what kind of target and use case you have. There are two general Embedded Programming classifications:","breadcrumbs":"Introduction » no_std » A no_std Rust Environment","id":"12","title":"A no_std Rust Environment"},"120":{"body":"Concurrency happens whenever different parts of your program might execute\\nat different times or out of order. In an embedded context, this includes: interrupt handlers, which run whenever the associated interrupt happens, various forms of multithreading, where your microprocessor regularly swaps\\nbetween parts of your program, and in some systems, multiple-core microprocessors, where each core can be\\nindependently running a different part of your program at the same time. Since many embedded programs need to deal with interrupts, concurrency will\\nusually come up sooner or later, and it’s also where many subtle and difficult\\nbugs can occur. Luckily, Rust provides a number of abstractions and safety\\nguarantees to help us write correct code.","breadcrumbs":"Concurrency » Concurrency","id":"120","title":"Concurrency"},"121":{"body":"The simplest concurrency for an embedded program is no concurrency: your\\nsoftware consists of a single main loop which just keeps running, and there\\nare no interrupts at all. Sometimes this is perfectly suited to the problem\\nat hand! Typically your loop will read some inputs, perform some processing,\\nand write some outputs. #[entry]\\nfn main() { let peripherals = setup_peripherals(); loop { let inputs = read_inputs(&peripherals); let outputs = process(inputs); write_outputs(&peripherals, outputs); }\\n} Since there’s no concurrency, there’s no need to worry about sharing data\\nbetween parts of your program or synchronising access to peripherals. If\\nyou can get away with such a simple approach this can be a great solution.","breadcrumbs":"Concurrency » No Concurrency","id":"121","title":"No Concurrency"},"122":{"body":"Unlike non-embedded Rust, we will not usually have the luxury of creating\\nheap allocations and passing references to that data into a newly-created\\nthread. Instead, our interrupt handlers might be called at any time and must\\nknow how to access whatever shared memory we are using. At the lowest level,\\nthis means we must have statically allocated mutable memory, which\\nboth the interrupt handler and the main code can refer to. In Rust, such static mut variables are always unsafe to read or write,\\nbecause without taking special care, you might trigger a race condition,\\nwhere your access to the variable is interrupted halfway through by an\\ninterrupt which also accesses that variable. For an example of how this behaviour can cause subtle errors in your code,\\nconsider an embedded program which counts rising edges of some input signal\\nin each one-second period (a frequency counter): static mut COUNTER: u32 = 0; #[entry]\\nfn main() -> ! { set_timer_1hz(); let mut last_state = false; loop { let state = read_signal_level(); if state && !last_state { // DANGER - Not actually safe! Could cause data races. unsafe { COUNTER += 1 }; } last_state = state; }\\n} #[interrupt]\\nfn timer() { unsafe { COUNTER = 0; }\\n} Each second, the timer interrupt sets the counter back to 0. Meanwhile, the\\nmain loop continually measures the signal, and incremements the counter when\\nit sees a change from low to high. We’ve had to use unsafe to access COUNTER, as it’s static mut, and that means we’re promising the compiler\\nwe won’t cause any undefined behaviour. Can you spot the race condition? The\\nincrement on COUNTER is not guaranteed to be atomic — in fact, on most\\nembedded platforms, it will be split into a load, then the increment, then\\na store. If the interrupt fired after the load but before the store, the\\nreset back to 0 would be ignored after the interrupt returns — and we would\\ncount twice as many transitions for that period.","breadcrumbs":"Concurrency » Global Mutable Data","id":"122","title":"Global Mutable Data"},"123":{"body":"So, what can we do about data races? A simple approach is to use critical\\nsections, a context where interrupts are disabled. By wrapping the access to COUNTER in main in a critical section, we can be sure the timer interrupt\\nwill not fire until we’re finished incrementing COUNTER: static mut COUNTER: u32 = 0; #[entry]\\nfn main() -> ! { set_timer_1hz(); let mut last_state = false; loop { let state = read_signal_level(); if state && !last_state { // New critical section ensures synchronised access to COUNTER cortex_m::interrupt::free(|_| { unsafe { COUNTER += 1 }; }); } last_state = state; }\\n} #[interrupt]\\nfn timer() { unsafe { COUNTER = 0; }\\n} In this example, we use cortex_m::interrupt::free, but other platforms will\\nhave similar mechanisms for executing code in a critical section. This is also\\nthe same as disabling interrupts, running some code, and then re-enabling\\ninterrupts. Note we didn’t need to put a critical section inside the timer interrupt,\\nfor two reasons: Writing 0 to COUNTER can’t be affected by a race since we don’t read it It will never be interrupted by the main thread anyway If COUNTER was being shared by multiple interrupt handlers that might preempt each other, then each one might require a critical section as well. This solves our immediate problem, but we’re still left writing a lot of unsafe code which we need to carefully reason about, and we might be using critical sections needlessly. Since each critical section temporarily pauses interrupt processing, there is an associated cost of some extra code size and higher interrupt latency and jitter (interrupts may take longer to be processed, and the time until they are processed will be more variable). Whether this is a problem depends on your system, but in general, we’d like to avoid it. It’s worth noting that while a critical section guarantees no interrupts will\\nfire, it does not provide an exclusivity guarantee on multi-core systems! The\\nother core could be happily accessing the same memory as your core, even\\nwithout interrupts. You will need stronger synchronisation primitives if you\\nare using multiple cores.","breadcrumbs":"Concurrency » Critical Sections","id":"123","title":"Critical Sections"},"124":{"body":"On some platforms, special atomic instructions are available, which provide\\nguarantees about read-modify-write operations. Specifically for Cortex-M: thumbv6\\n(Cortex-M0, Cortex-M0+) only provide atomic load and store instructions,\\nwhile thumbv7 (Cortex-M3 and above) provide full Compare and Swap (CAS)\\ninstructions. These CAS instructions give an alternative to the heavy-handed\\ndisabling of all interrupts: we can attempt the increment, it will succeed most\\nof the time, but if it was interrupted it will automatically retry the entire\\nincrement operation. These atomic operations are safe even across multiple\\ncores. use core::sync::atomic::{AtomicUsize, Ordering}; static COUNTER: AtomicUsize = AtomicUsize::new(0); #[entry]\\nfn main() -> ! { set_timer_1hz(); let mut last_state = false; loop { let state = read_signal_level(); if state && !last_state { // Use `fetch_add` to atomically add 1 to COUNTER COUNTER.fetch_add(1, Ordering::Relaxed); } last_state = state; }\\n} #[interrupt]\\nfn timer() { // Use `store` to write 0 directly to COUNTER COUNTER.store(0, Ordering::Relaxed)\\n} This time COUNTER is a safe static variable. Thanks to the AtomicUsize\\ntype COUNTER can be safely modified from both the interrupt handler and the\\nmain thread without disabling interrupts. When possible, this is a better\\nsolution — but it may not be supported on your platform. A note on Ordering: this affects how the compiler and hardware may reorder\\ninstructions, and also has consequences on cache visibility. Assuming that the\\ntarget is a single core platform Relaxed is sufficient and the most efficient\\nchoice in this particular case. Stricter ordering will cause the compiler to\\nemit memory barriers around the atomic operations; depending on what you’re\\nusing atomics for you may or may not need this! The precise details of the\\natomic model are complicated and best described elsewhere. For more details on atomics and ordering, see the nomicon.","breadcrumbs":"Concurrency » Atomic Access","id":"124","title":"Atomic Access"},"125":{"body":"None of the above solutions are especially satisfactory. They require unsafe\\nblocks which must be very carefully checked and are not ergonomic. Surely we\\ncan do better in Rust! We can abstract our counter into a safe interface which can be safely used\\nanywhere else in our code. For this example, we’ll use the critical-section\\ncounter, but you could do something very similar with atomics. use core::cell::UnsafeCell;\\nuse cortex_m::interrupt; // Our counter is just a wrapper around UnsafeCell, which is the heart\\n// of interior mutability in Rust. By using interior mutability, we can have\\n// COUNTER be `static` instead of `static mut`, but still able to mutate\\n// its counter value.\\nstruct CSCounter(UnsafeCell); const CS_COUNTER_INIT: CSCounter = CSCounter(UnsafeCell::new(0)); impl CSCounter { pub fn reset(&self, _cs: &interrupt::CriticalSection) { // By requiring a CriticalSection be passed in, we know we must // be operating inside a CriticalSection, and so can confidently // use this unsafe block (required to call UnsafeCell::get). unsafe { *self.0.get() = 0 }; } pub fn increment(&self, _cs: &interrupt::CriticalSection) { unsafe { *self.0.get() += 1 }; }\\n} // Required to allow static CSCounter. See explanation below.\\nunsafe impl Sync for CSCounter {} // COUNTER is no longer `mut` as it uses interior mutability;\\n// therefore it also no longer requires unsafe blocks to access.\\nstatic COUNTER: CSCounter = CS_COUNTER_INIT; #[entry]\\nfn main() -> ! { set_timer_1hz(); let mut last_state = false; loop { let state = read_signal_level(); if state && !last_state { // No unsafe here! interrupt::free(|cs| COUNTER.increment(cs)); } last_state = state; }\\n} #[interrupt]\\nfn timer() { // We do need to enter a critical section here just to obtain a valid // cs token, even though we know no other interrupt could pre-empt // this one. interrupt::free(|cs| COUNTER.reset(cs)); // We could use unsafe code to generate a fake CriticalSection if we // really wanted to, avoiding the overhead: // let cs = unsafe { interrupt::CriticalSection::new() };\\n} We’ve moved our unsafe code to inside our carefully-planned abstraction,\\nand now our application code does not contain any unsafe blocks. This design requires that the application pass a CriticalSection token in:\\nthese tokens are only safely generated by interrupt::free, so by requiring\\none be passed in, we ensure we are operating inside a critical section, without\\nhaving to actually do the lock ourselves. This guarantee is provided statically\\nby the compiler: there won’t be any runtime overhead associated with cs.\\nIf we had multiple counters, they could all be given the same cs, without\\nrequiring multiple nested critical sections. This also brings up an important topic for concurrency in Rust: the Send and Sync traits. To summarise the Rust book, a type is Send\\nwhen it can safely be moved to another thread, while it is Sync when\\nit can be safely shared between multiple threads. In an embedded context,\\nwe consider interrupts to be executing in a separate thread to the application\\ncode, so variables accessed by both an interrupt and the main code must be\\nSync. For most types in Rust, both of these traits are automatically derived for you\\nby the compiler. However, because CSCounter contains an UnsafeCell, it is\\nnot Sync, and therefore we could not make a static CSCounter: static\\nvariables must be Sync, since they can be accessed by multiple threads. To tell the compiler we have taken care that the CSCounter is in fact safe\\nto share between threads, we implement the Sync trait explicitly. As with the\\nprevious use of critical sections, this is only safe on single-core platforms:\\nwith multiple cores, you would need to go to greater lengths to ensure safety.","breadcrumbs":"Concurrency » Abstractions, Send, and Sync","id":"125","title":"Abstractions, Send, and Sync"},"126":{"body":"We’ve created a useful abstraction specific to our counter problem, but\\nthere are many common abstractions used for concurrency. One such synchronisation primitive is a mutex, short for mutual exclusion.\\nThese constructs ensure exclusive access to a variable, such as our counter. A\\nthread can attempt to lock (or acquire) the mutex, and either succeeds\\nimmediately, or blocks waiting for the lock to be acquired, or returns an error\\nthat the mutex could not be locked. While that thread holds the lock, it is\\ngranted access to the protected data. When the thread is done, it unlocks (or releases) the mutex, allowing another thread to lock it. In Rust, we would\\nusually implement the unlock using the Drop trait to ensure it is always\\nreleased when the mutex goes out of scope. Using a mutex with interrupt handlers can be tricky: it is not normally\\nacceptable for the interrupt handler to block, and it would be especially\\ndisastrous for it to block waiting for the main thread to release a lock,\\nsince we would then deadlock (the main thread will never release the lock\\nbecause execution stays in the interrupt handler). Deadlocking is not\\nconsidered unsafe: it is possible even in safe Rust. To avoid this behaviour entirely, we could implement a mutex which requires\\na critical section to lock, just like our counter example. So long as the\\ncritical section must last as long as the lock, we can be sure we have\\nexclusive access to the wrapped variable without even needing to track\\nthe lock/unlock state of the mutex. This is in fact done for us in the cortex_m crate! We could have written\\nour counter using it: use core::cell::Cell;\\nuse cortex_m::interrupt::Mutex; static COUNTER: Mutex> = Mutex::new(Cell::new(0)); #[entry]\\nfn main() -> ! { set_timer_1hz(); let mut last_state = false; loop { let state = read_signal_level(); if state && !last_state { interrupt::free(|cs| COUNTER.borrow(cs).set(COUNTER.borrow(cs).get() + 1)); } last_state = state; }\\n} #[interrupt]\\nfn timer() { // We still need to enter a critical section here to satisfy the Mutex. interrupt::free(|cs| COUNTER.borrow(cs).set(0));\\n} We’re now using Cell, which along with its sibling RefCell is used to\\nprovide safe interior mutability. We’ve already seen UnsafeCell which is\\nthe bottom layer of interior mutability in Rust: it allows you to obtain\\nmultiple mutable references to its value, but only with unsafe code. A Cell\\nis like an UnsafeCell but it provides a safe interface: it only permits\\ntaking a copy of the current value or replacing it, not taking a reference,\\nand since it is not Sync, it cannot be shared between threads. These\\nconstraints mean it’s safe to use, but we couldn’t use it directly in a static variable as a static must be Sync. So why does the example above work? The Mutex implements Sync for any T which is Send — such as a Cell. It can do this safely because it only\\ngives access to its contents during a critical section. We’re therefore able\\nto get a safe counter with no unsafe code at all! This is great for simple types like the u32 of our counter, but what about\\nmore complex types which are not Copy? An extremely common example in an\\nembedded context is a peripheral struct, which generally is not Copy.\\nFor that, we can turn to RefCell.","breadcrumbs":"Concurrency » Mutexes","id":"126","title":"Mutexes"},"127":{"body":"Device crates generated using svd2rust and similar abstractions provide\\nsafe access to peripherals by enforcing that only one instance of the\\nperipheral struct can exist at a time. This ensures safety, but makes it\\ndifficult to access a peripheral from both the main thread and an interrupt\\nhandler. To safely share peripheral access, we can use the Mutex we saw before. We’ll\\nalso need to use RefCell, which uses a runtime check to ensure only one\\nreference to a peripheral is given out at a time. This has more overhead than\\nthe plain Cell, but since we are giving out references rather than copies,\\nwe must be sure only one exists at a time. Finally, we’ll also have to account for somehow moving the peripheral into\\nthe shared variable after it has been initialised in the main code. To do\\nthis we can use the Option type, initialised to None and later set to\\nthe instance of the peripheral. use core::cell::RefCell;\\nuse cortex_m::interrupt::{self, Mutex};\\nuse stm32f4::stm32f405; static MY_GPIO: Mutex>> = Mutex::new(RefCell::new(None)); #[entry]\\nfn main() -> ! { // Obtain the peripheral singletons and configure it. // This example is from an svd2rust-generated crate, but // most embedded device crates will be similar. let dp = stm32f405::Peripherals::take().unwrap(); let gpioa = &dp.GPIOA; // Some sort of configuration function. // Assume it sets PA0 to an input and PA1 to an output. configure_gpio(gpioa); // Store the GPIOA in the mutex, moving it. interrupt::free(|cs| MY_GPIO.borrow(cs).replace(Some(dp.GPIOA))); // We can no longer use `gpioa` or `dp.GPIOA`, and instead have to // access it via the mutex. // Be careful to enable the interrupt only after setting MY_GPIO: // otherwise the interrupt might fire while it still contains None, // and as-written (with `unwrap()`), it would panic. set_timer_1hz(); let mut last_state = false; loop { // We\'ll now read state as a digital input, via the mutex let state = interrupt::free(|cs| { let gpioa = MY_GPIO.borrow(cs).borrow(); gpioa.as_ref().unwrap().idr.read().idr0().bit_is_set() }); if state && !last_state { // Set PA1 high if we\'ve seen a rising edge on PA0. interrupt::free(|cs| { let gpioa = MY_GPIO.borrow(cs).borrow(); gpioa.as_ref().unwrap().odr.modify(|_, w| w.odr1().set_bit()); }); } last_state = state; }\\n} #[interrupt]\\nfn timer() { // This time in the interrupt we\'ll just clear PA0. interrupt::free(|cs| { // We can use `unwrap()` because we know the interrupt wasn\'t enabled // until after MY_GPIO was set; otherwise we should handle the potential // for a None value. let gpioa = MY_GPIO.borrow(cs).borrow(); gpioa.as_ref().unwrap().odr.modify(|_, w| w.odr1().clear_bit()); });\\n} That’s quite a lot to take in, so let’s break down the important lines. static MY_GPIO: Mutex>> = Mutex::new(RefCell::new(None)); Our shared variable is now a Mutex around a RefCell which contains an Option. The Mutex ensures we only have access during a critical section,\\nand therefore makes the variable Sync, even though a plain RefCell would not\\nbe Sync. The RefCell gives us interior mutability with references, which\\nwe’ll need to use our GPIOA. The Option lets us initialise this variable\\nto something empty, and only later actually move the variable in. We cannot\\naccess the peripheral singleton statically, only at runtime, so this is\\nrequired. interrupt::free(|cs| MY_GPIO.borrow(cs).replace(Some(dp.GPIOA))); Inside a critical section we can call borrow() on the mutex, which gives us\\na reference to the RefCell. We then call replace() to move our new value\\ninto the RefCell. interrupt::free(|cs| { let gpioa = MY_GPIO.borrow(cs).borrow(); gpioa.as_ref().unwrap().odr.modify(|_, w| w.odr1().set_bit());\\n}); Finally, we use MY_GPIO in a safe and concurrent fashion. The critical section\\nprevents the interrupt firing as usual, and lets us borrow the mutex. The RefCell then gives us an &Option, and tracks how long it remains\\nborrowed - once that reference goes out of scope, the RefCell will be updated\\nto indicate it is no longer borrowed. Since we can’t move the GPIOA out of the &Option, we need to convert it to\\nan &Option<&GPIOA> with as_ref(), which we can finally unwrap() to obtain\\nthe &GPIOA which lets us modify the peripheral. If we need a mutable reference to a shared resource, then borrow_mut and deref_mut\\nshould be used instead. The following code shows an example using the TIM2 timer. use core::cell::RefCell;\\nuse core::ops::DerefMut;\\nuse cortex_m::interrupt::{self, Mutex};\\nuse cortex_m::asm::wfi;\\nuse stm32f4::stm32f405; static G_TIM: Mutex>>> = Mutex::new(RefCell::new(None)); #[entry]\\nfn main() -> ! { let mut cp = cm::Peripherals::take().unwrap(); let dp = stm32f405::Peripherals::take().unwrap(); // Some sort of timer configuration function. // Assume it configures the TIM2 timer, its NVIC interrupt, // and finally starts the timer. let tim = configure_timer_interrupt(&mut cp, dp); interrupt::free(|cs| { G_TIM.borrow(cs).replace(Some(tim)); }); loop { wfi(); }\\n} #[interrupt]\\nfn timer() { interrupt::free(|cs| { if let Some(ref mut tim)) = G_TIM.borrow(cs).borrow_mut().deref_mut() { tim.start(1.hz()); } });\\n} Whew! This is safe, but it is also a little unwieldy. Is there anything else\\nwe can do?","breadcrumbs":"Concurrency » Sharing Peripherals","id":"127","title":"Sharing Peripherals"},"128":{"body":"One alternative is the RTIC framework, short for Real Time Interrupt-driven Concurrency. It\\nenforces static priorities and tracks accesses to static mut variables\\n(“resources”) to statically ensure that shared resources are always accessed\\nsafely, without requiring the overhead of always entering critical sections and\\nusing reference counting (as in RefCell). This has a number of advantages such\\nas guaranteeing no deadlocks and giving extremely low time and memory overhead. RTIC comes with as asynchronous executor, so your software tasks are async functions where\\nyou can use async APIs in addition to regular synchronous APIs. The framework also includes other features like message passing, which reduces\\nthe need for explicit shared state, and the ability to schedule tasks to run at\\na given time, which can be used to implement periodic tasks.\\nCheck out the documentation for more information!","breadcrumbs":"Concurrency » RTIC","id":"128","title":"RTIC"},"129":{"body":"Embassy is an ecosystem of libraries which focus on using the async / await syntax\\nincluded in Rust for concurrency. The core of embassy is its asynchronous executor\\nwhich supports most common MCU architectures. embassy also takes a battery-included approach and offers many other components, for example: Time library Various HAL libraries which also provide the time library support. embassy-sync for synchronization primitives You can check the website and the book for more\\ninformation.","breadcrumbs":"Concurrency » Embassy","id":"129","title":"Embassy"},"13":{"body":"These kinds of environments are close to a normal PC environment.\\nWhat this means is that you are provided with a System Interface E.G. POSIX\\nthat provides you with primitives to interact with various systems, such as file systems, networking, memory management, threads, etc.\\nStandard libraries in turn usually depend on these primitives to implement their functionality.\\nYou may also have some sort of sysroot and restrictions on RAM/ROM-usage, and perhaps some\\nspecial HW or I/Os. Overall it feels like coding on a special-purpose PC environment.","breadcrumbs":"Introduction » no_std » Hosted Environments","id":"13","title":"Hosted Environments"},"130":{"body":"Another common model for embedded concurrency is the real-time operating system\\n(RTOS). While currently less well explored in Rust, they are widely used in\\ntraditional embedded development. Open source examples include FreeRTOS and ChibiOS. These RTOSs provide support for running multiple application threads\\nwhich the CPU swaps between, either when the threads yield control (called\\ncooperative multitasking) or based on a regular timer or interrupts (preemptive\\nmultitasking). The RTOS typically provide mutexes and other synchronisation\\nprimitives, and often interoperate with hardware features such as DMA engines. At the time of writing, there are not many Rust RTOS examples to point to,\\nbut it’s an interesting area so watch this space!","breadcrumbs":"Concurrency » Real Time Operating Systems","id":"130","title":"Real Time Operating Systems"},"131":{"body":"It is becoming more common to have two or more cores in embedded processors,\\nwhich adds an extra layer of complexity to concurrency. All the examples using\\na critical section (including the cortex_m::interrupt::Mutex) assume the only\\nother execution thread is the interrupt thread, but on a multi-core system\\nthat’s no longer true. Instead, we’ll need synchronisation primitives designed\\nfor multiple cores (also called SMP, for symmetric multi-processing). These typically use the atomic instructions we saw earlier, since the\\nprocessing system will ensure that atomicity is maintained over all cores. Covering these topics in detail is currently beyond the scope of this book,\\nbut the general patterns are the same as for the single-core case.","breadcrumbs":"Concurrency » Multiple Cores","id":"131","title":"Multiple Cores"},"132":{"body":"Eventually you’ll want to use dynamic data structures (AKA collections) in your\\nprogram. std provides a set of common collections: Vec, String, HashMap, etc. All the collections implemented in std use a global dynamic\\nmemory allocator (AKA the heap). As core is, by definition, free of memory allocations these implementations\\nare not available there, but they can be found in the alloc crate\\nthat’s shipped with the compiler. If you need collections, a heap allocated implementation is not your only\\noption. You can also use fixed capacity collections; one such implementation\\ncan be found in the heapless crate. In this section, we’ll explore and compare these two implementations.","breadcrumbs":"Collections » Collections","id":"132","title":"Collections"},"133":{"body":"The alloc crate is shipped with the standard Rust distribution. To import the\\ncrate you can directly use it without declaring it as a dependency in your Cargo.toml file. #![feature(alloc)] extern crate alloc; use alloc::vec::Vec; To be able to use any collection you’ll first need use the global_allocator\\nattribute to declare the global allocator your program will use. It’s required\\nthat the allocator you select implements the GlobalAlloc trait. For completeness and to keep this section as self-contained as possible we’ll\\nimplement a simple bump pointer allocator and use that as the global allocator.\\nHowever, we strongly suggest you use a battle tested allocator from crates.io\\nin your program instead of this allocator. // Bump pointer allocator implementation use core::alloc::{GlobalAlloc, Layout};\\nuse core::cell::UnsafeCell;\\nuse core::ptr; use cortex_m::interrupt; // Bump pointer allocator for *single* core systems\\nstruct BumpPointerAlloc { head: UnsafeCell, end: usize,\\n} unsafe impl Sync for BumpPointerAlloc {} unsafe impl GlobalAlloc for BumpPointerAlloc { unsafe fn alloc(&self, layout: Layout) -> *mut u8 { // `interrupt::free` is a critical section that makes our allocator safe // to use from within interrupts interrupt::free(|_| { let head = self.head.get(); let size = layout.size(); let align = layout.align(); let align_mask = !(align - 1); // move start up to the next alignment boundary let start = (*head + align - 1) & align_mask; if start + size > self.end { // a null pointer signal an Out Of Memory condition ptr::null_mut() } else { *head = start + size; start as *mut u8 } }) } unsafe fn dealloc(&self, _: *mut u8, _: Layout) { // this allocator never deallocates memory }\\n} // Declaration of the global memory allocator\\n// NOTE the user must ensure that the memory region `[0x2000_0100, 0x2000_0200]`\\n// is not used by other parts of the program\\n#[global_allocator]\\nstatic HEAP: BumpPointerAlloc = BumpPointerAlloc { head: UnsafeCell::new(0x2000_0100), end: 0x2000_0200,\\n}; Apart from selecting a global allocator the user will also have to define how\\nOut Of Memory (OOM) errors are handled using the unstable alloc_error_handler attribute. #![feature(alloc_error_handler)] use cortex_m::asm; #[alloc_error_handler]\\nfn on_oom(_layout: Layout) -> ! { asm::bkpt(); loop {}\\n} Once all that is in place, the user can finally use the collections in alloc. #[entry]\\nfn main() -> ! { let mut xs = Vec::new(); xs.push(42); assert!(xs.pop(), Some(42)); loop { // .. }\\n} If you have used the collections in the std crate then these will be familiar\\nas they are exact same implementation.","breadcrumbs":"Collections » Using alloc","id":"133","title":"Using alloc"},"134":{"body":"heapless requires no setup as its collections don’t depend on a global memory\\nallocator. Just use its collections and proceed to instantiate them: // heapless version: v0.4.x\\nuse heapless::Vec;\\nuse heapless::consts::*; #[entry]\\nfn main() -> ! { let mut xs: Vec<_, U8> = Vec::new(); xs.push(42).unwrap(); assert_eq!(xs.pop(), Some(42)); loop {}\\n} You’ll note two differences between these collections and the ones in alloc. First, you have to declare upfront the capacity of the collection. heapless\\ncollections never reallocate and have fixed capacities; this capacity is part of\\nthe type signature of the collection. In this case we have declared that xs\\nhas a capacity of 8 elements that is the vector can, at most, hold 8 elements.\\nThis is indicated by the U8 (see typenum) in the type signature. Second, the push method, and many other methods, return a Result. Since the heapless collections have fixed capacity all operations that insert elements\\ninto the collection can potentially fail. The API reflects this problem by\\nreturning a Result indicating whether the operation succeeded or not. In\\ncontrast, alloc collections will reallocate themselves on the heap to increase\\ntheir capacity. As of version v0.4.x all heapless collections store all their elements inline.\\nThis means that an operation like let x = heapless::Vec::new(); will allocate\\nthe collection on the stack, but it’s also possible to allocate the collection\\non a static variable, or even on the heap ( Box>).","breadcrumbs":"Collections » Using heapless","id":"134","title":"Using heapless"},"135":{"body":"Keep these in mind when choosing between heap allocated, relocatable collections\\nand fixed capacity collections.","breadcrumbs":"Collections » Trade-offs","id":"135","title":"Trade-offs"},"136":{"body":"With heap allocations Out Of Memory is always a possibility and can occur in\\nany place where a collection may need to grow: for example, all alloc::Vec.push invocations can potentially generate an OOM condition. Thus\\nsome operations can implicitly fail. Some alloc collections expose try_reserve methods that let you check for potential OOM conditions when\\ngrowing the collection but you need be proactive about using them. If you exclusively use heapless collections and you don’t use a memory\\nallocator for anything else then an OOM condition is impossible. Instead, you’ll\\nhave to deal with collections running out of capacity on a case by case basis.\\nThat is you’ll have deal with all the Results returned by methods like Vec.push. OOM failures can be harder to debug than say unwrap-ing on all Results\\nreturned by heapless::Vec.push because the observed location of failure may not match with the location of the cause of the problem. For example, even vec.reserve(1) can trigger an OOM if the allocator is nearly exhausted because\\nsome other collection was leaking memory (memory leaks are possible in safe\\nRust).","breadcrumbs":"Collections » Out Of Memory and error handling","id":"136","title":"Out Of Memory and error handling"},"137":{"body":"Reasoning about memory usage of heap allocated collections is hard because the\\ncapacity of long lived collections can change at runtime. Some operations may\\nimplicitly reallocate the collection increasing its memory usage, and some\\ncollections expose methods like shrink_to_fit that can potentially reduce the\\nmemory used by the collection – ultimately, it’s up to the allocator to decide\\nwhether to actually shrink the memory allocation or not. Additionally, the\\nallocator may have to deal with memory fragmentation which can increase the apparent memory usage. On the other hand if you exclusively use fixed capacity collections, store\\nmost of them in static variables and set a maximum size for the call stack\\nthen the linker will detect if you try to use more memory than what’s physically\\navailable. Furthermore, fixed capacity collections allocated on the stack will be reported\\nby -Z emit-stack-sizes flag which means that tools that analyze stack usage\\n(like stack-sizes) will include them in their analysis. However, fixed capacity collections can not be shrunk which can result in\\nlower load factors (the ratio between the size of the collection and its\\ncapacity) than what relocatable collections can achieve.","breadcrumbs":"Collections » Memory usage","id":"137","title":"Memory usage"},"138":{"body":"If you are building time sensitive applications or hard real time applications\\nthen you care, maybe a lot, about the worst case execution time of the different\\nparts of your program. The alloc collections can reallocate so the WCET of operations that may grow\\nthe collection will also include the time it takes to reallocate the collection,\\nwhich itself depends on the runtime capacity of the collection. This makes it\\nhard to determine the WCET of, for example, the alloc::Vec.push operation as\\nit depends on both the allocator being used and its runtime capacity. On the other hand fixed capacity collections never reallocate so all operations\\nhave a predictable execution time. For example, heapless::Vec.push executes in\\nconstant time.","breadcrumbs":"Collections » Worst Case Execution Time (WCET)","id":"138","title":"Worst Case Execution Time (WCET)"},"139":{"body":"alloc requires setting up a global allocator whereas heapless does not.\\nHowever, heapless requires you to pick the capacity of each collection that\\nyou instantiate. The alloc API will be familiar to virtually every Rust developer. The heapless API tries to closely mimic the alloc API but it will never be\\nexactly the same due to its explicit error handling – some developers may feel\\nthe explicit error handling is excessive or too cumbersome.","breadcrumbs":"Collections » Ease of use","id":"139","title":"Ease of use"},"14":{"body":"In a bare metal environment no code has been loaded before your program.\\nWithout the software provided by an OS we can not load the standard library.\\nInstead the program, along with the crates it uses, can only use the hardware (bare metal) to run.\\nTo prevent rust from loading the standard library use no_std.\\nThe platform-agnostic parts of the standard library are available through libcore.\\nlibcore also excludes things which are not always desirable in an embedded environment.\\nOne of these things is a memory allocator for dynamic memory allocation.\\nIf you require this or any other functionalities there are often crates which provide these.","breadcrumbs":"Introduction » no_std » Bare Metal Environments","id":"14","title":"Bare Metal Environments"},"140":{"body":"This chapter aims to collect various useful design patterns for embedded Rust.","breadcrumbs":"Design Patterns » Design Patterns","id":"140","title":"Design Patterns"},"141":{"body":"This is a set of common and recommended patterns for writing hardware\\nabstraction layers (HALs) for microcontrollers in Rust. These patterns are\\nintended to be used in addition to the existing Rust API Guidelines when\\nwriting HALs for microcontrollers. Checklist Naming Interoperability Predictability GPIO","breadcrumbs":"Design Patterns » HALs » HAL Design Patterns","id":"141","title":"HAL Design Patterns"},"142":{"body":"Naming (crate aligns with Rust naming conventions) The crate is named appropriately ( C-CRATE-NAME) Interoperability (crate interacts nicely with other library functionality) Wrapper types provide a destructor method ( C-FREE) HALs reexport their register access crate ( C-REEXPORT-PAC) Types implement the embedded-hal traits ( C-HAL-TRAITS) Predictability (crate enables legible code that acts how it looks) Constructors are used instead of extension traits ( C-CTOR) GPIO Interfaces (GPIO Interfaces follow a common pattern) Pin types are zero-sized by default ( C-ZST-PIN) Pin types provide methods to erase pin and port ( C-ERASED-PIN) Pin state should be encoded as type parameters ( C-PIN-STATE)","breadcrumbs":"Design Patterns » HALs » Checklist » HAL Design Patterns Checklist","id":"142","title":"HAL Design Patterns Checklist"},"143":{"body":"","breadcrumbs":"Design Patterns » HALs » Naming » Naming","id":"143","title":"Naming"},"144":{"body":"HAL crates should be named after the chip or family of chips they aim to\\nsupport. Their name should end with -hal to distinguish them from register\\naccess crates. The name should not contain underscores (use dashes instead).","breadcrumbs":"Design Patterns » HALs » Naming » The crate is named appropriately (C-CRATE-NAME)","id":"144","title":"The crate is named appropriately (C-CRATE-NAME)"},"145":{"body":"","breadcrumbs":"Design Patterns » HALs » Interoperability » Interoperability","id":"145","title":"Interoperability"},"146":{"body":"Any non- Copy wrapper type provided by the HAL should provide a free method\\nthat consumes the wrapper and returns back the raw peripheral (and possibly\\nother objects) it was created from. The method should shut down and reset the peripheral if necessary. Calling new\\nwith the raw peripheral returned by free should not fail due to an unexpected\\nstate of the peripheral. If the HAL type requires other non- Copy objects to be constructed (for example\\nI/O pins), any such object should be released and returned by free as well. free should return a tuple in that case. For example: #![allow(unused)] fn main() { pub struct TIMER0;\\npub struct Timer(TIMER0); impl Timer { pub fn new(periph: TIMER0) -> Self { Self(periph) } pub fn free(self) -> TIMER0 { self.0 }\\n} }","breadcrumbs":"Design Patterns » HALs » Interoperability » Wrapper types provide a destructor method (C-FREE)","id":"146","title":"Wrapper types provide a destructor method (C-FREE)"},"147":{"body":"HALs can be written on top of svd2rust-generated PACs, or on top of other\\ncrates that provide raw register access. HALs should always reexport the\\nregister access crate they are based on in their crate root. A PAC should be reexported under the name pac, regardless of the actual name\\nof the crate, as the name of the HAL should already make it clear what PAC is\\nbeing accessed.","breadcrumbs":"Design Patterns » HALs » Interoperability » HALs reexport their register access crate (C-REEXPORT-PAC)","id":"147","title":"HALs reexport their register access crate (C-REEXPORT-PAC)"},"148":{"body":"Types provided by the HAL should implement all applicable traits provided by the embedded-hal crate. Multiple traits may be implemented for the same type.","breadcrumbs":"Design Patterns » HALs » Interoperability » Types implement the embedded-hal traits (C-HAL-TRAITS)","id":"148","title":"Types implement the embedded-hal traits (C-HAL-TRAITS)"},"149":{"body":"","breadcrumbs":"Design Patterns » HALs » Predictability » Predictability","id":"149","title":"Predictability"},"15":{"body":"As mentioned before using libstd requires some sort of system integration, but this is not only because libstd is just providing a common way of accessing OS abstractions, it also provides a runtime.\\nThis runtime, among other things, takes care of setting up stack overflow protection, processing command line arguments,\\nand spawning the main thread before a program’s main function is invoked. This runtime also won’t be available in a no_std environment.","breadcrumbs":"Introduction » no_std » The libstd Runtime","id":"15","title":"The libstd Runtime"},"150":{"body":"All peripherals to which the HAL adds functionality should be wrapped in a new\\ntype, even if no additional fields are required for that functionality. Extension traits implemented for the raw peripheral should be avoided.","breadcrumbs":"Design Patterns » HALs » Predictability » Constructors are used instead of extension traits (C-CTOR)","id":"150","title":"Constructors are used instead of extension traits (C-CTOR)"},"151":{"body":"The Rust compiler does not by default perform full inlining across crate\\nboundaries. As embedded applications are sensitive to unexpected code size\\nincreases, #[inline] should be used to guide the compiler as follows: All “small” functions should be marked #[inline]. What qualifies as “small”\\nis subjective, but generally all functions that are expected to compile down\\nto single-digit instruction sequences qualify as small. Functions that are very likely to take constant values as parameters should be\\nmarked as #[inline]. This enables the compiler to compute even complicated\\ninitialization logic at compile time, provided the function inputs are known.","breadcrumbs":"Design Patterns » HALs » Predictability » Methods are decorated with #[inline] where appropriate (C-INLINE)","id":"151","title":"Methods are decorated with #[inline] where appropriate (C-INLINE)"},"152":{"body":"","breadcrumbs":"Design Patterns » HALs » GPIO » Recommendations for GPIO Interfaces","id":"152","title":"Recommendations for GPIO Interfaces"},"153":{"body":"GPIO Interfaces exposed by the HAL should provide dedicated zero-sized types for\\neach pin on every interface or port, resulting in a zero-cost GPIO abstraction\\nwhen all pin assignments are statically known. Each GPIO Interface or Port should implement a split method returning a\\nstruct with every pin. Example: #![allow(unused)] fn main() {\\npub struct PA0;\\npub struct PA1;\\n// ... pub struct PortA; impl PortA { pub fn split(self) -> PortAPins { PortAPins { pa0: PA0, pa1: PA1, // ... } }\\n} pub struct PortAPins { pub pa0: PA0, pub pa1: PA1, // ...\\n} }","breadcrumbs":"Design Patterns » HALs » GPIO » Pin types are zero-sized by default (C-ZST-PIN)","id":"153","title":"Pin types are zero-sized by default (C-ZST-PIN)"},"154":{"body":"Pins should provide type erasure methods that move their properties from\\ncompile time to runtime, and allow more flexibility in applications. Example: #![allow(unused)] fn main() {\\n/// Port A, pin 0.\\npub struct PA0; impl PA0 { pub fn erase_pin(self) -> PA { PA { pin: 0 } }\\n} /// A pin on port A.\\npub struct PA { /// The pin number. pin: u8,\\n} impl PA { pub fn erase_port(self) -> Pin { Pin { port: Port::A, pin: self.pin, } }\\n} pub struct Pin { port: Port, pin: u8, // (these fields can be packed to reduce the memory footprint)\\n} enum Port { A, B, C, D,\\n} }","breadcrumbs":"Design Patterns » HALs » GPIO » Pin types provide methods to erase pin and port (C-ERASED-PIN)","id":"154","title":"Pin types provide methods to erase pin and port (C-ERASED-PIN)"},"155":{"body":"Pins may be configured as input or output with different characteristics\\ndepending on the chip or family. This state should be encoded in the type system\\nto prevent use of pins in incorrect states. Additional, chip-specific state (eg. drive strength) may also be encoded in this\\nway, using additional type parameters. Methods for changing the pin state should be provided as into_input and into_output methods. Additionally, with_{input,output}_state methods should be provided that\\ntemporarily reconfigure a pin in a different state without moving it. The following methods should be provided for every pin type (that is, both\\nerased and non-erased pin types should provide the same API): pub fn into_input(self, input: N) -> Pin pub fn into_output(self, output: N) -> Pin pub fn with_input_state( &mut self, input: N, f: impl FnOnce(&mut PA1) -> R,\\n) -> R pub fn with_output_state( &mut self, output: N, f: impl FnOnce(&mut PA1) -> R,\\n) -> R Pin state should be bounded by sealed traits. Users of the HAL should have no\\nneed to add their own state. The traits can provide HAL-specific methods\\nrequired to implement the pin state API. Example: #![allow(unused)] fn main() { use std::marker::PhantomData;\\nmod sealed { pub trait Sealed {}\\n} pub trait PinState: sealed::Sealed {}\\npub trait OutputState: sealed::Sealed {}\\npub trait InputState: sealed::Sealed { // ...\\n} pub struct Output { _p: PhantomData,\\n} impl PinState for Output {}\\nimpl sealed::Sealed for Output {} pub struct PushPull;\\npub struct OpenDrain; impl OutputState for PushPull {}\\nimpl OutputState for OpenDrain {}\\nimpl sealed::Sealed for PushPull {}\\nimpl sealed::Sealed for OpenDrain {} pub struct Input { _p: PhantomData,\\n} impl PinState for Input {}\\nimpl sealed::Sealed for Input {} pub struct Floating;\\npub struct PullUp;\\npub struct PullDown; impl InputState for Floating {}\\nimpl InputState for PullUp {}\\nimpl InputState for PullDown {}\\nimpl sealed::Sealed for Floating {}\\nimpl sealed::Sealed for PullUp {}\\nimpl sealed::Sealed for PullDown {} pub struct PA1 { _p: PhantomData,\\n} impl PA1 { pub fn into_input(self, input: N) -> PA1> { todo!() } pub fn into_output(self, output: N) -> PA1> { todo!() } pub fn with_input_state( &mut self, input: N, f: impl FnOnce(&mut PA1) -> R, ) -> R { todo!() } pub fn with_output_state( &mut self, output: N, f: impl FnOnce(&mut PA1) -> R, ) -> R { todo!() }\\n} // Same for `PA` and `Pin`, and other pin types. }","breadcrumbs":"Design Patterns » HALs » GPIO » Pin state should be encoded as type parameters (C-PIN-STATE)","id":"155","title":"Pin state should be encoded as type parameters (C-PIN-STATE)"},"156":{"body":"This chapter collects a variety of tips that might be useful to experienced\\nembedded C developers looking to start writing Rust. It will especially\\nhighlight how things you might already be used to in C are different in Rust.","breadcrumbs":"Tips for embedded C developers » Tips for embedded C developers","id":"156","title":"Tips for embedded C developers"},"157":{"body":"In embedded C it is very common to use the preprocessor for a variety of\\npurposes, such as: Compile-time selection of code blocks with #ifdef Compile-time array sizes and computations Macros to simplify common patterns (to avoid function call overhead) In Rust there is no preprocessor, and so many of these use cases are addressed\\ndifferently. In the rest of this section we cover various alternatives to\\nusing the preprocessor.","breadcrumbs":"Tips for embedded C developers » Preprocessor","id":"157","title":"Preprocessor"},"158":{"body":"The closest match to #ifdef ... #endif in Rust are Cargo features. These\\nare a little more formal than the C preprocessor: all possible features are\\nexplicitly listed per crate, and can only be either on or off. Features are\\nturned on when you list a crate as a dependency, and are additive: if any crate\\nin your dependency tree enables a feature for another crate, that feature will\\nbe enabled for all users of that crate. For example, you might have a crate which provides a library of signal\\nprocessing primitives. Each one might take some extra time to compile or\\ndeclare some large table of constants which you’d like to avoid. You could\\ndeclare a Cargo feature for each component in your Cargo.toml: [features]\\nFIR = []\\nIIR = [] Then, in your code, use #[cfg(feature=\\"FIR\\")] to control what is included. #![allow(unused)] fn main() {\\n/// In your top-level lib.rs #[cfg(feature=\\"FIR\\")]\\npub mod fir; #[cfg(feature=\\"IIR\\")]\\npub mod iir; } You can similarly include code blocks only if a feature is not enabled, or if\\nany combination of features are or are not enabled. Additionally, Rust provides a number of automatically-set conditions you can\\nuse, such as target_arch to select different code based on architecture. For\\nfull details of the conditional compilation support, refer to the conditional compilation chapter of the Rust reference. The conditional compilation will only apply to the next statement or block. If\\na block can not be used in the current scope then the cfg attribute will\\nneed to be used multiple times. It’s worth noting that most of the time it is\\nbetter to simply include all the code and allow the compiler to remove dead\\ncode when optimising: it’s simpler for you and your users, and in general the\\ncompiler will do a good job of removing unused code.","breadcrumbs":"Tips for embedded C developers » Compile-Time Code Selection","id":"158","title":"Compile-Time Code Selection"},"159":{"body":"Rust supports const fn, functions which are guaranteed to be evaluable at\\ncompile-time and can therefore be used where constants are required, such as\\nin the size of arrays. This can be used alongside features mentioned above,\\nfor example: #![allow(unused)] fn main() {\\nconst fn array_size() -> usize { #[cfg(feature=\\"use_more_ram\\")] { 1024 } #[cfg(not(feature=\\"use_more_ram\\"))] { 128 }\\n} static BUF: [u32; array_size()] = [0u32; array_size()]; } These are new to stable Rust as of 1.31, so documentation is still sparse. The\\nfunctionality available to const fn is also very limited at the time of\\nwriting; in future Rust releases it is expected to expand on what is permitted\\nin a const fn.","breadcrumbs":"Tips for embedded C developers » Compile-Time Sizes and Computation","id":"159","title":"Compile-Time Sizes and Computation"},"16":{"body":"#![no_std] is a crate-level attribute that indicates that the crate will link to the core-crate instead of the std-crate.\\nThe libcore crate in turn is a platform-agnostic subset of the std crate\\nwhich makes no assumptions about the system the program will run on.\\nAs such, it provides APIs for language primitives like floats, strings and slices, as well as APIs that expose processor features\\nlike atomic operations and SIMD instructions. However it lacks APIs for anything that involves platform integration.\\nBecause of these properties no_std and libcore code can be used for any kind of\\nbootstrapping (stage 0) code like bootloaders, firmware or kernels.","breadcrumbs":"Introduction » no_std » Summary","id":"16","title":"Summary"},"160":{"body":"Rust provides an extremely powerful macro system. While the C preprocessor\\noperates almost directly on the text of your source code, the Rust macro system\\noperates at a higher level. There are two varieties of Rust macro: macros by\\nexample and procedural macros. The former are simpler and most common; they\\nlook like function calls and can expand to a complete expression, statement,\\nitem, or pattern. Procedural macros are more complex but permit extremely\\npowerful additions to the Rust language: they can transform arbitrary Rust\\nsyntax into new Rust syntax. In general, where you might have used a C preprocessor macro, you probably want\\nto see if a macro-by-example can do the job instead. They can be defined in\\nyour crate and easily used by your own crate or exported for other users. Be\\naware that since they must expand to complete expressions, statements, items,\\nor patterns, some use cases of C preprocessor macros will not work, for example\\na macro that expands to part of a variable name or an incomplete set of items\\nin a list. As with Cargo features, it is worth considering if you even need the macro. In\\nmany cases a regular function is easier to understand and will be inlined to\\nthe same code as a macro. The #[inline] and #[inline(always)] attributes\\ngive you further control over this process, although care should be taken here\\nas well — the compiler will automatically inline functions from the same crate\\nwhere appropriate, so forcing it to do so inappropriately might actually lead\\nto decreased performance. Explaining the entire Rust macro system is out of scope for this tips page, so\\nyou are encouraged to consult the Rust documentation for full details.","breadcrumbs":"Tips for embedded C developers » Macros","id":"160","title":"Macros"},"161":{"body":"Most Rust crates are built using Cargo (although it is not required). This\\ntakes care of many difficult problems with traditional build systems. However,\\nyou may wish to customise the build process. Cargo provides build.rs\\nscripts for this purpose. They are Rust scripts which can interact with the\\nCargo build system as required. Common use cases for build scripts include: provide build-time information, for example statically embedding the build\\ndate or Git commit hash into your executable generate linker scripts at build time depending on selected features or other\\nlogic change the Cargo build configuration add extra static libraries to link against At present there is no support for post-build scripts, which you might\\ntraditionally have used for tasks like automatic generation of binaries from\\nthe build objects or printing build information.","breadcrumbs":"Tips for embedded C developers » Build System","id":"161","title":"Build System"},"162":{"body":"Using Cargo for your build system also simplifies cross-compiling. In most\\ncases it suffices to tell Cargo --target thumbv6m-none-eabi and find a\\nsuitable executable in target/thumbv6m-none-eabi/debug/myapp. For platforms not natively supported by Rust, you will need to build libcore\\nfor that target yourself. On such platforms, Xargo can be used as a stand-in\\nfor Cargo which automatically builds libcore for you.","breadcrumbs":"Tips for embedded C developers » Cross-Compiling","id":"162","title":"Cross-Compiling"},"163":{"body":"In C you are probably used to accessing arrays directly by their index: int16_t arr[16];\\nint i;\\nfor(i=0; iRTOS interoperability: Zephyr Project We are collecting examples and use cases for this on our issue tracker in issue #62.","breadcrumbs":"Interoperability » Interoperability with RTOSs","id":"170","title":"Interoperability with RTOSs"},"171":{"body":"Using C or C++ inside of a Rust project consists of two major parts: Wrapping the exposed C API for use with Rust Building your C or C++ code to be integrated with the Rust code As C++ does not have a stable ABI for the Rust compiler to target, it is recommended to use the C ABI when combining Rust with C or C++.","breadcrumbs":"Interoperability » A little C with your Rust » A little C with your Rust","id":"171","title":"A little C with your Rust"},"172":{"body":"Before consuming C or C++ code from Rust, it is necessary to define (in Rust) what data types and function signatures exist in the linked code. In C or C++, you would include a header ( .h or .hpp) file which defines this data. In Rust, it is necessary to either manually translate these definitions to Rust, or use a tool to generate these definitions. First, we will cover manually translating these definitions from C/C++ to Rust.","breadcrumbs":"Interoperability » A little C with your Rust » Defining the interface","id":"172","title":"Defining the interface"},"173":{"body":"Typically, libraries written in C or C++ will provide a header file defining all types and functions used in public interfaces. An example file may look like this: /* File: cool.h */\\ntypedef struct CoolStruct { int x; int y;\\n} CoolStruct; void cool_function(int i, char c, CoolStruct* cs); When translated to Rust, this interface would look as such: /* File: cool_bindings.rs */\\n#[repr(C)]\\npub struct CoolStruct { pub x: cty::c_int, pub y: cty::c_int,\\n} extern \\"C\\" { pub fn cool_function( i: cty::c_int, c: cty::c_char, cs: *mut CoolStruct );\\n} Let’s take a look at this definition one piece at a time, to explain each of the parts. #[repr(C)]\\npub struct CoolStruct { ... } By default, Rust does not guarantee order, padding, or the size of data included in a struct. In order to guarantee compatibility with C code, we include the #[repr(C)] attribute, which instructs the Rust compiler to always use the same rules C does for organizing data within a struct. pub x: cty::c_int,\\npub y: cty::c_int, Due to the flexibility of how C or C++ defines an int or char, it is recommended to use primitive data types defined in cty, which will map types from C to types in Rust. extern \\"C\\" { pub fn cool_function( ... ); } This statement defines the signature of a function that uses the C ABI, called cool_function. By defining the signature without defining the body of the function, the definition of this function will need to be provided elsewhere, or linked into the final library or binary from a static library. i: cty::c_int, c: cty::c_char, cs: *mut CoolStruct Similar to our datatype above, we define the datatypes of the function arguments using C-compatible definitions. We also retain the same argument names, for clarity. We have one new type here, *mut CoolStruct. As C does not have a concept of Rust’s references, which would look like this: &mut CoolStruct, we instead have a raw pointer. As dereferencing this pointer is unsafe, and the pointer may in fact be a null pointer, care must be taken to ensure the guarantees typical of Rust when interacting with C or C++ code.","breadcrumbs":"Interoperability » A little C with your Rust » Wrapping C functions and Datatypes","id":"173","title":"Wrapping C functions and Datatypes"},"174":{"body":"Rather than manually generating these interfaces, which may be tedious and error prone, there is a tool called bindgen which will perform these conversions automatically. For instructions of the usage of bindgen, please refer to the bindgen user’s manual, however the typical process consists of the following: Gather all C or C++ headers defining interfaces or datatypes you would like to use with Rust. Write a bindings.h file, which #include \\"...\\"’s each of the files you gathered in step one. Feed this bindings.h file, along with any compilation flags used to compile\\nyour code into bindgen. Tip: use Builder.ctypes_prefix(\\"cty\\") / --ctypes-prefix=cty and Builder.use_core() / --use-core to make the generated code #![no_std] compatible. bindgen will produce the generated Rust code to the output of the terminal window. This output may be piped to a file in your project, such as bindings.rs. You may use this file in your Rust project to interact with C/C++ code compiled and linked as an external library. Tip: don’t forget to use the cty crate if your types in the generated bindings are prefixed with cty.","breadcrumbs":"Interoperability » A little C with your Rust » Automatically generating the interface","id":"174","title":"Automatically generating the interface"},"175":{"body":"As the Rust compiler does not directly know how to compile C or C++ code (or code from any other language, which presents a C interface), it is necessary to compile your non-Rust code ahead of time. For embedded projects, this most commonly means compiling the C/C++ code to a static archive (such as cool-library.a), which can then be combined with your Rust code at the final linking step. If the library you would like to use is already distributed as a static archive, it is not necessary to rebuild your code. Just convert the provided interface header file as described above, and include the static archive at compile/link time. If your code exists as a source project, it will be necessary to compile your C/C++ code to a static library, either by triggering your existing build system (such as make, CMake, etc.), or by porting the necessary compilation steps to use a tool called the cc crate. For both of these steps, it is necessary to use a build.rs script.","breadcrumbs":"Interoperability » A little C with your Rust » Building your C/C++ code","id":"175","title":"Building your C/C++ code"},"176":{"body":"A build.rs script is a file written in Rust syntax, that is executed on your compilation machine, AFTER dependencies of your project have been built, but BEFORE your project is built. The full reference may be found here. build.rs scripts are useful for generating code (such as via bindgen), calling out to external build systems such as Make, or directly compiling C/C++ through use of the cc crate.","breadcrumbs":"Interoperability » A little C with your Rust » Rust build.rs build scripts","id":"176","title":"Rust build.rs build scripts"},"177":{"body":"For projects with complex external projects or build systems, it may be easiest to use std::process::Command to “shell out” to your other build systems by traversing relative paths, calling a fixed command (such as make library), and then copying the resulting static library to the proper location in the target build directory. While your crate may be targeting a no_std embedded platform, your build.rs executes only on machines compiling your crate. This means you may use any Rust crates which will run on your compilation host.","breadcrumbs":"Interoperability » A little C with your Rust » Triggering external build systems","id":"177","title":"Triggering external build systems"},"178":{"body":"For projects with limited dependencies or complexity, or for projects where it is difficult to modify the build system to produce a static library (rather than a final binary or executable), it may be easier to instead utilize the cc crate, which provides an idiomatic Rust interface to the compiler provided by the host. In the simplest case of compiling a single C file as a dependency to a static library, an example build.rs script using the cc crate would look like this: fn main() { cc::Build::new() .file(\\"src/foo.c\\") .compile(\\"foo\\");\\n} The build.rs is placed at the root of the package. Then cargo build will compile and execute it before the build of the package. A static archive named libfoo.a is generated and placed in the target directory.","breadcrumbs":"Interoperability » A little C with your Rust » Building C/C++ code with the cc crate","id":"178","title":"Building C/C++ code with the cc crate"},"179":{"body":"Using Rust code inside a C or C++ project mostly consists of two parts. Creating a C-friendly API in Rust Embedding your Rust project into an external build system Apart from cargo and meson, most build systems don’t have native Rust support.\\nSo you’re most likely best off just using cargo for compiling your crate and\\nany dependencies.","breadcrumbs":"Interoperability » A little Rust with your C » A little Rust with your C","id":"179","title":"A little Rust with your C"},"18":{"body":"RFC-1184","breadcrumbs":"Introduction » no_std » See Also","id":"18","title":"See Also"},"180":{"body":"Create a new cargo project as usual. There are flags to tell cargo to emit a systems library, instead of\\nits regular rust target.\\nThis also allows you to set a different output name for your library,\\nif you want it to differ from the rest of your crate. [lib]\\nname = \\"your_crate\\"\\ncrate-type = [\\"cdylib\\"] # Creates dynamic lib\\n# crate-type = [\\"staticlib\\"] # Creates static lib","breadcrumbs":"Interoperability » A little Rust with your C » Setting up a project","id":"180","title":"Setting up a project"},"181":{"body":"Because C++ has no stable ABI for the Rust compiler to target, we use C for\\nany interoperability between different languages. This is no exception when using Rust\\ninside of C and C++ code.","breadcrumbs":"Interoperability » A little Rust with your C » Building a C API","id":"181","title":"Building a C API"},"182":{"body":"The Rust compiler mangles symbol names differently than native code linkers expect.\\nAs such, any function that Rust exports to be used outside of Rust needs to be told\\nnot to be mangled by the compiler.","breadcrumbs":"Interoperability » A little Rust with your C » #[no_mangle]","id":"182","title":"#[no_mangle]"},"183":{"body":"By default, any function you write in Rust will use the\\nRust ABI (which is also not stabilized).\\nInstead, when building outwards facing FFI APIs we need to\\ntell the compiler to use the system ABI. Depending on your platform, you might want to target a specific ABI version, which are\\ndocumented here. Putting these parts together, you get a function that looks roughly like this. #[no_mangle]\\npub extern \\"C\\" fn rust_function() { } Just as when using C code in your Rust project you now need to transform data\\nfrom and to a form that the rest of the application will understand.","breadcrumbs":"Interoperability » A little Rust with your C » extern \\"C\\"","id":"183","title":"extern \\"C\\""},"184":{"body":"So then, that’s one half of the problem solved.\\nHow do you use this now? This very much depends on your project and/or build system cargo will create a my_lib.so/ my_lib.dll or my_lib.a file,\\ndepending on your platform and settings. This library can simply be linked\\nby your build system. However, calling a Rust function from C requires a header file to declare\\nthe function signatures. Every function in your Rust-ffi API needs to have a corresponding header function. #[no_mangle]\\npub extern \\"C\\" fn rust_function() {} would then become void rust_function(); etc. There is a tool to automate this process,\\ncalled cbindgen which analyses your Rust code\\nand then generates headers for your C and C++ projects from it. At this point, using the Rust functions from C\\nis as simple as including the header and calling them! #include \\"my-rust-project.h\\"\\nrust_function();","breadcrumbs":"Interoperability » A little Rust with your C » Linking and greater project context.","id":"184","title":"Linking and greater project context."},"185":{"body":"","breadcrumbs":"Unsorted topics » Unsorted topics","id":"185","title":"Unsorted topics"},"186":{"body":"Everyone wants their program to be super fast and super small but it’s usually\\nnot possible to have both characteristics. This section discusses the\\ndifferent optimization levels that rustc provides and how they affect the\\nexecution time and binary size of a program.","breadcrumbs":"Unsorted topics » Optimizations: The speed size tradeoff » Optimizations: the speed size tradeoff","id":"186","title":"Optimizations: the speed size tradeoff"},"187":{"body":"This is the default. When you call cargo build you use the development (AKA dev) profile. This profile is optimized for debugging so it enables debug\\ninformation and does not enable any optimizations, i.e. it uses -C opt-level = 0. At least for bare metal development, debuginfo is zero cost in the sense that it\\nwon’t occupy space in Flash / ROM so we actually recommend that you enable\\ndebuginfo in the release profile – it is disabled by default. That will let you\\nuse breakpoints when debugging release builds. [profile.release]\\n# symbols are nice and they don\'t increase the size on Flash\\ndebug = true No optimizations is great for debugging because stepping through the code feels\\nlike you are executing the program statement by statement, plus you can print\\nstack variables and function arguments in GDB. When the code is optimized, trying\\nto print variables results in $0 = being printed. The biggest downside of the dev profile is that the resulting binary will be\\nhuge and slow. The size is usually more of a problem because unoptimized\\nbinaries can occupy dozens of KiB of Flash, which your target device may not\\nhave – the result: your unoptimized binary doesn’t fit in your device! Can we have smaller, debugger friendly binaries? Yes, there’s a trick.","breadcrumbs":"Unsorted topics » Optimizations: The speed size tradeoff » No optimizations","id":"187","title":"No optimizations"},"188":{"body":"There’s a Cargo feature named profile-overrides that lets you\\noverride the optimization level of dependencies. You can use that feature to\\noptimize all dependencies for size while keeping the top crate unoptimized and\\ndebugger friendly. Beware that generic code can sometimes be optimized alongside the crate where it\\nis instantiated, rather than the crate where it is defined. If you create an\\ninstance of a generic struct in your application and find that it pulls in code\\nwith a large footprint, it may be that increasing the optimisation level of the\\nrelevant dependencies has no effect. Here’s an example: # Cargo.toml\\n[package]\\nname = \\"app\\"\\n# .. [profile.dev.package.\\"*\\"] # +\\nopt-level = \\"z\\" # + Without the override: $ cargo size --bin app -- -A\\napp :\\nsection size addr\\n.vector_table 1024 0x8000000\\n.text 9060 0x8000400\\n.rodata 1708 0x8002780\\n.data 0 0x20000000\\n.bss 4 0x20000000 With the override: $ cargo size --bin app -- -A\\napp :\\nsection size addr\\n.vector_table 1024 0x8000000\\n.text 3490 0x8000400\\n.rodata 1100 0x80011c0\\n.data 0 0x20000000\\n.bss 4 0x20000000 That’s a 6 KiB reduction in Flash usage without any loss in the debuggability of\\nthe top crate. If you step into a dependency then you’ll start seeing those messages again but it’s usually the case that you want\\nto debug the top crate and not the dependencies. And if you do need to debug a\\ndependency then you can use the profile-overrides feature to exclude a\\nparticular dependency from being optimized. See example below: # .. # don\'t optimize the `cortex-m-rt` crate\\n[profile.dev.package.cortex-m-rt] # +\\nopt-level = 0 # + # but do optimize all the other dependencies\\n[profile.dev.package.\\"*\\"]\\ncodegen-units = 1 # better optimizations\\nopt-level = \\"z\\" Now the top crate and cortex-m-rt are debugger friendly!","breadcrumbs":"Unsorted topics » Optimizations: The speed size tradeoff » Optimizing dependencies","id":"188","title":"Optimizing dependencies"},"189":{"body":"As of 2018-09-18 rustc supports three “optimize for speed” levels: opt-level = 1, 2 and 3. When you run cargo build --release you are using the release\\nprofile which defaults to opt-level = 3. Both opt-level = 2 and 3 optimize for speed at the expense of binary size,\\nbut level 3 does more vectorization and inlining than level 2. In\\nparticular, you’ll see that at opt-level equal to or greater than 2 LLVM will\\nunroll loops. Loop unrolling has a rather high cost in terms of Flash / ROM\\n(e.g. from 26 bytes to 194 for a zero this array loop) but can also halve the\\nexecution time given the right conditions (e.g. number of iterations is big\\nenough). Currently there’s no way to disable loop unrolling in opt-level = 2 and 3 so\\nif you can’t afford its cost you should optimize your program for size.","breadcrumbs":"Unsorted topics » Optimizations: The speed size tradeoff » Optimize for speed","id":"189","title":"Optimize for speed"},"19":{"body":"Dealing with microcontrollers involves using several different tools as we’ll be\\ndealing with an architecture different than your laptop’s and we’ll have to run\\nand debug programs on a remote device. We’ll use all the tools listed below. Any recent version should work when a\\nminimum version is not specified, but we have listed the versions we have\\ntested. Rust 1.31, 1.31-beta, or a newer toolchain PLUS ARM Cortex-M compilation\\nsupport. cargo-binutils ~0.1.4 qemu-system-arm. Tested versions: 3.0.0 OpenOCD >=0.8. Tested versions: v0.9.0 and v0.10.0 GDB with ARM support. Version 7.12 or newer highly recommended. Tested\\nversions: 7.10, 7.11, 7.12 and 8.1 cargo-generate or git.\\nThese tools are optional but will make it easier to follow along with the book. The text below explains why we are using these tools. Installation instructions\\ncan be found on the next page.","breadcrumbs":"Introduction » Tooling » Tooling","id":"19","title":"Tooling"},"190":{"body":"As of 2018-09-18 rustc supports two “optimize for size” levels: opt-level = \\"s\\" and \\"z\\". These names were inherited from clang / LLVM and are not too\\ndescriptive but \\"z\\" is meant to give the idea that it produces smaller\\nbinaries than \\"s\\". If you want your release binaries to be optimized for size then change the profile.release.opt-level setting in Cargo.toml as shown below. [profile.release]\\n# or \\"z\\"\\nopt-level = \\"s\\" These two optimization levels greatly reduce LLVM’s inline threshold, a metric\\nused to decide whether to inline a function or not. One of Rust principles are\\nzero cost abstractions; these abstractions tend to use a lot of newtypes and\\nsmall functions to hold invariants (e.g. functions that borrow an inner value\\nlike deref, as_ref) so a low inline threshold can make LLVM miss\\noptimization opportunities (e.g. eliminate dead branches, inline calls to\\nclosures). When optimizing for size you may want to try increasing the inline threshold to\\nsee if that has any effect on the binary size. The recommended way to change the\\ninline threshold is to append the -C inline-threshold flag to the other\\nrustflags in .cargo/config.toml. # .cargo/config.toml\\n# this assumes that you are using the cortex-m-quickstart template\\n[target.\'cfg(all(target_arch = \\"arm\\", target_os = \\"none\\"))\']\\nrustflags = [ # .. \\"-C\\", \\"inline-threshold=123\\", # +\\n] What value to use? As of 1.29.0 these are the inline thresholds that the\\ndifferent optimization levels use: opt-level = 3 uses 275 opt-level = 2 uses 225 opt-level = \\"s\\" uses 75 opt-level = \\"z\\" uses 25 You should try 225 and 275 when optimizing for size.","breadcrumbs":"Unsorted topics » Optimizations: The speed size tradeoff » Optimize for size","id":"190","title":"Optimize for size"},"191":{"body":"If you want to perform math related functionality like calculating the squareroot or\\nthe exponential of a number and you have the full standard library available, your code\\nmight look like this: //! Some mathematical functions with standard support available fn main() { let float: f32 = 4.82832; let floored_float = float.floor(); let sqrt_of_four = floored_float.sqrt(); let sinus_of_four = floored_float.sin(); let exponential_of_four = floored_float.exp(); println!(\\"Floored test float {} to {}\\", float, floored_float); println!(\\"The square root of {} is {}\\", floored_float, sqrt_of_four); println!(\\"The sinus of four is {}\\", sinus_of_four); println!( \\"The exponential of four to the base e is {}\\", exponential_of_four )\\n} Without standard library support, these functions are not available.\\nAn external crate like libm can be used instead. The example code\\nwould then look like this: #![no_main]\\n#![no_std] use panic_halt as _; use cortex_m_rt::entry;\\nuse cortex_m_semihosting::{debug, hprintln};\\nuse libm::{exp, floorf, sin, sqrtf}; #[entry]\\nfn main() -> ! { let float = 4.82832; let floored_float = floorf(float); let sqrt_of_four = sqrtf(floored_float); let sinus_of_four = sin(floored_float.into()); let exponential_of_four = exp(floored_float.into()); hprintln!(\\"Floored test float {} to {}\\", float, floored_float).unwrap(); hprintln!(\\"The square root of {} is {}\\", floored_float, sqrt_of_four).unwrap(); hprintln!(\\"The sinus of four is {}\\", sinus_of_four).unwrap(); hprintln!( \\"The exponential of four to the base e is {}\\", exponential_of_four ) .unwrap(); // exit QEMU // NOTE do not run this on hardware; it can corrupt OpenOCD state // debug::exit(debug::EXIT_SUCCESS); loop {}\\n} If you need to perform more complex operations like DSP signal processing or advanced linear\\nalgebra on your MCU, the following crates might help you CMSIS DSP library binding constgebra micromath microfft nalgebra","breadcrumbs":"Unsorted topics » Performing Math Functionality » Performing math functionality with #[no_std]","id":"191","title":"Performing math functionality with #[no_std]"},"192":{"body":"The embedded ecosystem is full of different protocols, hardware components and\\nvendor-specific things that use their own terms and abbreviations. This Glossary\\nattempts to list them with pointers for understanding them better.","breadcrumbs":"Appendix A: Glossary » Appendix A: Glossary","id":"192","title":"Appendix A: Glossary"},"193":{"body":"A Board Support Crate provides a high level interface configured for a specific\\nboard. It usually depends on a HAL crate.\\nThere is a more detailed description on the memory-mapped registers page\\nor for a broader overview see this video.","breadcrumbs":"Appendix A: Glossary » BSP","id":"193","title":"BSP"},"194":{"body":"Floating-point Unit. A ‘math processor’ running only operations on floating-point numbers.","breadcrumbs":"Appendix A: Glossary » FPU","id":"194","title":"FPU"},"195":{"body":"A Hardware Abstraction Layer crate provides a developer friendly interface to a microcontroller’s\\nfeatures and peripherals. It is usually implemented on top of a Peripheral Access Crate (PAC).\\nIt may also implement traits from the embedded-hal crate.\\nThere is a more detailed description on the memory-mapped registers page\\nor for a broader overview see this video.","breadcrumbs":"Appendix A: Glossary » HAL","id":"195","title":"HAL"},"196":{"body":"Sometimes referred to as I²C or Inter-IC. It is a protocol meant for hardware communication\\nwithin a single integrated circuit. See here for more details","breadcrumbs":"Appendix A: Glossary » I2C","id":"196","title":"I2C"},"197":{"body":"A Peripheral Access Crate provides access to a microcontroller’s peripherals. It is one of\\nthe lower level crates and is usually generated directly from the provided SVD, often\\nusing svd2rust. The Hardware Abstraction Layer\\nwould usually depend on this crate.\\nThere is a more detailed description on the memory-mapped registers page\\nor for a broader overview see this video.","breadcrumbs":"Appendix A: Glossary » PAC","id":"197","title":"PAC"},"198":{"body":"Serial Peripheral Interface. See here for more information.","breadcrumbs":"Appendix A: Glossary » SPI","id":"198","title":"SPI"},"199":{"body":"System View Description is an XML file format used to describe the programmers view of a\\nmicrocontroller device. You can read more about it on the ARM CMSIS documentation site.","breadcrumbs":"Appendix A: Glossary » SVD","id":"199","title":"SVD"},"2":{"body":"The goals of this book are: Get developers up to speed with embedded Rust development. i.e. How to set\\nup a development environment. Share current best practices about using Rust for embedded development. i.e.\\nHow to best use Rust language features to write more correct embedded\\nsoftware. Serve as a cookbook in some cases. e.g. How do I mix C and Rust in a single\\nproject? This book tries to be as general as possible but to make things easier for both\\nthe readers and the writers it uses the ARM Cortex-M architecture in all its\\nexamples. However, the book doesn’t assume that the reader is familiar with this\\nparticular architecture and explains details particular to this architecture\\nwhere required.","breadcrumbs":"Introduction » Scope","id":"2","title":"Scope"},"20":{"body":"Bare metal programs are non-standard ( no_std) Rust programs that require some\\nadjustments to the linking process in order to get the memory layout of the program\\nright. This requires some additional files (like linker scripts) and\\nsettings (like linker flags). We have packaged those for you in a template\\nsuch that you only need to fill in the missing information (such as the project name and the\\ncharacteristics of your target hardware). Our template is compatible with cargo-generate: a Cargo subcommand for\\ncreating new Cargo projects from templates. You can also download the\\ntemplate using git, curl, wget, or your web browser.","breadcrumbs":"Introduction » Tooling » cargo-generate OR git","id":"20","title":"cargo-generate OR git"},"200":{"body":"Universal asynchronous receiver-transmitter. See here for more information.","breadcrumbs":"Appendix A: Glossary » UART","id":"200","title":"UART"},"201":{"body":"Universal synchronous and asynchronous receiver-transmitter. See here for more information.","breadcrumbs":"Appendix A: Glossary » USART","id":"201","title":"USART"},"21":{"body":"cargo-binutils is a collection of Cargo subcommands that make it easy to use\\nthe LLVM tools that are shipped with the Rust toolchain. These tools include the\\nLLVM versions of objdump, nm and size and are used for inspecting\\nbinaries. The advantage of using these tools over GNU binutils is that (a) installing the\\nLLVM tools is the same one-command installation ( rustup component add llvm-tools) regardless of your OS and (b) tools like objdump support\\nall the architectures that rustc supports – from ARM to x86_64 – because\\nthey both share the same LLVM backend.","breadcrumbs":"Introduction » Tooling » cargo-binutils","id":"21","title":"cargo-binutils"},"22":{"body":"QEMU is an emulator. In this case we use the variant that can fully emulate ARM\\nsystems. We use QEMU to run embedded programs on the host. Thanks to this you\\ncan follow some parts of this book even if you don’t have any hardware with you!","breadcrumbs":"Introduction » Tooling » qemu-system-arm","id":"22","title":"qemu-system-arm"},"23":{"body":"","breadcrumbs":"Introduction » Tooling » Tooling for Embedded Rust Debugging","id":"23","title":"Tooling for Embedded Rust Debugging"},"24":{"body":"Debugging embedded systems in Rust requires specialized tools including software to manage the debugging process, debuggers to inspect and control program execution, and hardware probes to facilitate interaction between the host and the embedded device. This document outlines essential software tools like Probe-rs and OpenOCD, which simplify and support the debugging process, alongside prominent debuggers such as GDB and the Probe-rs Visual Studio Code extension. Additionally, it covers key hardware probes such as Rusty-probe, ST-Link, J-Link, and MCU-Link, which are integral for effective debugging and programming of embedded devices.","breadcrumbs":"Introduction » Tooling » Overview","id":"24","title":"Overview"},"25":{"body":"","breadcrumbs":"Introduction » Tooling » Software that drives debugging tools","id":"25","title":"Software that drives debugging tools"},"26":{"body":"Probe-rs is a modern, Rust-focused software designed to work with debuggers in embedded systems. Unlike OpenOCD, Probe-rs is built with simplicity in mind and aims to reduce the configuration burden often found in other debugging solutions. It supports various probes and targets, providing a high-level interface for interacting with embedded hardware. Probe-rs integrates directly with Rust tooling, and integrates with Visual Studio Code through its extension, allowing developers to streamline their debugging workflow.","breadcrumbs":"Introduction » Tooling » Probe-rs","id":"26","title":"Probe-rs"},"27":{"body":"OpenOCD is an open-source software tool used for debugging, testing, and programming embedded systems. It provides an interface between the host system and embedded hardware, supporting various transport layers like JTAG and SWD (Serial Wire Debug). OpenOCD integrates with GDB, which is a debugger. OpenOCD is widely supported, with extensive documentation and a large community, but may require complex configuration, especially for custom embedded setups.","breadcrumbs":"Introduction » Tooling » OpenOCD (Open On-Chip Debugger)","id":"27","title":"OpenOCD (Open On-Chip Debugger)"},"28":{"body":"A debugger allows developers to inspect and control the execution of a program in order to identify and correct errors or bugs. It provides functionalities such as setting breakpoints, stepping through code line by line, and examining the values of variables and memory states. Debuggers are essential for thorough software development and maintenance, enabling developers to ensure that their code behaves as intended under various conditions. Debuggers know how to: Interact with the memory mapped registers. Set Breakpoints/Watchpoints. Read and write to the memory mapped registers. Detect when the MCU has been halted for a debug event. Continue MCU execution after a debug event has been encountered. Erase and write to the microcontroller’s FLASH.","breadcrumbs":"Introduction » Tooling » Debuggers","id":"28","title":"Debuggers"},"29":{"body":"Probe-rs has a Visual Studio Code extension, providing a seamless debugging experience without extensive setup. Through this connection, developers can use Rust-specific features like pretty printing and detailed error messages, ensuring that their debugging process aligns with the Rust ecosystem.","breadcrumbs":"Introduction » Tooling » Probe-rs Visual Studio Code Extension","id":"29","title":"Probe-rs Visual Studio Code Extension"},"3":{"body":"This book caters towards people with either some embedded background or some Rust background, however we believe\\neverybody curious about embedded Rust programming can get something out of this book. For those without any prior knowledge\\nwe suggest you read the “Assumptions and Prerequisites” section and catch up on missing knowledge to get more out of the book\\nand improve your reading experience. You can check out the “Other Resources” section to find resources on topics\\nyou might want to catch up on.","breadcrumbs":"Introduction » Who This Book is For","id":"3","title":"Who This Book is For"},"30":{"body":"TRACE32 is a professional debugging and tracing solution developed by Lauterbach for embedded systems. It supports a wide range of processor architectures, including ARM and RISC-V, and connects to target hardware via JTAG, SWD, and various trace interfaces. TRACE32 provides advanced debugging capabilities such as multicore debugging, complex breakpoints, and real-time trace analysis. It works with standard ELF/DWARF debug information, making it compatible with Rust binaries built using conventional toolchains.","breadcrumbs":"Introduction » Tooling » TRACE32","id":"30","title":"TRACE32"},"31":{"body":"GDB is a versatile debugging tool that allows developers to examine the state of programs while they run or after they crash. For embedded Rust, GDB connects to the target system via OpenOCD or other debugging servers to interact with the embedded code. GDB is highly configurable and supports features like remote debugging, variable inspection, and conditional breakpoints. It can be used on a variety of platforms, and has extensive support for Rust-specific debugging needs, such as pretty printing and integration with IDEs.","breadcrumbs":"Introduction » Tooling » GDB (GNU Debugger)","id":"31","title":"GDB (GNU Debugger)"},"32":{"body":"A hardware probe is a device used in the development and debugging of embedded systems to facilitate communication between a host computer and the target embedded device. It typically supports protocols like JTAG or SWD, enabling it to program, debug, and analyze the microcontroller or microprocessor on the embedded system. Hardware probes are crucial for developers to set breakpoints, step through code, and inspect memory and processor registers, effectively allowing them to diagnose and fix issues in real-time.","breadcrumbs":"Introduction » Tooling » Probes","id":"32","title":"Probes"},"33":{"body":"Rusty-probe is an open-sourced USB-based hardware debugging probe designed to work with probe-rs. The combination of Rusty-Probe and probe-rs provides an easy-to-use, cost-effective solution for developers working with embedded Rust applications.","breadcrumbs":"Introduction » Tooling » Rusty-probe","id":"33","title":"Rusty-probe"},"34":{"body":"The ST-Link is a popular debugging and programming probe developed by STMicroelectronics primarily for their STM32 and STM8 microcontroller series. It supports both debugging and programming via JTAG or SWD (Serial Wire Debug) interfaces. ST-Link is widely used due to its direct support from STMicroelectronics’ extensive range of development boards and its integration into major IDEs, making it a convenient choice for developers working with STM microcontrollers.","breadcrumbs":"Introduction » Tooling » ST-Link","id":"34","title":"ST-Link"},"35":{"body":"J-Link, developed by SEGGER Microcontroller, is a robust and versatile debugger supporting a wide range of CPU cores and devices beyond just ARM, such as RISC-V. Known for its high performance and reliability, J-Link supports various communication interfaces, including JTAG, SWD, and fine-pitch JTAG interfaces. It is favored for its advanced features like unlimited breakpoints in flash memory and its compatibility with a multitude of development environments.","breadcrumbs":"Introduction » Tooling » J-Link","id":"35","title":"J-Link"},"36":{"body":"MCU-Link is a debugging probe that also functions as a programmer, provided by NXP Semiconductors. It supports a variety of ARM Cortex microcontrollers and interfaces seamlessly with development tools like MCUXpresso IDE. MCU-Link is particularly notable for its versatility and affordability, making it an accessible option for hobbyists, educators, and professional developers alike.","breadcrumbs":"Introduction » Tooling » MCU-Link","id":"36","title":"MCU-Link"},"37":{"body":"This page contains OS-agnostic installation instructions for a few of the tools:","breadcrumbs":"Introduction » Installation » Installing the tools","id":"37","title":"Installing the tools"},"38":{"body":"Install rustup by following the instructions at https://rustup.rs. NOTE Make sure you have a compiler version equal to or newer than 1.31. rustc -V should return a date newer than the one shown below. $ rustc -V\\nrustc 1.31.1 (b6c32da9b 2018-12-18) For bandwidth and disk usage concerns the default installation only supports\\nnative compilation. To add cross compilation support for the ARM Cortex-M\\narchitectures choose one of the following compilation targets. For the STM32F3DISCOVERY\\nboard used for the examples in this book, use the thumbv7em-none-eabihf target. Find the best Cortex-M for you. Cortex-M0, M0+, and M1 (ARMv6-M architecture): rustup target add thumbv6m-none-eabi Cortex-M3 (ARMv7-M architecture): rustup target add thumbv7m-none-eabi Cortex-M4 and M7 without hardware floating point (ARMv7E-M architecture): rustup target add thumbv7em-none-eabi Cortex-M4F and M7F with hardware floating point (ARMv7E-M architecture): rustup target add thumbv7em-none-eabihf Cortex-M23 (ARMv8-M architecture): rustup target add thumbv8m.base-none-eabi Cortex-M33 and M35P (ARMv8-M architecture): rustup target add thumbv8m.main-none-eabi Cortex-M33F and M35PF with hardware floating point (ARMv8-M architecture): rustup target add thumbv8m.main-none-eabihf","breadcrumbs":"Introduction » Installation » Rust Toolchain","id":"38","title":"Rust Toolchain"},"39":{"body":"cargo install cargo-binutils rustup component add llvm-tools WINDOWS: prerequisite C++ Build Tools for Visual Studio 2019 is installed. https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools&rel=16","breadcrumbs":"Introduction » Installation » cargo-binutils","id":"39","title":"cargo-binutils"},"4":{"body":"You are comfortable using the Rust Programming Language, and have written,\\nrun, and debugged Rust applications on a desktop environment. You should also\\nbe familiar with the idioms of the 2018 edition as this book targets\\nRust 2018. You are comfortable developing and debugging embedded systems in another\\nlanguage such as C, C++, or Ada, and are familiar with concepts such as: Cross Compilation Memory Mapped Peripherals Interrupts Common interfaces such as I2C, SPI, Serial, etc.","breadcrumbs":"Introduction » Assumptions and Prerequisites","id":"4","title":"Assumptions and Prerequisites"},"40":{"body":"We’ll use this later to generate a project from a template. cargo install cargo-generate Note: on some Linux distros (e.g. Ubuntu) you may need to install the packages libssl-dev and pkg-config prior to installing cargo-generate.","breadcrumbs":"Introduction » Installation » cargo-generate","id":"40","title":"cargo-generate"},"41":{"body":"Now follow the instructions specific to the OS you are using: Linux Windows macOS","breadcrumbs":"Introduction » Installation » OS-Specific Instructions","id":"41","title":"OS-Specific Instructions"},"42":{"body":"Here are the installation commands for a few Linux distributions.","breadcrumbs":"Introduction » Installation » Linux » Linux","id":"42","title":"Linux"},"43":{"body":"Ubuntu 18.04 or newer / Debian stretch or newer NOTE gdb-multiarch is the GDB command you’ll use to debug your ARM\\nCortex-M programs sudo apt install gdb-multiarch openocd qemu-system-arm Ubuntu 14.04 and 16.04 NOTE arm-none-eabi-gdb is the GDB command you’ll use to debug your ARM\\nCortex-M programs sudo apt install gdb-arm-none-eabi openocd qemu-system-arm Fedora 27 or newer sudo dnf install gdb openocd qemu-system-arm Arch Linux NOTE arm-none-eabi-gdb is the GDB command you’ll use to debug ARM\\nCortex-M programs sudo pacman -S arm-none-eabi-gdb qemu-system-arm openocd","breadcrumbs":"Introduction » Installation » Linux » Packages","id":"43","title":"Packages"},"44":{"body":"This rule lets you use OpenOCD with the Discovery board without root privilege. Create the file /etc/udev/rules.d/70-st-link.rules with the contents shown below. # STM32F3DISCOVERY rev A/B - ST-LINK/V2\\nATTRS{idVendor}==\\"0483\\", ATTRS{idProduct}==\\"3748\\", TAG+=\\"uaccess\\" # STM32F3DISCOVERY rev C+ - ST-LINK/V2-1\\nATTRS{idVendor}==\\"0483\\", ATTRS{idProduct}==\\"374b\\", TAG+=\\"uaccess\\" Then reload all the udev rules with: sudo udevadm control --reload-rules If you had the board plugged to your laptop, unplug it and then plug it again. You can check the permissions by running this command: lsusb Which should show something like (..)\\nBus 001 Device 018: ID 0483:374b STMicroelectronics ST-LINK/V2.1\\n(..) Take note of the bus and device numbers. Use those numbers to create a path like /dev/bus/usb//. Then use this path like so: ls -l /dev/bus/usb/001/018 crw-------+ 1 root root 189, 17 Sep 13 12:34 /dev/bus/usb/001/018 getfacl /dev/bus/usb/001/018 | grep user user::rw-\\nuser:you:rw- The + appended to permissions indicates the existence of an extended\\npermission. The getfacl command tells the user you can make use of\\nthis device. Now, go to the next section.","breadcrumbs":"Introduction » Installation » Linux » udev rules","id":"44","title":"udev rules"},"45":{"body":"All the tools can be installed using Homebrew or MacPorts:","breadcrumbs":"Introduction » Installation » MacOS » macOS","id":"45","title":"macOS"},"46":{"body":"$ # GDB\\n$ brew install arm-none-eabi-gdb $ # OpenOCD\\n$ brew install openocd $ # QEMU\\n$ brew install qemu NOTE If OpenOCD crashes you may need to install the latest version using: $ brew install --HEAD openocd","breadcrumbs":"Introduction » Installation » MacOS » Install tools with Homebrew","id":"46","title":"Install tools with Homebrew"},"47":{"body":"$ # GDB\\n$ sudo port install arm-none-eabi-gcc $ # OpenOCD\\n$ sudo port install openocd $ # QEMU\\n$ sudo port install qemu That’s all! Go to the next section.","breadcrumbs":"Introduction » Installation » MacOS » Install tools with MacPorts","id":"47","title":"Install tools with MacPorts"},"48":{"body":"","breadcrumbs":"Introduction » Installation » Windows » Windows","id":"48","title":"Windows"},"49":{"body":"ARM provides .exe installers for Windows. Grab one from here, and follow the instructions.\\nJust before the installation process finishes tick/select the “Add path to environment variable”\\noption. Then verify that the tools are in your %PATH%: $ arm-none-eabi-gdb -v\\nGNU gdb (GNU Tools for Arm Embedded Processors 7-2018-q2-update) 8.1.0.20180315-git\\n(..)","breadcrumbs":"Introduction » Installation » Windows » arm-none-eabi-gdb","id":"49","title":"arm-none-eabi-gdb"},"5":{"body":"If you are unfamiliar with anything mentioned above or if you want more information about a specific topic mentioned in this book you might find some of these resources helpful. Topic Resource Description Rust Rust Book If you are not yet comfortable with Rust, we highly suggest reading this book. Rust, Embedded Discovery Book If you have never done any embedded programming, this book might be a better start Rust, Embedded Embedded Rust Bookshelf Here you can find several other resources provided by Rust’s Embedded Working Group. Rust, Embedded Embedonomicon The nitty gritty details when doing embedded programming in Rust. Rust, Embedded embedded FAQ Frequently asked questions about Rust in an embedded context. Rust, Embedded Comprehensive Rust 🦀: Bare Metal Teaching material for a 4-day class on bare-metal Rust development Interrupts Interrupt - Memory-mapped IO/Peripherals Memory-mapped I/O - SPI, UART, RS232, USB, I2C, TTL Stack Exchange about SPI, UART, and other interfaces -","breadcrumbs":"Introduction » Other Resources","id":"5","title":"Other Resources"},"50":{"body":"There’s no official binary release of OpenOCD for Windows but if you’re not in the mood to compile\\nit yourself, the xPack project provides a binary distribution, here. Follow the\\nprovided installation instructions. Then update your %PATH% environment variable to\\ninclude the path where the binaries were installed. ( C:\\\\Users\\\\USERNAME\\\\AppData\\\\Roaming\\\\xPacks\\\\@xpack-dev-tools\\\\openocd\\\\0.10.0-13.1\\\\.content\\\\bin\\\\,\\nif you’ve been using the easy install) Verify that OpenOCD is in your %PATH% with: $ openocd -v\\nOpen On-Chip Debugger 0.10.0\\n(..)","breadcrumbs":"Introduction » Installation » Windows » OpenOCD","id":"50","title":"OpenOCD"},"51":{"body":"Grab QEMU from the official website.","breadcrumbs":"Introduction » Installation » Windows » QEMU","id":"51","title":"QEMU"},"52":{"body":"You’ll also need to install this USB driver or OpenOCD won’t work. Follow the installer\\ninstructions and make sure you install the right version (32-bit or 64-bit) of the driver. That’s all! Go to the next section.","breadcrumbs":"Introduction » Installation » Windows » ST-LINK USB driver","id":"52","title":"ST-LINK USB driver"},"53":{"body":"In this section we check that some of the required tools / drivers have been\\ncorrectly installed and configured. Connect your laptop / PC to the discovery board using a Mini-USB USB cable. The\\ndiscovery board has two USB connectors; use the one labeled “USB ST-LINK” that\\nsits on the center of the edge of the board. Also check that the ST-LINK header is populated. See the picture below; the\\nST-LINK header is highlighted. Now run the following command: openocd -f interface/stlink.cfg -f target/stm32f3x.cfg NOTE: Old versions of openocd, including the 0.10.0 release from 2017, do\\nnot contain the new (and preferable) interface/stlink.cfg file; instead you\\nmay need to use interface/stlink-v2.cfg or interface/stlink-v2-1.cfg. You should get the following output and the program should block the console: Open On-Chip Debugger 0.10.0\\nLicensed under GNU GPL v2\\nFor bug reports, read http://openocd.org/doc/doxygen/bugs.html\\nInfo : auto-selecting first available session transport \\"hla_swd\\". To override use \'transport select \'.\\nadapter speed: 1000 kHz\\nadapter_nsrst_delay: 100\\nInfo : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD\\nnone separate\\nInfo : Unable to match requested speed 1000 kHz, using 950 kHz\\nInfo : Unable to match requested speed 1000 kHz, using 950 kHz\\nInfo : clock speed 950 kHz\\nInfo : STLINK v2 JTAG v27 API v2 SWIM v15 VID 0x0483 PID 0x374B\\nInfo : using stlink api v2\\nInfo : Target voltage: 2.919881\\nInfo : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints The contents may not match exactly but you should get the last line about\\nbreakpoints and watchpoints. If you got it then terminate the OpenOCD process\\nand move to the next section. If you didn’t get the “breakpoints” line then try one of the following commands. openocd -f interface/stlink-v2.cfg -f target/stm32f3x.cfg openocd -f interface/stlink-v2-1.cfg -f target/stm32f3x.cfg If one of those commands works it means you got an old hardware revision of the\\ndiscovery board. That won’t be a problem but commit that fact to memory as\\nyou’ll need to configure things a bit differently later on. You can move to the next section. If none of the commands work as a normal user then try to run them with root\\npermission (e.g. sudo openocd ..). If the commands do work with root\\npermission then check that the udev rules have been correctly set. If you have reached this point and OpenOCD is not working please open an issue\\nand we’ll help you out!","breadcrumbs":"Introduction » Installation » Verify Installation » Verify Installation","id":"53","title":"Verify Installation"},"54":{"body":"In this section we’ll walk you through the process of writing, building,\\nflashing and debugging embedded programs. You will be able to try most of the\\nexamples without any special hardware as we will show you the basics using\\nQEMU, a popular open-source hardware emulator. The only section where hardware\\nis required is, naturally enough, the Hardware section,\\nwhere we use OpenOCD to program an STM32F3DISCOVERY.","breadcrumbs":"Getting started » Getting Started","id":"54","title":"Getting Started"},"55":{"body":"We’ll start writing a program for the LM3S6965, a Cortex-M3 microcontroller.\\nWe have chosen this as our initial target because it can be emulated using QEMU\\nso you don’t need to fiddle with hardware in this section and we can focus on\\nthe tooling and the development process. IMPORTANT\\nWe’ll use the name “app” for the project name in this tutorial.\\nWhenever you see the word “app” you should replace it with the name you selected\\nfor your project. Or, you could also name your project “app” and avoid the\\nsubstitutions.","breadcrumbs":"Getting started » QEMU » QEMU","id":"55","title":"QEMU"},"56":{"body":"We’ll use the cortex-m-quickstart project template to generate a new\\nproject from it. The created project will contain a barebone application: a good\\nstarting point for a new embedded rust application. In addition, the project will\\ncontain an examples directory, with several separate applications, highlighting\\nsome of the key embedded rust functionality.","breadcrumbs":"Getting started » QEMU » Creating a non standard Rust program","id":"56","title":"Creating a non standard Rust program"},"57":{"body":"First install cargo-generate cargo install cargo-generate Then generate a new project cargo generate --git https://github.com/knurling-rs/app-template Project Name: app Creating project called `app`... Done! New project created /tmp/app cd app","breadcrumbs":"Getting started » QEMU » Using cargo-generate","id":"57","title":"Using cargo-generate"},"58":{"body":"Clone the repository git clone https://github.com/rust-embedded/cortex-m-quickstart app\\ncd app And then fill in the placeholders in the Cargo.toml file [package]\\nauthors = [\\"{{authors}}\\"] # \\"{{authors}}\\" -> \\"John Smith\\"\\nedition = \\"2018\\"\\nname = \\"{{project-name}}\\" # \\"{{project-name}}\\" -> \\"app\\"\\nversion = \\"0.1.0\\" # .. [[bin]]\\nname = \\"{{project-name}}\\" # \\"{{project-name}}\\" -> \\"app\\"\\ntest = false\\nbench = false","breadcrumbs":"Getting started » QEMU » Using git","id":"58","title":"Using git"},"59":{"body":"Grab the latest snapshot of the cortex-m-quickstart template and extract it. curl -LO https://github.com/rust-embedded/cortex-m-quickstart/archive/master.zip\\nunzip master.zip\\nmv cortex-m-quickstart-master app\\ncd app Or you can browse to cortex-m-quickstart, click the green “Clone or\\ndownload” button and then click “Download ZIP”. Then fill in the placeholders in the Cargo.toml file as done in the second\\npart of the “Using git” version.","breadcrumbs":"Getting started » QEMU » Using neither","id":"59","title":"Using neither"},"6":{"body":"This book has been translated by generous volunteers. If you would like your\\ntranslation listed here, please open a PR to add it. Japanese\\n( repository) Chinese\\n( repository)","breadcrumbs":"Introduction » Translations","id":"6","title":"Translations"},"60":{"body":"For convenience here are the most important parts of the source code in src/main.rs: #![no_std]\\n#![no_main] use panic_halt as _; use cortex_m_rt::entry; #[entry]\\nfn main() -> ! { loop { // your code goes here }\\n} This program is a bit different from a standard Rust program so let’s take a\\ncloser look. #![no_std] indicates that this program will not link to the standard crate, std. Instead it will link to its subset: the core crate. #![no_main] indicates that this program won’t use the standard main\\ninterface that most Rust programs use. The main (no pun intended) reason to go\\nwith no_main is that using the main interface in no_std context requires\\nnightly. use panic_halt as _;. This crate provides a panic_handler that defines\\nthe panicking behavior of the program. We will cover this in more detail in the Panicking chapter of the book. #[entry] is an attribute provided by the cortex-m-rt crate that’s used\\nto mark the entry point of the program. As we are not using the standard main\\ninterface we need another way to indicate the entry point of the program and\\nthat’d be #[entry]. fn main() -> !. Our program will be the only process running on the target\\nhardware so we don’t want it to end! We use a divergent function (the -> !\\nbit in the function signature) to ensure at compile time that’ll be the case.","breadcrumbs":"Getting started » QEMU » Program Overview","id":"60","title":"Program Overview"},"61":{"body":"First of all we will need the memory layout for the target microcontroller, the\\nLM3S6965 in our case. Otherwise the build will fail to link the image. Create a\\nfile named memory.x at the root of the project and paste the following content: MEMORY\\n{ /* NOTE 1 K = 1 KiBi = 1024 bytes */ /* TODO Adjust these memory regions to match your device memory layout */ /* These values correspond to the LM3S6965, one of the few devices QEMU can emulate */ FLASH : ORIGIN = 0x00000000, LENGTH = 256K RAM : ORIGIN = 0x20000000, LENGTH = 64K\\n} /* This is where the call stack will be allocated. */\\n/* The stack is of the full descending type. */\\n/* You may want to use this variable to locate the call stack and static variables in different memory regions. Below is shown the default value */\\n/* _stack_start = ORIGIN(RAM) + LENGTH(RAM); */ /* You can use this symbol to customize the location of the .text section */\\n/* If omitted the .text section will be placed right after the .vector_table section */\\n/* This is required only on microcontrollers that store some configuration right after the vector table */\\n/* _stext = ORIGIN(FLASH) + 0x400; */ /* Example of putting non-initialized variables into custom RAM locations. */\\n/* This assumes you have defined a region RAM2 above, and in the Rust sources added the attribute `#[link_section = \\".ram2bss\\"]` to the data you want to place there. */\\n/* Note that the section will not be zero-initialized by the runtime! */\\n/* SECTIONS { .ram2bss (NOLOAD) : ALIGN(4) { *(.ram2bss); . = ALIGN(4); } > RAM2 } INSERT AFTER .bss;\\n*/ The next step is to cross compile the program for the Cortex-M3 architecture.\\nThat’s as simple as running cargo build --target $TRIPLE if you know what the\\ncompilation target ( $TRIPLE) should be. Luckily, the .cargo/config.toml in the\\ntemplate has the answer: tail -n6 .cargo/config.toml [build]\\n# Pick ONE of these compilation targets\\n# target = \\"thumbv6m-none-eabi\\" # Cortex-M0 and Cortex-M0+\\ntarget = \\"thumbv7m-none-eabi\\" # Cortex-M3\\n# target = \\"thumbv7em-none-eabi\\" # Cortex-M4 and Cortex-M7 (no FPU)\\n# target = \\"thumbv7em-none-eabihf\\" # Cortex-M4F and Cortex-M7F (with FPU) To cross compile for the Cortex-M3 architecture we have to use thumbv7m-none-eabi. That target is not automatically installed when installing\\nthe Rust toolchain, it would now be a good time to add that target to the toolchain,\\nif you haven’t done it yet: rustup target add thumbv7m-none-eabi Since the thumbv7m-none-eabi compilation target has been set as the default in\\nyour .cargo/config.toml file, the two commands below do the same: cargo build --target thumbv7m-none-eabi\\ncargo build","breadcrumbs":"Getting started » QEMU » Cross compiling","id":"61","title":"Cross compiling"},"62":{"body":"Now we have a non-native ELF binary in target/thumbv7m-none-eabi/debug/app. We\\ncan inspect it using cargo-binutils. With cargo-readobj we can print the ELF headers to confirm that this is an ARM\\nbinary. cargo readobj --bin app -- --file-headers Note that: --bin app is sugar for inspect the binary at target/$TRIPLE/debug/app --bin app will also (re)compile the binary, if necessary ELF Header: Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2\'s complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0x0 Type: EXEC (Executable file) Machine: ARM Version: 0x1 Entry point address: 0x405 Start of program headers: 52 (bytes into file) Start of section headers: 153204 (bytes into file) Flags: 0x5000200 Size of this header: 52 (bytes) Size of program headers: 32 (bytes) Number of program headers: 2 Size of section headers: 40 (bytes) Number of section headers: 19 Section header string table index: 18 cargo-size can print the size of the linker sections of the binary. cargo size --bin app --release -- -A we use --release to inspect the optimized version app :\\nsection size addr\\n.vector_table 1024 0x0\\n.text 92 0x400\\n.rodata 0 0x45c\\n.data 0 0x20000000\\n.bss 0 0x20000000\\n.debug_str 2958 0x0\\n.debug_loc 19 0x0\\n.debug_abbrev 567 0x0\\n.debug_info 4929 0x0\\n.debug_ranges 40 0x0\\n.debug_macinfo 1 0x0\\n.debug_pubnames 2035 0x0\\n.debug_pubtypes 1892 0x0\\n.ARM.attributes 46 0x0\\n.debug_frame 100 0x0\\n.debug_line 867 0x0\\nTotal 14570 A refresher on ELF linker sections .text contains the program instructions .rodata contains constant values like strings .data contains statically allocated variables whose initial values are not zero .bss also contains statically allocated variables whose initial values are zero .vector_table is a non-standard section that we use to store the vector\\n(interrupt) table .ARM.attributes and the .debug_* sections contain metadata and will not be loaded onto the target when flashing the binary. IMPORTANT: ELF files contain metadata like debug information so their size\\non disk does not accurately reflect the space the program will occupy when\\nflashed on a device. Always use cargo-size to check how big a binary really\\nis. cargo-objdump can be used to disassemble the binary. cargo objdump --bin app --release -- --disassemble --no-show-raw-insn --print-imm-hex NOTE if the above command complains about Unknown command line argument see\\nthe following bug report: https://github.com/rust-embedded/book/issues/269 NOTE this output can differ on your system. New versions of rustc, LLVM\\nand libraries can generate different assembly. We truncated some of the instructions\\nto keep the snippet small. app: file format ELF32-arm-little Disassembly of section .text:\\nmain: 400: bl #0x256 404: b #-0x4 Reset: 406: bl #0x24e 40a: movw r0, #0x0 < .. truncated any more instructions .. > DefaultHandler_: 656: b #-0x4 UsageFault: 657: strb r7, [r4, #0x3] DefaultPreInit: 658: bx lr __pre_init: 659: strb r7, [r0, #0x1] __nop: 65a: bx lr HardFaultTrampoline: 65c: mrs r0, msp 660: b #-0x2 HardFault_: 662: b #-0x4 HardFault: 663: ","breadcrumbs":"Getting started » QEMU » Inspecting","id":"62","title":"Inspecting"},"63":{"body":"Next, let’s see how to run an embedded program on QEMU! This time we’ll use the hello example which actually does something. By default, this example uses [defmt]\\nand RTT to print text. NOTE defmt is a third-party dependency (i.e. non-core) widely used in the\\nEmbedded Rust ecosystem. In order to read and decode the messages produced by defmt in the host, we need to\\nswitch the RTT transport output to semihosting. When using real hardware this requires\\na debug session but when using QEMU this Just Works. Let’s switch the dependencies: cargo remove defmt-rtt\\ncargo add defmt-semihosting Open src/lib.rs and replace use defmt_rtt as _; by use defmt_semihosting as _; Now we can build the example: cargo build --bin hello The output binary will be located at target/thumbv7m-none-eabi/debug/hello. To run this binary on QEMU, the following command would be usually enough: qemu-system-arm \\\\ -cpu cortex-m3 \\\\ -machine lm3s6965evb \\\\ -nographic \\\\ -semihosting-config enable=on,target=native \\\\ -kernel target/thumbv7m-none-eabi/debug/hello In our case, since we use defmt, the host will not be able to decode the output. Instead, we\\nwill need a tool by Ferrous Systems named qemu-run: git clone git@github.com:knurling-rs/defmt.git\\ncd defmt/qemu-run/\\ncargo run -- --machine lm3s6965evb ../qemu-rs/target/thumbv7m-none-eabi/debug/hello Hello, world! The command should successfully exit (exit code = 0) after printing the text. On\\n*nix you can check that with the following command: echo $? 0 Let’s break down that QEMU command: qemu-system-arm. This is the QEMU emulator. There are a few variants of\\nthese QEMU binaries; this one does full system emulation of ARM machines\\nhence the name. -cpu cortex-m3. This tells QEMU to emulate a Cortex-M3 CPU. Specifying the\\nCPU model lets us catch some miscompilation errors: for example, running a\\nprogram compiled for the Cortex-M4F, which has a hardware FPU, will make QEMU\\nerror during its execution. -machine lm3s6965evb. This tells QEMU to emulate the LM3S6965EVB, an\\nevaluation board that contains a LM3S6965 microcontroller. -nographic. This tells QEMU to not launch its GUI. -semihosting-config (..). This tells QEMU to enable semihosting. Semihosting\\nlets the emulated device, among other things, use the host stdout, stderr and\\nstdin and create files on the host. -kernel $file. This tells QEMU which binary to load and run on the emulated\\nmachine. Typing out that long QEMU command is too much work! We can set a custom runner\\nto simplify the process. .cargo/config.toml has a commented out runner that invokes\\nQEMU; let’s uncomment it: head -n3 .cargo/config.toml [target.thumbv7m-none-eabi]\\n# uncomment this to make `cargo run` execute programs on QEMU\\nrunner = \\"qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel\\" This runner only applies to the thumbv7m-none-eabi target, which is our\\ndefault compilation target. Now cargo run will compile the program and run it\\non QEMU: cargo run --example hello --release Compiling app v0.1.0 (file:///tmp/app) Finished release [optimized + debuginfo] target(s) in 0.26s Running `qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel target/thumbv7m-none-eabi/release/examples/hello`\\nHello, world!","breadcrumbs":"Getting started » QEMU » Running","id":"63","title":"Running"},"64":{"body":"Debugging is critical to embedded development. Let’s see how it’s done. Debugging an embedded device involves remote debugging as the program that we\\nwant to debug won’t be running on the machine that’s running the debugger\\nprogram (GDB or LLDB). Remote debugging involves a client and a server. In a QEMU setup, the client\\nwill be a GDB (or LLDB) process and the server will be the QEMU process that’s\\nalso running the embedded program. In this section we’ll use the hello example we already compiled. The first debugging step is to launch QEMU in debugging mode: qemu-system-arm \\\\ -cpu cortex-m3 \\\\ -machine lm3s6965evb \\\\ -nographic \\\\ -semihosting-config enable=on,target=native \\\\ -gdb tcp::3333 \\\\ -S \\\\ -kernel target/thumbv7m-none-eabi/debug/examples/hello This command won’t print anything to the console and will block the terminal. We\\nhave passed two extra flags this time: -gdb tcp::3333. This tells QEMU to wait for a GDB connection on TCP\\nport 3333. -S. This tells QEMU to freeze the machine at startup. Without this the\\nprogram would have reached the end of main before we had a chance to launch\\nthe debugger! Next we launch GDB in another terminal and tell it to load the debug symbols of\\nthe example: gdb-multiarch -q target/thumbv7m-none-eabi/debug/examples/hello NOTE: you might need another version of gdb instead of gdb-multiarch depending\\non which one you installed in the installation chapter. This could also be arm-none-eabi-gdb or just gdb. Then within the GDB shell we connect to QEMU, which is waiting for a connection\\non TCP port 3333. target remote :3333 Remote debugging using :3333\\nReset () at $REGISTRY/cortex-m-rt-0.6.1/src/lib.rs:473\\n473 pub unsafe extern \\"C\\" fn Reset() -> ! { You’ll see that the process is halted and that the program counter is pointing\\nto a function named Reset. That is the reset handler: what Cortex-M cores\\nexecute upon booting. Note that on some setup, instead of displaying the line Reset () at $REGISTRY/cortex-m-rt-0.6.1/src/lib.rs:473 as shown above, gdb may print some warnings like : core::num::bignum::Big32x40::mul_small () at src/libcore/num/bignum.rs:254 src/libcore/num/bignum.rs: No such file or directory. That’s a known glitch. You can safely ignore those warnings, you’re most likely at Reset(). This reset handler will eventually call our main function. Let’s skip all the\\nway there using a breakpoint and the continue command. To set the breakpoint, let’s first take a look where we would like to break in our code, with the list command. list main This will show the source code, from the file examples/hello.rs. 6 use panic_halt as _;\\n7\\n8 use cortex_m_rt::entry;\\n9 use cortex_m_semihosting::{debug, hprintln};\\n10\\n11 #[entry]\\n12 fn main() -> ! {\\n13 hprintln!(\\"Hello, world!\\").unwrap();\\n14\\n15 // exit QEMU We would like to add a breakpoint just before the “Hello, world!”, which is on line 13. We do that with the break command: break 13 We can now instruct gdb to run up to our main function, with the continue command: continue Continuing. Breakpoint 1, hello::__cortex_m_rt_main () at examples\\\\hello.rs:13\\n13 hprintln!(\\"Hello, world!\\").unwrap(); We are now close to the code that prints “Hello, world!”. Let’s move forward\\nusing the next command. next 16 debug::exit(debug::EXIT_SUCCESS); At this point you should see “Hello, world!” printed on the terminal that’s\\nrunning qemu-system-arm. $ qemu-system-arm (..)\\nHello, world! Calling next again will terminate the QEMU process. next [Inferior 1 (Remote target) exited normally] You can now exit the GDB session. quit","breadcrumbs":"Getting started » QEMU » Debugging","id":"64","title":"Debugging"},"65":{"body":"By now you should be somewhat familiar with the tooling and the development\\nprocess. In this section we’ll switch to real hardware; the process will remain\\nlargely the same. Let’s dive in.","breadcrumbs":"Getting started » Hardware » Hardware","id":"65","title":"Hardware"},"66":{"body":"Before we begin you need to identify some characteristics of the target device\\nas these will be used to configure the project: The ARM core. e.g. Cortex-M3. Does the ARM core include an FPU? Cortex-M4 F and Cortex-M7 F cores do. How much Flash memory and RAM does the target device have? e.g. 256 KiB of\\nFlash and 32 KiB of RAM. Where are Flash memory and RAM mapped in the address space? e.g. RAM is\\ncommonly located at address 0x2000_0000. You can find this information in the data sheet or the reference manual of your\\ndevice. In this section we’ll be using our reference hardware, the STM32F3DISCOVERY.\\nThis board contains an STM32F303VCT6 microcontroller. This microcontroller has: A Cortex-M4F core that includes a single precision FPU 256 KiB of Flash located at address 0x0800_0000. 40 KiB of RAM located at address 0x2000_0000. (There’s another RAM region but\\nfor simplicity we’ll ignore it).","breadcrumbs":"Getting started » Hardware » Know your hardware","id":"66","title":"Know your hardware"},"67":{"body":"We’ll start from scratch with a fresh template instance. Refer to the previous section on QEMU for a refresher on how to do this without cargo-generate. $ cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart Project Name: app Creating project called `app`... Done! New project created /tmp/app $ cd app Step number one is to set a default compilation target in .cargo/config.toml. tail -n5 .cargo/config.toml # Pick ONE of these compilation targets\\n# target = \\"thumbv6m-none-eabi\\" # Cortex-M0 and Cortex-M0+\\n# target = \\"thumbv7m-none-eabi\\" # Cortex-M3\\n# target = \\"thumbv7em-none-eabi\\" # Cortex-M4 and Cortex-M7 (no FPU)\\ntarget = \\"thumbv7em-none-eabihf\\" # Cortex-M4F and Cortex-M7F (with FPU) We’ll use thumbv7em-none-eabihf as that covers the Cortex-M4F core. NOTE: As you may remember from the previous chapter, we have to install\\nall targets and this is a new one. So don’t forget to run the installation\\nprocess rustup target add thumbv7em-none-eabihf for this target. The second step is to enter the memory region information into the memory.x\\nfile. $ cat memory.x\\n/* Linker script for the STM32F303VCT6 */\\nMEMORY\\n{ /* NOTE 1 K = 1 KiBi = 1024 bytes */ FLASH : ORIGIN = 0x08000000, LENGTH = 256K RAM : ORIGIN = 0x20000000, LENGTH = 40K\\n} NOTE: If you for some reason changed the memory.x file after you had made\\nthe first build of a specific build target, then do cargo clean before cargo build, because cargo build may not track updates of memory.x. We’ll start with the hello example again, but first we have to make a small\\nchange. In examples/hello.rs, make sure the debug::exit() call is commented out or\\nremoved. It is used only for running in QEMU. #[entry]\\nfn main() -> ! { hprintln!(\\"Hello, world!\\").unwrap(); // exit QEMU // NOTE do not run this on hardware; it can corrupt OpenOCD state // debug::exit(debug::EXIT_SUCCESS); loop {}\\n} You can now cross compile programs using cargo build\\nand inspect the binaries using cargo-binutils as you did before. The cortex-m-rt crate handles all the magic required to get your chip running,\\nas helpfully, pretty much all Cortex-M CPUs boot in the same fashion. cargo build --example hello","breadcrumbs":"Getting started » Hardware » Configuring","id":"67","title":"Configuring"},"68":{"body":"Debugging will look a bit different. In fact, the first steps can look different\\ndepending on the target device. In this section we’ll show the steps required to\\ndebug a program running on the STM32F3DISCOVERY. This is meant to serve as a\\nreference; for device specific information about debugging check out the\\nDebugonomicon. As before we’ll do remote debugging and the client will be a GDB process. This\\ntime, however, the server will be OpenOCD. As done during the verify section connect the discovery board to your laptop /\\nPC and check that the ST-LINK header is populated. On a terminal run openocd to connect to the ST-LINK on the discovery board.\\nRun this command from the root of the template; openocd will pick up the openocd.cfg file which indicates which interface file and target file to use. cat openocd.cfg # Sample OpenOCD configuration for the STM32F3DISCOVERY development board # Depending on the hardware revision you got you\'ll have to pick ONE of these\\n# interfaces. At any time only one interface should be commented out. # Revision C (newer revision)\\nsource [find interface/stlink.cfg] # Revision A and B (older revisions)\\n# source [find interface/stlink-v2.cfg] source [find target/stm32f3x.cfg] NOTE If you found out that you have an older revision of the discovery\\nboard during the verify section then you should modify the openocd.cfg\\nfile at this point to use interface/stlink-v2.cfg. $ openocd\\nOpen On-Chip Debugger 0.10.0\\nLicensed under GNU GPL v2\\nFor bug reports, read http://openocd.org/doc/doxygen/bugs.html\\nInfo : auto-selecting first available session transport \\"hla_swd\\". To override use \'transport select \'.\\nadapter speed: 1000 kHz\\nadapter_nsrst_delay: 100\\nInfo : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD\\nnone separate\\nInfo : Unable to match requested speed 1000 kHz, using 950 kHz\\nInfo : Unable to match requested speed 1000 kHz, using 950 kHz\\nInfo : clock speed 950 kHz\\nInfo : STLINK v2 JTAG v27 API v2 SWIM v15 VID 0x0483 PID 0x374B\\nInfo : using stlink api v2\\nInfo : Target voltage: 2.913879\\nInfo : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints On another terminal run GDB, also from the root of the template. gdb-multiarch -q target/thumbv7em-none-eabihf/debug/examples/hello NOTE: like before you might need another version of gdb instead of gdb-multiarch depending\\non which one you installed in the installation chapter. This could also be arm-none-eabi-gdb or just gdb. Next connect GDB to OpenOCD, which is waiting for a TCP connection on port 3333. (gdb) target remote :3333\\nRemote debugging using :3333\\n0x00000000 in ?? () Now proceed to flash (load) the program onto the microcontroller using the load command. (gdb) load\\nLoading section .vector_table, size 0x400 lma 0x8000000\\nLoading section .text, size 0x1518 lma 0x8000400\\nLoading section .rodata, size 0x414 lma 0x8001918\\nStart address 0x08000400, load size 7468\\nTransfer rate: 13 KB/sec, 2489 bytes/write. The program is now loaded. This program uses semihosting so before we do any\\nsemihosting call we have to tell OpenOCD to enable semihosting. You can send\\ncommands to OpenOCD using the monitor command. (gdb) monitor arm semihosting enable\\nsemihosting is enabled You can see all the OpenOCD commands by invoking the monitor help command. Like before we can skip all the way to main using a breakpoint and the continue command. (gdb) break main\\nBreakpoint 1 at 0x8000490: file examples/hello.rs, line 11.\\nNote: automatically using hardware breakpoints for read-only addresses. (gdb) continue\\nContinuing. Breakpoint 1, hello::__cortex_m_rt_main_trampoline () at examples/hello.rs:11\\n11 #[entry] NOTE If GDB blocks the terminal instead of hitting the breakpoint after\\nyou issue the continue command above, you might want to double check that\\nthe memory region information in the memory.x file is correctly set up\\nfor your device (both the starts and lengths). Step into the main function with step. (gdb) step\\nhalted: PC: 0x08000496\\nhello::__cortex_m_rt_main () at examples/hello.rs:13\\n13 hprintln!(\\"Hello, world!\\").unwrap(); After advancing the program with next you should see “Hello, world!” printed on the OpenOCD console,\\namong other stuff. $ openocd\\n(..)\\nInfo : halted: PC: 0x08000502\\nHello, world!\\nInfo : halted: PC: 0x080004ac\\nInfo : halted: PC: 0x080004ae\\nInfo : halted: PC: 0x080004b0\\nInfo : halted: PC: 0x080004b4\\nInfo : halted: PC: 0x080004b8\\nInfo : halted: PC: 0x080004bc The message is only displayed once as the program is about to enter the infinite loop defined in line 19: loop {} You can now exit GDB using the quit command. (gdb) quit\\nA debugging session is active. Inferior 1 [Remote target] will be detached. Quit anyway? (y or n) Debugging now requires a few more steps so we have packed all those steps into a\\nsingle GDB script named openocd.gdb. The file was created during the cargo generate step, and should work without any modifications. Let’s have a peek: cat openocd.gdb target extended-remote :3333 # print demangled symbols\\nset print asm-demangle on # detect unhandled exceptions, hard faults and panics\\nbreak DefaultHandler\\nbreak HardFault\\nbreak rust_begin_unwind monitor arm semihosting enable load # start the process but immediately halt the processor\\nstepi Now running -x openocd.gdb target/thumbv7em-none-eabihf/debug/examples/hello will immediately connect GDB to\\nOpenOCD, enable semihosting, load the program and start the process. Alternatively, you can turn -x openocd.gdb into a custom runner to make cargo run build a program and start a GDB session. This runner is included\\nin .cargo/config.toml but it’s commented out. head -n10 .cargo/config.toml [target.thumbv7m-none-eabi]\\n# uncomment this to make `cargo run` execute programs on QEMU\\n# runner = \\"qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel\\" [target.\'cfg(all(target_arch = \\"arm\\", target_os = \\"none\\"))\']\\n# uncomment ONE of these three option to make `cargo run` start a GDB session\\n# which option to pick depends on your system\\nrunner = \\"arm-none-eabi-gdb -x openocd.gdb\\"\\n# runner = \\"gdb-multiarch -x openocd.gdb\\"\\n# runner = \\"gdb -x openocd.gdb\\" $ cargo run --example hello\\n(..)\\nLoading section .vector_table, size 0x400 lma 0x8000000\\nLoading section .text, size 0x1e70 lma 0x8000400\\nLoading section .rodata, size 0x61c lma 0x8002270\\nStart address 0x800144e, load size 10380\\nTransfer rate: 17 KB/sec, 3460 bytes/write.\\n(gdb)","breadcrumbs":"Getting started » Hardware » Debugging","id":"68","title":"Debugging"},"69":{"body":"Embedded systems can only get so far by executing normal Rust code and moving data around in RAM. If we want to get any information into or out of our system (be that blinking an LED, detecting a button press or communicating with an off-chip peripheral on some sort of bus) we’re going to have to dip into the world of Peripherals and their ‘memory mapped registers’. You may well find that the code you need to access the peripherals in your micro-controller has already been written, at one of the following levels: Micro-architecture Crate - This sort of crate handles any useful routines common to the processor core your microcontroller is using, as well as any peripherals that are common to all micro-controllers that use that particular type of processor core. For example the cortex-m crate gives you functions to enable and disable interrupts, which are the same for all Cortex-M based micro-controllers. It also gives you access to the ‘SysTick’ peripheral included with all Cortex-M based micro-controllers. Peripheral Access Crate (PAC) - This sort of crate is a thin wrapper over the various memory-wrapper registers defined for your particular part-number of micro-controller you are using. For example, tm4c123x for the Texas Instruments Tiva-C TM4C123 series, or stm32f30x for the ST-Micro STM32F30x series. Here, you’ll be interacting with the registers directly, following each peripheral’s operating instructions given in your micro-controller’s Technical Reference Manual. HAL Crate - These crates offer a more user-friendly API for your particular processor, often by implementing some common traits defined in embedded-hal. For example, this crate might offer a Serial struct, with a constructor that takes an appropriate set of GPIO pins and a baud rate, and offers some sort of write_byte function for sending data. See the chapter on Portability for more information on embedded-hal. Board Crate - These crates go one step further than a HAL Crate by pre-configuring various peripherals and GPIO pins to suit the specific developer kit or board you are using, such as stm32f3-discovery for the STM32F3DISCOVERY board.","breadcrumbs":"Getting started » Memory-mapped Registers » Memory Mapped Registers","id":"69","title":"Memory Mapped Registers"},"7":{"body":"This book generally assumes that you’re reading it front-to-back. Later\\nchapters build on concepts in earlier chapters, and earlier chapters may\\nnot dig into details on a topic, revisiting the topic in a later chapter. This book will be using the STM32F3DISCOVERY development board from\\nSTMicroelectronics for the majority of the examples contained within. This board\\nis based on the ARM Cortex-M architecture, and while basic functionality is\\nthe same across most CPUs based on this architecture, peripherals and other\\nimplementation details of Microcontrollers are different between different\\nvendors, and often even different between Microcontroller families from the same\\nvendor. For this reason, we suggest purchasing the STM32F3DISCOVERY development board\\nfor the purpose of following the examples in this book.","breadcrumbs":"Introduction » How to Use This Book","id":"7","title":"How to Use This Book"},"70":{"body":"A board crate is the perfect starting point, if you’re new to embedded Rust. They nicely abstract the HW details that might be overwhelming when starting studying this subject, and makes standard tasks easy, like turning a LED on or off. The functionality it exposes varies a lot between boards. Since this book aims at staying hardware agnostic, the board crates won’t be covered by this book. If you want to experiment with the STM32F3DISCOVERY board, it is highly recommended to take a look at the stm32f3-discovery board crate, which provides functionality to blink the board LEDs, access its compass, bluetooth and more. The Discovery book offers a great introduction to the use of a board crate. But if you’re working on a system that doesn’t yet have dedicated board crate, or you need functionality not provided by existing crates, read on as we start from the bottom, with the micro-architecture crates.","breadcrumbs":"Getting started » Memory-mapped Registers » Board Crate","id":"70","title":"Board Crate"},"71":{"body":"Let’s look at the SysTick peripheral that’s common to all Cortex-M based micro-controllers. We can find a pretty low-level API in the cortex-m crate, and we can use it like this: #![no_std]\\n#![no_main]\\nuse cortex_m::peripheral::{syst, Peripherals};\\nuse cortex_m_rt::entry;\\nuse panic_halt as _; #[entry]\\nfn main() -> ! { let peripherals = Peripherals::take().unwrap(); let mut systick = peripherals.SYST; systick.set_clock_source(syst::SystClkSource::Core); systick.set_reload(1_000); systick.clear_current(); systick.enable_counter(); while !systick.has_wrapped() { // Loop } loop {}\\n} The functions on the SYST struct map pretty closely to the functionality defined by the ARM Technical Reference Manual for this peripheral. There’s nothing in this API about ‘delaying for X milliseconds’ - we have to crudely implement that ourselves using a while loop. Note that we can’t access our SYST struct until we have called Peripherals::take() - this is a special routine that guarantees that there is only one SYST structure in our entire program. For more on that, see the Peripherals section.","breadcrumbs":"Getting started » Memory-mapped Registers » Micro-architecture crate","id":"71","title":"Micro-architecture crate"},"72":{"body":"We won’t get very far with our embedded software development if we restrict ourselves to only the basic peripherals included with every Cortex-M. At some point, we’re going to need to write some code that’s specific to the particular micro-controller we’re using. In this example, let’s assume we have an Texas Instruments TM4C123 - a middling 80MHz Cortex-M4 with 256 KiB of Flash. We’re going to pull in the tm4c123x crate to make use of this chip. #![no_std]\\n#![no_main] use panic_halt as _; // panic handler use cortex_m_rt::entry;\\nuse tm4c123x; #[entry]\\npub fn init() -> (Delay, Leds) { let cp = cortex_m::Peripherals::take().unwrap(); let p = tm4c123x::Peripherals::take().unwrap(); let pwm = p.PWM0; pwm.ctl.write(|w| w.globalsync0().clear_bit()); // Mode = 1 => Count up/down mode pwm._2_ctl.write(|w| w.enable().set_bit().mode().set_bit()); pwm._2_gena.write(|w| w.actcmpau().zero().actcmpad().one()); // 528 cycles (264 up and down) = 4 loops per video line (2112 cycles) pwm._2_load.write(|w| unsafe { w.load().bits(263) }); pwm._2_cmpa.write(|w| unsafe { w.compa().bits(64) }); pwm.enable.write(|w| w.pwm4en().set_bit());\\n} We’ve accessed the PWM0 peripheral in exactly the same way as we accessed the SYST peripheral earlier, except we called tm4c123x::Peripherals::take(). As this crate was auto-generated using svd2rust, the access functions for our register fields take a closure, rather than a numeric argument. While this looks like a lot of code, the Rust compiler can use it to perform a bunch of checks for us, but then generate machine-code which is pretty close to hand-written assembler! Where the auto-generated code isn’t able to determine that all possible arguments to a particular accessor function are valid (for example, if the SVD defines the register as 32-bit but doesn’t say if some of those 32-bit values have a special meaning), then the function is marked as unsafe. We can see this in the example above when setting the load and compa sub-fields using the bits() function.","breadcrumbs":"Getting started » Memory-mapped Registers » Using a Peripheral Access Crate (PAC)","id":"72","title":"Using a Peripheral Access Crate (PAC)"},"73":{"body":"The read() function returns an object which gives read-only access to the various sub-fields within this register, as defined by the manufacturer’s SVD file for this chip. You can find all the functions available on special R return type for this particular register, in this particular peripheral, on this particular chip, in the tm4c123x documentation. if pwm.ctl.read().globalsync0().is_set() { // Do a thing\\n}","breadcrumbs":"Getting started » Memory-mapped Registers » Reading","id":"73","title":"Reading"},"74":{"body":"The write() function takes a closure with a single argument. Typically we call this w. This argument then gives read-write access to the various sub-fields within this register, as defined by the manufacturer’s SVD file for this chip. Again, you can find all the functions available on the ‘w’ for this particular register, in this particular peripheral, on this particular chip, in the tm4c123x documentation. Note that all of the sub-fields that we do not set will be set to a default value for us - any existing content in the register will be lost. pwm.ctl.write(|w| w.globalsync0().clear_bit());","breadcrumbs":"Getting started » Memory-mapped Registers » Writing","id":"74","title":"Writing"},"75":{"body":"If we wish to change only one particular sub-field in this register and leave the other sub-fields unchanged, we can use the modify function. This function takes a closure with two arguments - one for reading and one for writing. Typically we call these r and w respectively. The r argument can be used to inspect the current contents of the register, and the w argument can be used to modify the register contents. pwm.ctl.modify(|r, w| w.globalsync0().clear_bit()); The modify function really shows the power of closures here. In C, we’d have to read into some temporary value, modify the correct bits and then write the value back. This means there’s considerable scope for error: uint32_t temp = pwm0.ctl.read();\\ntemp |= PWM0_CTL_GLOBALSYNC0;\\npwm0.ctl.write(temp);\\nuint32_t temp2 = pwm0.enable.read();\\ntemp2 |= PWM0_ENABLE_PWM4EN;\\npwm0.enable.write(temp); // Uh oh! Wrong variable!","breadcrumbs":"Getting started » Memory-mapped Registers » Modifying","id":"75","title":"Modifying"},"76":{"body":"The HAL crate for a chip typically works by implementing a custom Trait for the raw structures exposed by the PAC. Often this trait will define a function called constrain() for single peripherals or split() for things like GPIO ports with multiple pins. This function will consume the underlying raw peripheral structure and return a new object with a higher-level API. This API may also do things like have the Serial port new function require a borrow on some Clock structure, which can only be generated by calling the function which configures the PLLs and sets up all the clock frequencies. In this way, it is statically impossible to create a Serial port object without first having configured the clock rates, or for the Serial port object to misconvert the baud rate into clock ticks. Some crates even define special traits for the states each GPIO pin can be in, requiring the user to put a pin into the correct state (say, by selecting the appropriate Alternate Function Mode) before passing the pin into Peripheral. All with no run-time cost! Let’s see an example: #![no_std]\\n#![no_main] use panic_halt as _; // panic handler use cortex_m_rt::entry;\\nuse tm4c123x_hal as hal;\\nuse tm4c123x_hal::prelude::*;\\nuse tm4c123x_hal::serial::{NewlineMode, Serial};\\nuse tm4c123x_hal::sysctl; #[entry]\\nfn main() -> ! { let p = hal::Peripherals::take().unwrap(); let cp = hal::CorePeripherals::take().unwrap(); // Wrap up the SYSCTL struct into an object with a higher-layer API let mut sc = p.SYSCTL.constrain(); // Pick our oscillation settings sc.clock_setup.oscillator = sysctl::Oscillator::Main( sysctl::CrystalFrequency::_16mhz, sysctl::SystemClock::UsePll(sysctl::PllOutputFrequency::_80_00mhz), ); // Configure the PLL with those settings let clocks = sc.clock_setup.freeze(); // Wrap up the GPIO_PORTA struct into an object with a higher-layer API. // Note it needs to borrow `sc.power_control` so it can power up the GPIO // peripheral automatically. let mut porta = p.GPIO_PORTA.split(&sc.power_control); // Activate the UART. let uart = Serial::uart0( p.UART0, // The transmit pin porta .pa1 .into_af_push_pull::(&mut porta.control), // The receive pin porta .pa0 .into_af_push_pull::(&mut porta.control), // No RTS or CTS required (), (), // The baud rate 115200_u32.bps(), // Output handling NewlineMode::SwapLFtoCRLF, // We need the clock rates to calculate the baud rate divisors &clocks, // We need this to power up the UART peripheral &sc.power_control, ); loop { writeln!(uart, \\"Hello, World!\\\\r\\\\n\\").unwrap(); }\\n}","breadcrumbs":"Getting started » Memory-mapped Registers » Using a HAL crate","id":"76","title":"Using a HAL crate"},"77":{"body":"Semihosting is a mechanism that lets embedded devices do I/O on the host and is\\nmainly used to log messages to the host console. Semihosting requires a debug\\nsession and pretty much nothing else (no extra wires!) so it’s super convenient\\nto use. The downside is that it’s super slow: each write operation can take\\nseveral milliseconds depending on the hardware debugger (e.g. ST-Link) you use. The cortex-m-semihosting crate provides an API to do semihosting operations\\non Cortex-M devices. The program below is the semihosting version of “Hello,\\nworld!”: #![no_main]\\n#![no_std] use panic_halt as _; use cortex_m_rt::entry;\\nuse cortex_m_semihosting::hprintln; #[entry]\\nfn main() -> ! { hprintln!(\\"Hello, world!\\").unwrap(); loop {}\\n} If you run this program on hardware you’ll see the “Hello, world!” message\\nwithin the OpenOCD logs. $ openocd\\n(..)\\nHello, world!\\n(..) You do need to enable semihosting in OpenOCD from GDB first: (gdb) monitor arm semihosting enable\\nsemihosting is enabled QEMU understands semihosting operations so the above program will also work with qemu-system-arm without having to start a debug session. Note that you’ll\\nneed to pass the -semihosting-config flag to QEMU to enable semihosting\\nsupport; these flags are already included in the .cargo/config.toml file of the\\ntemplate. $ # this program will block the terminal\\n$ cargo run Running `qemu-system-arm (..)\\nHello, world! There’s also an exit semihosting operation that can be used to terminate the\\nQEMU process. Important: do not use debug::exit on hardware; this function\\ncan corrupt your OpenOCD session and you will not be able to debug more programs\\nuntil you restart it. #![no_main]\\n#![no_std] use panic_halt as _; use cortex_m_rt::entry;\\nuse cortex_m_semihosting::debug; #[entry]\\nfn main() -> ! { let roses = \\"blue\\"; if roses == \\"red\\" { debug::exit(debug::EXIT_SUCCESS); } else { debug::exit(debug::EXIT_FAILURE); } loop {}\\n} $ cargo run Running `qemu-system-arm (..) $ echo $?\\n1 One last tip: you can set the panicking behavior to exit(EXIT_FAILURE). This\\nwill let you write no_std run-pass tests that you can run on QEMU. For convenience, the panic-semihosting crate has an “exit” feature that when\\nenabled invokes exit(EXIT_FAILURE) after logging the panic message to the host\\nstderr. #![no_main]\\n#![no_std] use panic_semihosting as _; // features = [\\"exit\\"] use cortex_m_rt::entry;\\nuse cortex_m_semihosting::debug; #[entry]\\nfn main() -> ! { let roses = \\"blue\\"; assert_eq!(roses, \\"red\\"); loop {}\\n} $ cargo run Running `qemu-system-arm (..)\\npanicked at \'assertion failed: `(left == right)` left: `\\"blue\\"`, right: `\\"red\\"`\', examples/hello.rs:15:5 $ echo $?\\n1 NOTE: To enable this feature on panic-semihosting, edit your Cargo.toml dependencies section where panic-semihosting is specified with: panic-semihosting = { version = \\"VERSION\\", features = [\\"exit\\"] } where VERSION is the version desired. For more information on dependencies\\nfeatures check the specifying dependencies section of the Cargo book.","breadcrumbs":"Getting started » Semihosting » Semihosting","id":"77","title":"Semihosting"},"78":{"body":"Panicking is a core part of the Rust language. Built-in operations like indexing\\nare runtime checked for memory safety. When out of bounds indexing is attempted\\nthis results in a panic. In the standard library panicking has a defined behavior: it unwinds the stack\\nof the panicking thread, unless the user opted for aborting the program on\\npanics. In programs without standard library, however, the panicking behavior is left\\nundefined. A behavior can be chosen by declaring a #[panic_handler] function.\\nThis function must appear exactly once in the dependency graph of a program,\\nand must have the following signature: fn(&PanicInfo) -> !, where PanicInfo\\nis a struct containing information about the location of the panic. Given that embedded systems range from user facing to safety critical (cannot\\ncrash) there’s no one size fits all panicking behavior but there are plenty of\\ncommonly used behaviors. These common behaviors have been packaged into crates\\nthat define the #[panic_handler] function. Some examples include: panic-abort. A panic causes the abort instruction to be executed. panic-halt. A panic causes the program, or the current thread, to halt by\\nentering an infinite loop. panic-itm. The panicking message is logged using the ITM, an ARM Cortex-M\\nspecific peripheral. panic-semihosting. The panicking message is logged to the host using the\\nsemihosting technique. You may be able to find even more crates searching for the panic-handler\\nkeyword on crates.io. A program can pick one of these behaviors simply by linking to the corresponding\\ncrate. The fact that the panicking behavior is expressed in the source of\\nan application as a single line of code is not only useful as documentation but\\ncan also be used to change the panicking behavior according to the compilation\\nprofile. For example: #![no_main]\\n#![no_std] // dev profile: easier to debug panics; can put a breakpoint on `rust_begin_unwind`\\n#[cfg(debug_assertions)]\\nuse panic_halt as _; // release profile: minimize the binary size of the application\\n#[cfg(not(debug_assertions))]\\nuse panic_abort as _; // .. In this example the crate links to the panic-halt crate when built with the\\ndev profile ( cargo build), but links to the panic-abort crate when built\\nwith the release profile ( cargo build --release). The use panic_abort as _; form of the use statement is used to ensure the panic_abort panic handler is\\nincluded in our final executable while making it clear to the compiler that we won’t explicitly use anything from\\nthe crate. Without the as _ rename, the compiler would warn that we have an unused import.\\nSometimes you might see extern crate panic_abort instead, which is an older style used before the\\n2018 edition of Rust, and should now only be used for “sysroot” crates (those distributed with Rust itself) such\\nas proc_macro, alloc, std, and test.","breadcrumbs":"Getting started » Panicking » Panicking","id":"78","title":"Panicking"},"79":{"body":"Here’s an example that tries to index an array beyond its length. The operation\\nresults in a panic. #![no_main]\\n#![no_std] use panic_semihosting as _; use cortex_m_rt::entry; #[entry]\\nfn main() -> ! { let xs = [0, 1, 2]; let i = xs.len(); let _y = xs[i]; // out of bounds access loop {}\\n} This example chose the panic-semihosting behavior which prints the panic\\nmessage to the host console using semihosting. $ cargo run Running `qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb (..)\\npanicked at \'index out of bounds: the len is 3 but the index is 4\', src/main.rs:12:13 You can try changing the behavior to panic-halt and confirm that no message is\\nprinted in that case.","breadcrumbs":"Getting started » Panicking » An example","id":"79","title":"An example"},"8":{"body":"The work on this book is coordinated in this repository and is mainly\\ndeveloped by the resources team. If you have trouble following the instructions in this book or find that some\\nsection of the book is not clear enough or hard to follow then that’s a bug and\\nit should be reported in the issue tracker of this book. Pull requests fixing typos and adding new content are very welcome!","breadcrumbs":"Introduction » Contributing to This Book","id":"8","title":"Contributing to This Book"},"80":{"body":"Exceptions, and interrupts, are a hardware mechanism by which the processor\\nhandles asynchronous events and fatal errors (e.g. executing an invalid\\ninstruction). Exceptions imply preemption and involve exception handlers,\\nsubroutines executed in response to the signal that triggered the event. The cortex-m-rt crate provides an exception attribute to declare exception\\nhandlers. // Exception handler for the SysTick (System Timer) exception\\n#[exception]\\nfn SysTick() { // ..\\n} Other than the exception attribute exception handlers look like plain\\nfunctions but there’s one more difference: exception handlers can not be\\ncalled by software. Following the previous example, the statement SysTick();\\nwould result in a compilation error. This behavior is pretty much intended and it’s required to provide a feature: static mut variables declared inside exception handlers are safe to use. #[exception]\\nfn SysTick() { static mut COUNT: u32 = 0; // `COUNT` has transformed to type `&mut u32` and it\'s safe to use *COUNT += 1;\\n} As you may know, using static mut variables in a function makes it non-reentrant. It’s undefined behavior to call a non-reentrant function,\\ndirectly or indirectly, from more than one exception / interrupt handler or from main and one or more exception / interrupt handlers. Safe Rust must never result in undefined behavior so non-reentrant functions\\nmust be marked as unsafe. Yet I just told that exception handlers can safely\\nuse static mut variables. How is this possible? This is possible because exception handlers can not be called by software thus reentrancy is not\\npossible. These handlers are called by the hardware itself which is assumed to be physically non-concurrent. As a result, in the context of exception handlers in embedded systems, the absence of concurrent invocations of the same handler ensures that there are no reentrancy issues, even if the handler uses static mutable variables. In a multicore system, where multiple processor cores are executing code concurrently, the potential for reentrancy issues becomes relevant again, even within exception handlers. While each core may have its own set of exception handlers, there can still be scenarios where multiple cores attempt to execute the same exception handler simultaneously. To address this concern in a multicore environment, proper synchronization mechanisms need to be employed within the exception handlers to ensure that access to shared resources is properly coordinated among the cores. This typically involves the use of techniques such as locks, semaphores, or atomic operations to prevent data races and maintain data integrity Note that the exception attribute transforms definitions of static variables\\ninside the function by wrapping them into unsafe blocks and providing us\\nwith new appropriate variables of type &mut of the same name.\\nThus we can dereference the reference via * to access the values of the variables without\\nneeding to wrap them in an unsafe block.","breadcrumbs":"Getting started » Exceptions » Exceptions","id":"80","title":"Exceptions"},"81":{"body":"Here’s an example that uses the system timer to raise a SysTick exception\\nroughly every second. The SysTick exception handler keeps track of how many\\ntimes it has been called in the COUNT variable and then prints the value of COUNT to the host console using semihosting. NOTE: You can run this example on any Cortex-M device; you can also run it\\non QEMU #![deny(unsafe_code)]\\n#![no_main]\\n#![no_std] use panic_halt as _; use core::fmt::Write; use cortex_m::peripheral::syst::SystClkSource;\\nuse cortex_m_rt::{entry, exception};\\nuse cortex_m_semihosting::{ debug, hio::{self, HostStream},\\n}; #[entry]\\nfn main() -> ! { let p = cortex_m::Peripherals::take().unwrap(); let mut syst = p.SYST; // configures the system timer to trigger a SysTick exception every second syst.set_clock_source(SystClkSource::Core); // this is configured for the LM3S6965 which has a default CPU clock of 12 MHz syst.set_reload(12_000_000); syst.clear_current(); syst.enable_counter(); syst.enable_interrupt(); loop {}\\n} #[exception]\\nfn SysTick() { static mut COUNT: u32 = 0; static mut STDOUT: Option = None; *COUNT += 1; // Lazy initialization if STDOUT.is_none() { *STDOUT = hio::hstdout().ok(); } if let Some(hstdout) = STDOUT.as_mut() { write!(hstdout, \\"{}\\", *COUNT).ok(); } // IMPORTANT omit this `if` block if running on real hardware or your // debugger will end in an inconsistent state if *COUNT == 9 { // This will terminate the QEMU process debug::exit(debug::EXIT_SUCCESS); }\\n} tail -n5 Cargo.toml [dependencies]\\ncortex-m = \\"0.5.7\\"\\ncortex-m-rt = \\"0.6.3\\"\\npanic-halt = \\"0.2.0\\"\\ncortex-m-semihosting = \\"0.3.1\\" $ cargo run --release Running `qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb (..)\\n123456789 If you run this on the Discovery board you’ll see the output on the OpenOCD\\nconsole. Also, the program will not stop when the count reaches 9.","breadcrumbs":"Getting started » Exceptions » A complete example","id":"81","title":"A complete example"},"82":{"body":"What the exception attribute actually does is override the default exception\\nhandler for a specific exception. If you don’t override the handler for a\\nparticular exception it will be handled by the DefaultHandler function, which\\ndefaults to: fn DefaultHandler() { loop {}\\n} This function is provided by the cortex-m-rt crate and marked as #[no_mangle] so you can put a breakpoint on “DefaultHandler” and catch unhandled exceptions. It’s possible to override this DefaultHandler using the exception attribute: #[exception]\\nfn DefaultHandler(irqn: i16) { // custom default handler\\n} The irqn argument indicates which exception is being serviced. A negative\\nvalue indicates that a Cortex-M exception is being serviced; and zero or a\\npositive value indicate that a device specific exception, AKA interrupt, is\\nbeing serviced.","breadcrumbs":"Getting started » Exceptions » The default exception handler","id":"82","title":"The default exception handler"},"83":{"body":"The HardFault exception is a bit special. This exception is fired when the\\nprogram enters an invalid state so its handler can not return as that could\\nresult in undefined behavior. Also, the runtime crate does a bit of work before\\nthe user defined HardFault handler is invoked to improve debuggability. The result is that the HardFault handler must have the following signature: fn(&ExceptionFrame) -> !. The argument of the handler is a pointer to\\nregisters that were pushed into the stack by the exception. These registers are\\na snapshot of the processor state at the moment the exception was triggered and\\nare useful to diagnose a hard fault. Here’s an example that performs an illegal operation: a read to a nonexistent\\nmemory location. NOTE: This program won’t work, i.e. it won’t crash, on QEMU because qemu-system-arm -machine lm3s6965evb doesn’t check memory loads and will\\nhappily return 0 on reads to invalid memory. #![no_main]\\n#![no_std] use panic_halt as _; use core::fmt::Write;\\nuse core::ptr; use cortex_m_rt::{entry, exception, ExceptionFrame};\\nuse cortex_m_semihosting::hio; #[entry]\\nfn main() -> ! { // read a nonexistent memory location unsafe { ptr::read_volatile(0x3FFF_0000 as *const u32); } loop {}\\n} #[exception]\\nfn HardFault(ef: &ExceptionFrame) -> ! { if let Ok(mut hstdout) = hio::hstdout() { writeln!(hstdout, \\"{:#?}\\", ef).ok(); } loop {}\\n} The HardFault handler prints the ExceptionFrame value. If you run this\\nyou’ll see something like this on the OpenOCD console. $ openocd\\n(..)\\nExceptionFrame { r0: 0x3fff0000, r1: 0x00000003, r2: 0x080032e8, r3: 0x00000000, r12: 0x00000000, lr: 0x080016df, pc: 0x080016e2, xpsr: 0x61000000,\\n} The pc value is the value of the Program Counter at the time of the exception\\nand it points to the instruction that triggered the exception. If you look at the disassembly of the program: $ cargo objdump --bin app --release -- -d --no-show-raw-insn --print-imm-hex\\n(..)\\nResetTrampoline: 8000942: movw r0, #0xfffe 8000946: movt r0, #0x3fff 800094a: ldr r0, [r0] 800094c: b #-0x4 You can lookup the value of the program counter 0x0800094a in the disassembly.\\nYou’ll see that a load operation ( ldr r0, [r0] ) caused the exception.\\nThe r0 field of ExceptionFrame will tell you the value of register r0\\nwas 0x3fff_fffe at that time.","breadcrumbs":"Getting started » Exceptions » The hard fault handler","id":"83","title":"The hard fault handler"},"84":{"body":"Interrupts differ from exceptions in a variety of ways but their operation and\\nuse is largely similar and they are also handled by the same interrupt\\ncontroller. Whereas exceptions are defined by the Cortex-M architecture,\\ninterrupts are always vendor (and often even chip) specific implementations,\\nboth in naming and functionality. Interrupts do allow for a lot of flexibility which needs to be accounted for\\nwhen attempting to use them in an advanced way. We will not cover those uses in\\nthis book, however it is a good idea to keep the following in mind: Interrupts have programmable priorities which determine their handlers’ execution order Interrupts can nest and preempt, i.e. execution of an interrupt handler might be interrupted by another higher-priority interrupt In general the reason causing the interrupt to trigger needs to be cleared to prevent re-entering the interrupt handler endlessly The general initialization steps at runtime are always the same: Setup the peripheral(s) to generate interrupts requests at the desired occasions Set the desired priority of the interrupt handler in the interrupt controller Enable the interrupt handler in the interrupt controller Similarly to exceptions, the cortex-m-rt crate exposes an interrupt attribute for declaring interrupt handlers. However, this\\nattribute is only available when the device feature is enabled. That said, this attribute is not intended to be used directly—doing\\nso will result in a compilation error. Instead, you should use the re-exported version of the interrupt attribute provided by the device crate (usually generated using svd2rust).\\nThis ensures that the compiler can verify that the interrupt actually exists on the target device. The list of available interrupts—and\\ntheir position in the interrupt vector table—is typically auto-generated from an SVD file by svd2rust. use lm3s6965::interrupt; // Re-exported attribute from the device crate // Interrupt handler for the Timer2 interrupt\\n#[interrupt]\\nfn TIMER2A() { // .. // Clear reason for the generated interrupt request\\n} Interrupt handlers look like plain functions (except for the lack of arguments)\\nsimilar to exception handlers. However they can not be called directly by other\\nparts of the firmware due to the special calling conventions. It is however\\npossible to generate interrupt requests in software to trigger a diversion to\\nthe interrupt handler. Similar to exception handlers it is also possible to declare static mut\\nvariables inside the interrupt handlers for safe state keeping. #[interrupt]\\nfn TIMER2A() { static mut COUNT: u32 = 0; // `COUNT` has type `&mut u32` and it\'s safe to use *COUNT += 1;\\n} For a more detailed description about the mechanisms demonstrated here please\\nrefer to the exceptions section.","breadcrumbs":"Getting started » Interrupts » Interrupts","id":"84","title":"Interrupts"},"85":{"body":"","breadcrumbs":"Peripherals » Peripherals","id":"85","title":"Peripherals"},"86":{"body":"Most Microcontrollers have more than just a CPU, RAM, or Flash Memory - they contain sections of silicon which are used for interacting with systems outside of the microcontroller, as well as directly and indirectly interacting with their surroundings in the world via sensors, motor controllers, or human interfaces such as a display or keyboard. These components are collectively known as Peripherals. These peripherals are useful because they allow a developer to offload processing to them, avoiding having to handle everything in software. Similar to how a desktop developer would offload graphics processing to a video card, embedded developers can offload some tasks to peripherals allowing the CPU to spend its time doing something else important, or doing nothing in order to save power. If you look at the main circuit board in an old-fashioned home computer from the 1970s or 1980s (and actually, the desktop PCs of yesterday are not so far removed from the embedded systems of today) you would expect to see: A processor A RAM chip A ROM chip An I/O controller The RAM chip, ROM chip and I/O controller (the peripheral in this system) would be joined to the processor through a series of parallel traces known as a ‘bus’. This bus carries address information, which selects which device on the bus the processor wishes to communicate with, and a data bus which carries the actual data. In our embedded microcontrollers, the same principles apply - it’s just that everything is packed on to a single piece of silicon. However, unlike graphics cards, which typically have a Software API like Vulkan, Metal, or OpenGL, peripherals are exposed to our Microcontroller with a hardware interface, which is mapped to a chunk of the memory.","breadcrumbs":"Peripherals » What are Peripherals?","id":"86","title":"What are Peripherals?"},"87":{"body":"On a microcontroller, writing some data to some other arbitrary address, such as 0x4000_0000 or 0x0000_0000, may also be a completely valid action. On a desktop system, access to memory is tightly controlled by the MMU, or Memory Management Unit. This component has two major responsibilities: enforcing access permission to sections of memory (preventing one process from reading or modifying the memory of another process); and re-mapping segments of the physical memory to virtual memory ranges used in software. Microcontrollers do not typically have an MMU, and instead only use real physical addresses in software. Although 32 bit microcontrollers have a real and linear address space from 0x0000_0000, and 0xFFFF_FFFF, they generally only use a few hundred kilobytes of that range for actual memory. This leaves a significant amount of address space remaining. In earlier chapters, we were talking about RAM being located at address 0x2000_0000. If our RAM was 64 KiB long (i.e. with a maximum address of 0xFFFF) then addresses 0x2000_0000 to 0x2000_FFFF would correspond to our RAM. When we write to a variable which lives at address 0x2000_1234, what happens internally is that some logic detects the upper portion of the address (0x2000 in this example) and then activates the RAM so that it can act upon the lower portion of the address (0x1234 in this case). On a Cortex-M we also have our Flash ROM mapped in at address 0x0000_0000 up to, say, address 0x0007_FFFF (if we have a 512 KiB Flash ROM). Rather than ignore all remaining space between these two regions, Microcontroller designers instead mapped the interface for peripherals in certain memory locations. This ends up looking something like this: Nordic nRF52832 Datasheet (pdf)","breadcrumbs":"Peripherals » Linear and Real Memory Space","id":"87","title":"Linear and Real Memory Space"},"88":{"body":"Interaction with these peripherals is simple at a first glance - write the right data to the correct address. For example, sending a 32 bit word over a serial port could be as direct as writing that 32 bit word to a certain memory address. The Serial Port Peripheral would then take over and send out the data automatically. Configuration of these peripherals works similarly. Instead of calling a function to configure a peripheral, a chunk of memory is exposed which serves as the hardware API. Write 0x8000_0000 to a SPI Frequency Configuration Register, and the SPI port will send data at 8 Megabits per second. Write 0x0200_0000 to the same address, and the SPI port will send data at 125 Kilobits per second. These configuration registers look a little bit like this: Nordic nRF52832 Datasheet (pdf) This interface is how interactions with the hardware are made, no matter what language is used, whether that language is Assembly, C, or Rust.","breadcrumbs":"Peripherals » Memory Mapped Peripherals","id":"88","title":"Memory Mapped Peripherals"},"89":{"body":"","breadcrumbs":"Peripherals » A first attempt in Rust » A First Attempt","id":"89","title":"A First Attempt"},"9":{"body":"This book is distributed under the following licenses: The code samples and free-standing Cargo projects contained within this book are licensed under the terms of both the MIT License and the Apache License v2.0. The written prose, pictures and diagrams contained within this book are licensed under the terms of the Creative Commons CC-BY-SA v4.0 license. TL;DR: If you want to use our text or images in your work, you need to: Give the appropriate credit (i.e. mention this book on your slide, and provide a link to the relevant page) Provide a link to the CC-BY-SA v4.0 licence Indicate if you have changed the material in any way, and make any changes to our material available under the same licence Also, please do let us know if you find this book useful!","breadcrumbs":"Introduction » Re-using this material","id":"9","title":"Re-using this material"},"90":{"body":"Let’s look at the ‘SysTick’ peripheral - a simple timer which comes with every Cortex-M processor core. Typically you’ll be looking these up in the chip manufacturer’s data sheet or Technical Reference Manual, but this example is common to all ARM Cortex-M cores, let’s look in the ARM reference manual. We see there are four registers: Offset Name Description Width 0x00 SYST_CSR Control and Status Register 32 bits 0x04 SYST_RVR Reload Value Register 32 bits 0x08 SYST_CVR Current Value Register 32 bits 0x0C SYST_CALIB Calibration Value Register 32 bits","breadcrumbs":"Peripherals » A first attempt in Rust » The Registers","id":"90","title":"The Registers"},"91":{"body":"In Rust, we can represent a collection of registers in exactly the same way as we do in C - with a struct. #[repr(C)]\\nstruct SysTick { pub csr: u32, pub rvr: u32, pub cvr: u32, pub calib: u32,\\n} The qualifier #[repr(C)] tells the Rust compiler to lay this structure out like a C compiler would. That’s very important, as Rust allows structure fields to be re-ordered, while C does not. You can imagine the debugging we’d have to do if these fields were silently re-arranged by the compiler! With this qualifier in place, we have our four 32-bit fields which correspond to the table above. But of course, this struct is of no use by itself - we need a variable. let systick = 0xE000_E010 as *mut SysTick;\\nlet time = unsafe { (*systick).cvr };","breadcrumbs":"Peripherals » A first attempt in Rust » The C Approach","id":"91","title":"The C Approach"},"92":{"body":"Now, there are a couple of problems with the approach above. We have to use unsafe every time we want to access our Peripheral. We’ve got no way of specifying which registers are read-only or read-write. Any piece of code anywhere in your program could access the hardware\\nthrough this structure. Most importantly, it doesn’t actually work… Now, the problem is that compilers are clever. If you make two writes to the same piece of RAM, one after the other, the compiler can notice this and just skip the first write entirely. In C, we can mark variables as volatile to ensure that every read or write occurs as intended. In Rust, we instead mark the accesses as volatile, not the variable. let systick = unsafe { &mut *(0xE000_E010 as *mut SysTick) };\\nlet time = unsafe { core::ptr::read_volatile(&mut systick.cvr) }; So, we’ve fixed one of our four problems, but now we have even more unsafe code! Fortunately, there’s a third party crate which can help - volatile_register. use volatile_register::{RW, RO}; #[repr(C)]\\nstruct SysTick { pub csr: RW, pub rvr: RW, pub cvr: RW, pub calib: RO,\\n} fn get_systick() -> &\'static mut SysTick { unsafe { &mut *(0xE000_E010 as *mut SysTick) }\\n} fn get_time() -> u32 { let systick = get_systick(); systick.cvr.read()\\n} Now, the volatile accesses are performed automatically through the read and write methods. It’s still unsafe to perform writes, but to be fair, hardware is a bunch of mutable state and there’s no way for the compiler to know whether these writes are actually safe, so this is a good default position.","breadcrumbs":"Peripherals » A first attempt in Rust » Volatile Accesses","id":"92","title":"Volatile Accesses"},"93":{"body":"We need to wrap this struct up into a higher-layer API that is safe for our users to call. As the driver author, we manually verify the unsafe code is correct, and then present a safe API for our users so they don’t have to worry about it (provided they trust us to get it right!). One example might be: use volatile_register::{RW, RO}; pub struct SystemTimer { p: &\'static mut RegisterBlock\\n} #[repr(C)]\\nstruct RegisterBlock { pub csr: RW, pub rvr: RW, pub cvr: RW, pub calib: RO,\\n} impl SystemTimer { pub fn new() -> SystemTimer { SystemTimer { p: unsafe { &mut *(0xE000_E010 as *mut RegisterBlock) } } } pub fn get_time(&self) -> u32 { self.p.cvr.read() } pub fn set_reload(&mut self, reload_value: u32) { unsafe { self.p.rvr.write(reload_value) } }\\n} pub fn example_usage() -> String { let mut st = SystemTimer::new(); st.set_reload(0x00FF_FFFF); format!(\\"Time is now 0x{:08x}\\", st.get_time())\\n} Now, the problem with this approach is that the following code is perfectly acceptable to the compiler: fn thread1() { let mut st = SystemTimer::new(); st.set_reload(2000);\\n} fn thread2() { let mut st = SystemTimer::new(); st.set_reload(1000);\\n} Our &mut self argument to the set_reload function checks that there are no other references to that particular SystemTimer struct, but they don’t stop the user creating a second SystemTimer which points to the exact same peripheral! Code written in this fashion will work if the author is diligent enough to spot all of these ‘duplicate’ driver instances, but once the code is spread out over multiple modules, drivers, developers, and days, it gets easier and easier to make these kinds of mistakes.","breadcrumbs":"Peripherals » A first attempt in Rust » The Rusty Wrapper","id":"93","title":"The Rusty Wrapper"},"94":{"body":"Unfortunately, hardware is basically nothing but mutable global state, which can feel very frightening for a Rust developer. Hardware exists independently from the structures of the code we write, and can be modified at any time by the real world.","breadcrumbs":"Peripherals » The Borrow Checker » Mutable Global State","id":"94","title":"Mutable Global State"},"95":{"body":"How can we reliably interact with these peripherals? Always use volatile methods to read or write to peripheral memory, as it can change at any time In software, we should be able to share any number of read-only accesses to these peripherals If some software should have read-write access to a peripheral, it should hold the only reference to that peripheral","breadcrumbs":"Peripherals » The Borrow Checker » What should our rules be?","id":"95","title":"What should our rules be?"},"96":{"body":"The last two of these rules sound suspiciously similar to what the Borrow Checker does already! Imagine if we could pass around ownership of these peripherals, or offer immutable or mutable references to them? Well, we can, but for the Borrow Checker, we need to have exactly one instance of each peripheral, so Rust can handle this correctly. Well, luckily in the hardware, there is only one instance of any given peripheral, but how can we expose that in the structure of our code?","breadcrumbs":"Peripherals » The Borrow Checker » The Borrow Checker","id":"96","title":"The Borrow Checker"},"97":{"body":"In software engineering, the singleton pattern is a software design pattern that restricts the instantiation of a class to one object. Wikipedia: Singleton Pattern","breadcrumbs":"Peripherals » Singletons » Singletons","id":"97","title":"Singletons"},"98":{"body":"We could make everything a public static, like this static mut THE_SERIAL_PORT: SerialPort = SerialPort; fn main() { let _ = unsafe { THE_SERIAL_PORT.read_speed(); };\\n} But this has a few problems. It is a mutable global variable, and in Rust, these are always unsafe to interact with. These variables are also visible across your whole program, which means the borrow checker is unable to help you track references and ownership of these variables.","breadcrumbs":"Peripherals » Singletons » But why can’t we just use global variable(s)?","id":"98","title":"But why can’t we just use global variable(s)?"},"99":{"body":"Instead of just making our peripheral a global variable, we might instead decide to make a structure, in this case called PERIPHERALS, which contains an Option for each of our peripherals. struct Peripherals { serial: Option,\\n}\\nimpl Peripherals { fn take_serial(&mut self) -> SerialPort { let p = replace(&mut self.serial, None); p.unwrap() }\\n}\\nstatic mut PERIPHERALS: Peripherals = Peripherals { serial: Some(SerialPort),\\n}; This structure allows us to obtain a single instance of our peripheral. If we try to call take_serial() more than once, our code will panic! fn main() { let serial_1 = unsafe { PERIPHERALS.take_serial() }; // This panics! // let serial_2 = unsafe { PERIPHERALS.take_serial() };\\n} Although interacting with this structure is unsafe, once we have the SerialPort it contained, we no longer need to use unsafe, or the PERIPHERALS structure at all. This has a small runtime overhead because we must wrap the SerialPort structure in an option, and we’ll need to call take_serial() once, however this small up-front cost allows us to leverage the borrow checker throughout the rest of our program.","breadcrumbs":"Peripherals » Singletons » How do we do this in Rust?","id":"99","title":"How do we do this in Rust?"}},"docInfo":{"0":{"body":16,"breadcrumbs":2,"title":1},"1":{"body":17,"breadcrumbs":3,"title":2},"10":{"body":5,"breadcrumbs":4,"title":2},"100":{"body":84,"breadcrumbs":5,"title":3},"101":{"body":78,"breadcrumbs":2,"title":0},"102":{"body":57,"breadcrumbs":5,"title":3},"103":{"body":100,"breadcrumbs":4,"title":2},"104":{"body":106,"breadcrumbs":6,"title":2},"105":{"body":57,"breadcrumbs":6,"title":2},"106":{"body":93,"breadcrumbs":8,"title":3},"107":{"body":211,"breadcrumbs":7,"title":2},"108":{"body":233,"breadcrumbs":6,"title":2},"109":{"body":287,"breadcrumbs":6,"title":2},"11":{"body":139,"breadcrumbs":4,"title":2},"110":{"body":51,"breadcrumbs":8,"title":4},"111":{"body":45,"breadcrumbs":8,"title":3},"112":{"body":90,"breadcrumbs":8,"title":3},"113":{"body":28,"breadcrumbs":6,"title":1},"114":{"body":99,"breadcrumbs":2,"title":1},"115":{"body":130,"breadcrumbs":3,"title":2},"116":{"body":5,"breadcrumbs":4,"title":3},"117":{"body":80,"breadcrumbs":3,"title":2},"118":{"body":57,"breadcrumbs":2,"title":1},"119":{"body":66,"breadcrumbs":2,"title":1},"12":{"body":51,"breadcrumbs":5,"title":3},"120":{"body":72,"breadcrumbs":2,"title":1},"121":{"body":57,"breadcrumbs":2,"title":1},"122":{"body":183,"breadcrumbs":4,"title":3},"123":{"body":196,"breadcrumbs":3,"title":2},"124":{"body":172,"breadcrumbs":3,"title":2},"125":{"body":322,"breadcrumbs":4,"title":3},"126":{"body":282,"breadcrumbs":2,"title":1},"127":{"body":421,"breadcrumbs":3,"title":2},"128":{"body":88,"breadcrumbs":2,"title":1},"129":{"body":46,"breadcrumbs":2,"title":1},"13":{"body":49,"breadcrumbs":4,"title":2},"130":{"body":72,"breadcrumbs":5,"title":4},"131":{"body":69,"breadcrumbs":3,"title":2},"132":{"body":64,"breadcrumbs":2,"title":1},"133":{"body":236,"breadcrumbs":3,"title":2},"134":{"body":130,"breadcrumbs":3,"title":2},"135":{"body":11,"breadcrumbs":3,"title":2},"136":{"body":96,"breadcrumbs":5,"title":4},"137":{"body":108,"breadcrumbs":3,"title":2},"138":{"body":64,"breadcrumbs":6,"title":5},"139":{"body":42,"breadcrumbs":3,"title":2},"14":{"body":57,"breadcrumbs":5,"title":3},"140":{"body":9,"breadcrumbs":4,"title":2},"141":{"body":27,"breadcrumbs":6,"title":3},"142":{"body":88,"breadcrumbs":8,"title":4},"143":{"body":0,"breadcrumbs":5,"title":1},"144":{"body":21,"breadcrumbs":10,"title":6},"145":{"body":0,"breadcrumbs":5,"title":1},"146":{"body":78,"breadcrumbs":11,"title":7},"147":{"body":38,"breadcrumbs":12,"title":8},"148":{"body":15,"breadcrumbs":12,"title":8},"149":{"body":0,"breadcrumbs":5,"title":1},"15":{"body":43,"breadcrumbs":4,"title":2},"150":{"body":18,"breadcrumbs":11,"title":7},"151":{"body":59,"breadcrumbs":10,"title":6},"152":{"body":0,"breadcrumbs":7,"title":3},"153":{"body":65,"breadcrumbs":12,"title":8},"154":{"body":70,"breadcrumbs":14,"title":10},"155":{"body":286,"breadcrumbs":12,"title":8},"156":{"body":21,"breadcrumbs":8,"title":4},"157":{"body":42,"breadcrumbs":5,"title":1},"158":{"body":157,"breadcrumbs":8,"title":4},"159":{"body":62,"breadcrumbs":8,"title":4},"16":{"body":61,"breadcrumbs":3,"title":1},"160":{"body":156,"breadcrumbs":5,"title":1},"161":{"body":85,"breadcrumbs":6,"title":2},"162":{"body":39,"breadcrumbs":6,"title":2},"163":{"body":88,"breadcrumbs":8,"title":4},"164":{"body":101,"breadcrumbs":7,"title":3},"165":{"body":200,"breadcrumbs":6,"title":2},"166":{"body":301,"breadcrumbs":7,"title":3},"167":{"body":18,"breadcrumbs":5,"title":1},"168":{"body":120,"breadcrumbs":2,"title":1},"169":{"body":22,"breadcrumbs":4,"title":3},"17":{"body":52,"breadcrumbs":3,"title":1},"170":{"body":33,"breadcrumbs":3,"title":2},"171":{"body":37,"breadcrumbs":7,"title":3},"172":{"body":42,"breadcrumbs":6,"title":2},"173":{"body":215,"breadcrumbs":8,"title":4},"174":{"body":102,"breadcrumbs":7,"title":3},"175":{"body":92,"breadcrumbs":7,"title":3},"176":{"body":41,"breadcrumbs":8,"title":4},"177":{"body":48,"breadcrumbs":8,"title":4},"178":{"body":67,"breadcrumbs":9,"title":5},"179":{"body":38,"breadcrumbs":7,"title":3},"18":{"body":2,"breadcrumbs":3,"title":1},"180":{"body":40,"breadcrumbs":7,"title":3},"181":{"body":19,"breadcrumbs":7,"title":3},"182":{"body":21,"breadcrumbs":5,"title":1},"183":{"body":54,"breadcrumbs":6,"title":2},"184":{"body":82,"breadcrumbs":8,"title":4},"185":{"body":0,"breadcrumbs":4,"title":2},"186":{"body":24,"breadcrumbs":10,"title":4},"187":{"body":120,"breadcrumbs":7,"title":1},"188":{"body":183,"breadcrumbs":8,"title":2},"189":{"body":94,"breadcrumbs":8,"title":2},"19":{"body":93,"breadcrumbs":3,"title":1},"190":{"body":166,"breadcrumbs":8,"title":2},"191":{"body":149,"breadcrumbs":9,"title":4},"192":{"body":19,"breadcrumbs":4,"title":2},"193":{"body":25,"breadcrumbs":3,"title":1},"194":{"body":10,"breadcrumbs":3,"title":1},"195":{"body":34,"breadcrumbs":3,"title":1},"196":{"body":17,"breadcrumbs":3,"title":1},"197":{"body":35,"breadcrumbs":3,"title":1},"198":{"body":7,"breadcrumbs":3,"title":1},"199":{"body":18,"breadcrumbs":3,"title":1},"2":{"body":69,"breadcrumbs":2,"title":1},"20":{"body":58,"breadcrumbs":5,"title":3},"200":{"body":8,"breadcrumbs":3,"title":1},"201":{"body":9,"breadcrumbs":3,"title":1},"21":{"body":57,"breadcrumbs":4,"title":2},"22":{"body":22,"breadcrumbs":5,"title":3},"23":{"body":0,"breadcrumbs":6,"title":4},"24":{"body":68,"breadcrumbs":3,"title":1},"25":{"body":0,"breadcrumbs":6,"title":4},"26":{"body":53,"breadcrumbs":4,"title":2},"27":{"body":45,"breadcrumbs":6,"title":4},"28":{"body":70,"breadcrumbs":3,"title":1},"29":{"body":31,"breadcrumbs":8,"title":6},"3":{"body":46,"breadcrumbs":2,"title":1},"30":{"body":54,"breadcrumbs":3,"title":1},"31":{"body":49,"breadcrumbs":5,"title":3},"32":{"body":49,"breadcrumbs":3,"title":1},"33":{"body":29,"breadcrumbs":4,"title":2},"34":{"body":46,"breadcrumbs":4,"title":2},"35":{"body":47,"breadcrumbs":4,"title":2},"36":{"body":34,"breadcrumbs":4,"title":2},"37":{"body":8,"breadcrumbs":4,"title":2},"38":{"body":158,"breadcrumbs":4,"title":2},"39":{"body":22,"breadcrumbs":4,"title":2},"4":{"body":47,"breadcrumbs":3,"title":2},"40":{"body":26,"breadcrumbs":4,"title":2},"41":{"body":9,"breadcrumbs":5,"title":3},"42":{"body":6,"breadcrumbs":4,"title":1},"43":{"body":93,"breadcrumbs":4,"title":1},"44":{"body":113,"breadcrumbs":5,"title":2},"45":{"body":5,"breadcrumbs":4,"title":1},"46":{"body":27,"breadcrumbs":6,"title":3},"47":{"body":22,"breadcrumbs":6,"title":3},"48":{"body":0,"breadcrumbs":4,"title":1},"49":{"body":41,"breadcrumbs":7,"title":4},"5":{"body":105,"breadcrumbs":2,"title":1},"50":{"body":45,"breadcrumbs":4,"title":1},"51":{"body":4,"breadcrumbs":4,"title":1},"52":{"body":25,"breadcrumbs":7,"title":4},"53":{"body":271,"breadcrumbs":6,"title":2},"54":{"body":36,"breadcrumbs":4,"title":2},"55":{"body":44,"breadcrumbs":4,"title":1},"56":{"body":35,"breadcrumbs":8,"title":5},"57":{"body":31,"breadcrumbs":6,"title":3},"58":{"body":42,"breadcrumbs":5,"title":2},"59":{"body":46,"breadcrumbs":4,"title":1},"6":{"body":15,"breadcrumbs":2,"title":1},"60":{"body":130,"breadcrumbs":5,"title":2},"61":{"body":253,"breadcrumbs":5,"title":2},"62":{"body":391,"breadcrumbs":4,"title":1},"63":{"body":349,"breadcrumbs":4,"title":1},"64":{"body":358,"breadcrumbs":4,"title":1},"65":{"body":17,"breadcrumbs":4,"title":1},"66":{"body":95,"breadcrumbs":5,"title":2},"67":{"body":233,"breadcrumbs":4,"title":1},"68":{"body":708,"breadcrumbs":4,"title":1},"69":{"body":211,"breadcrumbs":8,"title":3},"7":{"body":69,"breadcrumbs":3,"title":2},"70":{"body":86,"breadcrumbs":7,"title":2},"71":{"body":89,"breadcrumbs":8,"title":3},"72":{"body":183,"breadcrumbs":10,"title":5},"73":{"body":34,"breadcrumbs":6,"title":1},"74":{"body":50,"breadcrumbs":6,"title":1},"75":{"body":83,"breadcrumbs":6,"title":1},"76":{"body":221,"breadcrumbs":8,"title":3},"77":{"body":289,"breadcrumbs":4,"title":1},"78":{"body":265,"breadcrumbs":4,"title":1},"79":{"body":72,"breadcrumbs":4,"title":1},"8":{"body":34,"breadcrumbs":3,"title":2},"80":{"body":287,"breadcrumbs":4,"title":1},"81":{"body":165,"breadcrumbs":5,"title":2},"82":{"body":74,"breadcrumbs":6,"title":3},"83":{"body":216,"breadcrumbs":6,"title":3},"84":{"body":252,"breadcrumbs":4,"title":1},"85":{"body":0,"breadcrumbs":2,"title":1},"86":{"body":156,"breadcrumbs":2,"title":1},"87":{"body":162,"breadcrumbs":5,"title":4},"88":{"body":97,"breadcrumbs":4,"title":3},"89":{"body":0,"breadcrumbs":6,"title":2},"9":{"body":79,"breadcrumbs":4,"title":3},"90":{"body":68,"breadcrumbs":5,"title":1},"91":{"body":74,"breadcrumbs":6,"title":2},"92":{"body":153,"breadcrumbs":6,"title":2},"93":{"body":158,"breadcrumbs":6,"title":2},"94":{"body":22,"breadcrumbs":6,"title":3},"95":{"body":27,"breadcrumbs":4,"title":1},"96":{"body":40,"breadcrumbs":5,"title":2},"97":{"body":15,"breadcrumbs":3,"title":1},"98":{"body":36,"breadcrumbs":6,"title":4},"99":{"body":96,"breadcrumbs":3,"title":1}},"length":202},"lang":"English"}}')); \ No newline at end of file diff --git a/typ/mdbook/assets/toc-7ac66f26.js b/typ/mdbook/assets/toc-7ac66f26.js new file mode 100644 index 00000000..d93cfd8d --- /dev/null +++ b/typ/mdbook/assets/toc-7ac66f26.js @@ -0,0 +1,455 @@ +// Populate the sidebar +// +// This is a script, and not included directly in the page, to control the total size of the book. +// The TOC contains an entry for each page, so if each page includes a copy of the TOC, +// the total size of the page becomes O(n**2). +class MDBookSidebarScrollbox extends HTMLElement { + constructor() { + super(); + } + connectedCallback() { + this.innerHTML = '
    1. Introduction
      1. Hardware
      2. no_std
      3. Tooling
      4. Installation
        1. Linux
        2. MacOS
        3. Windows
        4. Verify Installation
    2. Getting started
      1. QEMU
      2. Hardware
      3. Memory-mapped Registers
      4. Semihosting
      5. Panicking
      6. Exceptions
      7. Interrupts
    3. Peripherals
      1. A first attempt in Rust
      2. The Borrow Checker
      3. Singletons
    4. Static Guarantees
      1. Typestate Programming
      2. Peripherals as State Machines
      3. Design Contracts
      4. Zero Cost Abstractions
    5. Portability
    6. Concurrency
    7. Collections
    8. Design Patterns
      1. HALs
        1. Checklist
        2. Naming
        3. Interoperability
        4. Predictability
        5. GPIO
    9. Tips for embedded C developers
    10. Interoperability
      1. A little C with your Rust
      2. A little Rust with your C
    11. Unsorted topics
      1. Optimizations: The speed size tradeoff
      2. Performing Math Functionality
    12. Appendix A: Glossary
    '; + // Set the current, active page, and reveal it if it's hidden + let current_page = document.location.href.toString().split('#')[0].split('?')[0]; + if (current_page.endsWith('/')) { + current_page += 'index.html'; + } + const links = Array.prototype.slice.call(this.querySelectorAll('a')); + const l = links.length; + for (let i = 0; i < l; ++i) { + const link = links[i]; + const href = link.getAttribute('href'); + if (href && !href.startsWith('#') && !/^(?:[a-z+]+:)?\/\//.test(href)) { + link.href = path_to_root + href; + } + // The 'index' page is supposed to alias the first chapter in the book. + // Check both with and without the '.html' suffix to be robust against pretty URLs + if (link.href.replace(/\.html$/, '') === current_page.replace(/\.html$/, '') + || i === 0 + && path_to_root === '' + && current_page.endsWith('/index.html')) { + link.classList.add('active'); + let parent = link.parentElement; + while (parent) { + if (parent.tagName === 'LI' && parent.classList.contains('chapter-item')) { + parent.classList.add('expanded'); + } + parent = parent.parentElement; + } + } + } + // Track and set sidebar scroll position + this.addEventListener('click', e => { + if (e.target.tagName === 'A') { + const clientRect = e.target.getBoundingClientRect(); + const sidebarRect = this.getBoundingClientRect(); + sessionStorage.setItem('sidebar-scroll-offset', clientRect.top - sidebarRect.top); + } + }, { passive: true }); + const sidebarScrollOffset = sessionStorage.getItem('sidebar-scroll-offset'); + sessionStorage.removeItem('sidebar-scroll-offset'); + if (sidebarScrollOffset !== null) { + // preserve sidebar scroll position when navigating via links within sidebar + const activeSection = this.querySelector('.active'); + if (activeSection) { + const clientRect = activeSection.getBoundingClientRect(); + const sidebarRect = this.getBoundingClientRect(); + const currentOffset = clientRect.top - sidebarRect.top; + this.scrollTop += currentOffset - parseFloat(sidebarScrollOffset); + } + } else { + // scroll sidebar to current active section when navigating via + // 'next/previous chapter' buttons + const activeSection = document.querySelector('#mdbook-sidebar .active'); + if (activeSection) { + activeSection.scrollIntoView({ block: 'center' }); + } + } + // Toggle buttons + const sidebarAnchorToggles = document.querySelectorAll('.chapter-fold-toggle'); + function toggleSection(ev) { + ev.currentTarget.parentElement.parentElement.classList.toggle('expanded'); + } + Array.from(sidebarAnchorToggles).forEach(el => { + el.addEventListener('click', toggleSection); + }); + } +} +window.customElements.define('mdbook-sidebar-scrollbox', MDBookSidebarScrollbox); + + +// --------------------------------------------------------------------------- +// Support for dynamically adding headers to the sidebar. + +(function() { + // This is used to detect which direction the page has scrolled since the + // last scroll event. + let lastKnownScrollPosition = 0; + // This is the threshold in px from the top of the screen where it will + // consider a header the "current" header when scrolling down. + const defaultDownThreshold = 150; + // Same as defaultDownThreshold, except when scrolling up. + const defaultUpThreshold = 300; + // The threshold is a virtual horizontal line on the screen where it + // considers the "current" header to be above the line. The threshold is + // modified dynamically to handle headers that are near the bottom of the + // screen, and to slightly offset the behavior when scrolling up vs down. + let threshold = defaultDownThreshold; + // This is used to disable updates while scrolling. This is needed when + // clicking the header in the sidebar, which triggers a scroll event. It + // is somewhat finicky to detect when the scroll has finished, so this + // uses a relatively dumb system of disabling scroll updates for a short + // time after the click. + let disableScroll = false; + // Array of header elements on the page. + let headers; + // Array of li elements that are initially collapsed headers in the sidebar. + // I'm not sure why eslint seems to have a false positive here. + // eslint-disable-next-line prefer-const + let headerToggles = []; + // This is a debugging tool for the threshold which you can enable in the console. + let thresholdDebug = false; + + // Updates the threshold based on the scroll position. + function updateThreshold() { + const scrollTop = window.pageYOffset || document.documentElement.scrollTop; + const windowHeight = window.innerHeight; + const documentHeight = document.documentElement.scrollHeight; + + // The number of pixels below the viewport, at most documentHeight. + // This is used to push the threshold down to the bottom of the page + // as the user scrolls towards the bottom. + const pixelsBelow = Math.max(0, documentHeight - (scrollTop + windowHeight)); + // The number of pixels above the viewport, at least defaultDownThreshold. + // Similar to pixelsBelow, this is used to push the threshold back towards + // the top when reaching the top of the page. + const pixelsAbove = Math.max(0, defaultDownThreshold - scrollTop); + // How much the threshold should be offset once it gets close to the + // bottom of the page. + const bottomAdd = Math.max(0, windowHeight - pixelsBelow - defaultDownThreshold); + let adjustedBottomAdd = bottomAdd; + + // Adjusts bottomAdd for a small document. The calculation above + // assumes the document is at least twice the windowheight in size. If + // it is less than that, then bottomAdd needs to be shrunk + // proportional to the difference in size. + if (documentHeight < windowHeight * 2) { + const maxPixelsBelow = documentHeight - windowHeight; + const t = 1 - pixelsBelow / Math.max(1, maxPixelsBelow); + const clamp = Math.max(0, Math.min(1, t)); + adjustedBottomAdd *= clamp; + } + + let scrollingDown = true; + if (scrollTop < lastKnownScrollPosition) { + scrollingDown = false; + } + + if (scrollingDown) { + // When scrolling down, move the threshold up towards the default + // downwards threshold position. If near the bottom of the page, + // adjustedBottomAdd will offset the threshold towards the bottom + // of the page. + const amountScrolledDown = scrollTop - lastKnownScrollPosition; + const adjustedDefault = defaultDownThreshold + adjustedBottomAdd; + threshold = Math.max(adjustedDefault, threshold - amountScrolledDown); + } else { + // When scrolling up, move the threshold down towards the default + // upwards threshold position. If near the bottom of the page, + // quickly transition the threshold back up where it normally + // belongs. + const amountScrolledUp = lastKnownScrollPosition - scrollTop; + const adjustedDefault = defaultUpThreshold - pixelsAbove + + Math.max(0, adjustedBottomAdd - defaultDownThreshold); + threshold = Math.min(adjustedDefault, threshold + amountScrolledUp); + } + + if (documentHeight <= windowHeight) { + threshold = 0; + } + + if (thresholdDebug) { + const id = 'mdbook-threshold-debug-data'; + let data = document.getElementById(id); + if (data === null) { + data = document.createElement('div'); + data.id = id; + data.style.cssText = ` + position: fixed; + top: 50px; + right: 10px; + background-color: 0xeeeeee; + z-index: 9999; + pointer-events: none; + `; + document.body.appendChild(data); + } + data.innerHTML = ` + + + + + + + + + + +
    documentHeight${documentHeight.toFixed(1)}
    windowHeight${windowHeight.toFixed(1)}
    scrollTop${scrollTop.toFixed(1)}
    pixelsAbove${pixelsAbove.toFixed(1)}
    pixelsBelow${pixelsBelow.toFixed(1)}
    bottomAdd${bottomAdd.toFixed(1)}
    adjustedBottomAdd${adjustedBottomAdd.toFixed(1)}
    scrollingDown${scrollingDown}
    threshold${threshold.toFixed(1)}
    + `; + drawDebugLine(); + } + + lastKnownScrollPosition = scrollTop; + } + + function drawDebugLine() { + if (!document.body) { + return; + } + const id = 'mdbook-threshold-debug-line'; + const existingLine = document.getElementById(id); + if (existingLine) { + existingLine.remove(); + } + const line = document.createElement('div'); + line.id = id; + line.style.cssText = ` + position: fixed; + top: ${threshold}px; + left: 0; + width: 100vw; + height: 2px; + background-color: red; + z-index: 9999; + pointer-events: none; + `; + document.body.appendChild(line); + } + + function mdbookEnableThresholdDebug() { + thresholdDebug = true; + updateThreshold(); + drawDebugLine(); + } + + window.mdbookEnableThresholdDebug = mdbookEnableThresholdDebug; + + // Updates which headers in the sidebar should be expanded. If the current + // header is inside a collapsed group, then it, and all its parents should + // be expanded. + function updateHeaderExpanded(currentA) { + // Add expanded to all header-item li ancestors. + let current = currentA.parentElement; + while (current) { + if (current.tagName === 'LI' && current.classList.contains('header-item')) { + current.classList.add('expanded'); + } + current = current.parentElement; + } + } + + // Updates which header is marked as the "current" header in the sidebar. + // This is done with a virtual Y threshold, where headers at or below + // that line will be considered the current one. + function updateCurrentHeader() { + if (!headers || !headers.length) { + return; + } + + // Reset the classes, which will be rebuilt below. + const els = document.getElementsByClassName('current-header'); + for (const el of els) { + el.classList.remove('current-header'); + } + for (const toggle of headerToggles) { + toggle.classList.remove('expanded'); + } + + // Find the last header that is above the threshold. + let lastHeader = null; + for (const header of headers) { + const rect = header.getBoundingClientRect(); + if (rect.top <= threshold) { + lastHeader = header; + } else { + break; + } + } + if (lastHeader === null) { + lastHeader = headers[0]; + const rect = lastHeader.getBoundingClientRect(); + const windowHeight = window.innerHeight; + if (rect.top >= windowHeight) { + return; + } + } + + // Get the anchor in the summary. + const href = '#' + lastHeader.id; + const a = [...document.querySelectorAll('.header-in-summary')] + .find(element => element.getAttribute('href') === href); + if (!a) { + return; + } + + a.classList.add('current-header'); + + updateHeaderExpanded(a); + } + + // Updates which header is "current" based on the threshold line. + function reloadCurrentHeader() { + if (disableScroll) { + return; + } + updateThreshold(); + updateCurrentHeader(); + } + + + // When clicking on a header in the sidebar, this adjusts the threshold so + // that it is located next to the header. This is so that header becomes + // "current". + function headerThresholdClick(event) { + // See disableScroll description why this is done. + disableScroll = true; + setTimeout(() => { + disableScroll = false; + }, 100); + // requestAnimationFrame is used to delay the update of the "current" + // header until after the scroll is done, and the header is in the new + // position. + requestAnimationFrame(() => { + requestAnimationFrame(() => { + // Closest is needed because if it has child elements like . + const a = event.target.closest('a'); + const href = a.getAttribute('href'); + const targetId = href.substring(1); + const targetElement = document.getElementById(targetId); + if (targetElement) { + threshold = targetElement.getBoundingClientRect().bottom; + updateCurrentHeader(); + } + }); + }); + } + + // Takes the nodes from the given head and copies them over to the + // destination, along with some filtering. + function filterHeader(source, dest) { + const clone = source.cloneNode(true); + clone.querySelectorAll('mark').forEach(mark => { + mark.replaceWith(...mark.childNodes); + }); + dest.append(...clone.childNodes); + } + + // Scans page for headers and adds them to the sidebar. + document.addEventListener('DOMContentLoaded', function() { + const activeSection = document.querySelector('#mdbook-sidebar .active'); + if (activeSection === null) { + return; + } + + const main = document.getElementsByTagName('main')[0]; + headers = Array.from(main.querySelectorAll('h2, h3, h4, h5, h6')) + .filter(h => h.id !== '' && h.children.length && h.children[0].tagName === 'A'); + + if (headers.length === 0) { + return; + } + + // Build a tree of headers in the sidebar. + + const stack = []; + + const firstLevel = parseInt(headers[0].tagName.charAt(1)); + for (let i = 1; i < firstLevel; i++) { + const ol = document.createElement('ol'); + ol.classList.add('section'); + if (stack.length > 0) { + stack[stack.length - 1].ol.appendChild(ol); + } + stack.push({level: i + 1, ol: ol}); + } + + // The level where it will start folding deeply nested headers. + const foldLevel = 3; + + for (let i = 0; i < headers.length; i++) { + const header = headers[i]; + const level = parseInt(header.tagName.charAt(1)); + + const currentLevel = stack[stack.length - 1].level; + if (level > currentLevel) { + // Begin nesting to this level. + for (let nextLevel = currentLevel + 1; nextLevel <= level; nextLevel++) { + const ol = document.createElement('ol'); + ol.classList.add('section'); + const last = stack[stack.length - 1]; + const lastChild = last.ol.lastChild; + // Handle the case where jumping more than one nesting + // level, which doesn't have a list item to place this new + // list inside of. + if (lastChild) { + lastChild.appendChild(ol); + } else { + last.ol.appendChild(ol); + } + stack.push({level: nextLevel, ol: ol}); + } + } else if (level < currentLevel) { + while (stack.length > 1 && stack[stack.length - 1].level > level) { + stack.pop(); + } + } + + const li = document.createElement('li'); + li.classList.add('header-item'); + li.classList.add('expanded'); + if (level < foldLevel) { + li.classList.add('expanded'); + } + const span = document.createElement('span'); + span.classList.add('chapter-link-wrapper'); + const a = document.createElement('a'); + span.appendChild(a); + a.href = '#' + header.id; + a.classList.add('header-in-summary'); + filterHeader(header.children[0], a); + a.addEventListener('click', headerThresholdClick); + const nextHeader = headers[i + 1]; + if (nextHeader !== undefined) { + const nextLevel = parseInt(nextHeader.tagName.charAt(1)); + if (nextLevel > level && level >= foldLevel) { + const toggle = document.createElement('a'); + toggle.classList.add('chapter-fold-toggle'); + toggle.classList.add('header-toggle'); + toggle.addEventListener('click', () => { + li.classList.toggle('expanded'); + }); + const toggleDiv = document.createElement('div'); + toggleDiv.textContent = '❱'; + toggle.appendChild(toggleDiv); + span.appendChild(toggle); + headerToggles.push(li); + } + } + li.appendChild(span); + + const currentParent = stack[stack.length - 1]; + currentParent.ol.appendChild(li); + } + + const onThisPage = document.createElement('div'); + onThisPage.classList.add('on-this-page'); + onThisPage.append(stack[0].ol); + const activeItemSpan = activeSection.parentElement; + activeItemSpan.after(onThisPage); + }); + + document.addEventListener('DOMContentLoaded', reloadCurrentHeader); + document.addEventListener('scroll', reloadCurrentHeader, { passive: true }); +})(); + diff --git a/typ/mdbook/assets/tomorrow-night-4c0ae647.css b/typ/mdbook/assets/tomorrow-night-4c0ae647.css new file mode 100644 index 00000000..11752b8a --- /dev/null +++ b/typ/mdbook/assets/tomorrow-night-4c0ae647.css @@ -0,0 +1,104 @@ +/* Tomorrow Night Theme */ +/* https://github.com/jmblog/color-themes-for-highlightjs */ +/* Original theme - https://github.com/chriskempson/tomorrow-theme */ +/* https://github.com/jmblog/color-themes-for-highlightjs */ + +/* Tomorrow Comment */ +.hljs-comment { + color: #969896; +} + +/* Tomorrow Red */ +.hljs-variable, +.hljs-attribute, +.hljs-attr, +.hljs-tag, +.hljs-regexp, +.ruby .hljs-constant, +.xml .hljs-tag .hljs-title, +.xml .hljs-pi, +.xml .hljs-doctype, +.html .hljs-doctype, +.css .hljs-id, +.css .hljs-class, +.css .hljs-pseudo { + color: #cc6666; +} + +/* Tomorrow Orange */ +.hljs-number, +.hljs-preprocessor, +.hljs-pragma, +.hljs-built_in, +.hljs-literal, +.hljs-params, +.hljs-constant { + color: #de935f; +} + +/* Tomorrow Yellow */ +.ruby .hljs-class .hljs-title, +.css .hljs-rule .hljs-attribute { + color: #f0c674; +} + +/* Tomorrow Green */ +.hljs-string, +.hljs-value, +.hljs-inheritance, +.hljs-header, +.hljs-name, +.ruby .hljs-symbol, +.xml .hljs-cdata { + color: #b5bd68; +} + +/* Tomorrow Aqua */ +.hljs-title, +.hljs-section, +.css .hljs-hexcolor { + color: #8abeb7; +} + +/* Tomorrow Blue */ +.hljs-function, +.python .hljs-decorator, +.python .hljs-title, +.ruby .hljs-function .hljs-title, +.ruby .hljs-title .hljs-keyword, +.perl .hljs-sub, +.javascript .hljs-title, +.coffeescript .hljs-title { + color: #81a2be; +} + +/* Tomorrow Purple */ +.hljs-keyword, +.javascript .hljs-function { + color: #b294bb; +} + +.hljs { + display: block; + overflow-x: auto; + background: #1d1f21; + color: #c5c8c6; +} + +.coffeescript .javascript, +.javascript .xml, +.tex .hljs-formula, +.xml .javascript, +.xml .vbscript, +.xml .css, +.xml .hljs-cdata { + opacity: 0.5; +} + +.hljs-addition { + color: #718c00; +} + +.hljs-deletion { + color: #c82829; +} diff --git a/typ/mdbook/icons.typ b/typ/mdbook/icons.typ new file mode 100644 index 00000000..e1658863 --- /dev/null +++ b/typ/mdbook/icons.typ @@ -0,0 +1,74 @@ +#let icons = ( + bars: ( + "0 0 448 512", + "M0 96C0 78.3 14.3 64 32 64H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H416c17.7 0 32 14.3 32 32z" + ), + paintbrush: ( + "0 0 576 512", + "M371.3 367.1c27.3-3.9 51.9-19.4 67.2-42.9L600.2 74.1c12.6-19.5 9.4-45.3-7.6-61.2S549.7-4.4 531.1 9.6L294.4 187.2c-24 18-38.2 46.1-38.4 76.1L371.3 367.1zm-19.6 25.4l-116-104.4C175.9 290.3 128 339.6 128 400c0 3.9 .2 7.8 .6 11.6c1.8 17.5-10.2 36.4-27.8 36.4H96c-17.7 0-32 14.3-32 32s14.3 32 32 32H240c61.9 0 112-50.1 112-112c0-2.5-.1-5-.2-7.5z" + ), + search: ( + "0 0 512 512", + "M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352c79.5 0 144-64.5 144-144s-64.5-144-144-144S64 128.5 64 208s64.5 144 144 144z" + ), + globe: ( + "0 0 512 512", + "M351.9 280l-190.9 0c2.9 64.5 17.2 123.9 37.5 167.4 11.4 24.5 23.7 41.8 35.1 52.4 11.2 10.5 18.9 12.2 22.9 12.2s11.7-1.7 22.9-12.2c11.4-10.6 23.7-28 35.1-52.4 20.3-43.5 34.6-102.9 37.5-167.4zM160.9 232l190.9 0C349 167.5 334.7 108.1 314.4 64.6 303 40.2 290.7 22.8 279.3 12.2 268.1 1.7 260.4 0 256.4 0s-11.7 1.7-22.9 12.2c-11.4 10.6-23.7 28-35.1 52.4-20.3 43.5-34.6 102.9-37.5 167.4zm-48 0C116.4 146.4 138.5 66.9 170.8 14.7 78.7 47.3 10.9 131.2 1.5 232l111.4 0zM1.5 280c9.4 100.8 77.2 184.7 169.3 217.3-32.3-52.2-54.4-131.7-57.9-217.3L1.5 280zm398.4 0c-3.5 85.6-25.6 165.1-57.9 217.3 92.1-32.7 159.9-116.5 169.3-217.3l-111.4 0zm111.4-48C501.9 131.2 434.1 47.3 342 14.7 374.3 66.9 396.4 146.4 399.9 232l111.4 0z" + ), + pdf: ( + "0 0 576 512", + "M208 48L96 48c-8.8 0-16 7.2-16 16l0 384c0 8.8 7.2 16 16 16l80 0 0 48-80 0c-35.3 0-64-28.7-64-64L32 64C32 28.7 60.7 0 96 0L229.5 0c17 0 33.3 6.7 45.3 18.7L397.3 141.3c12 12 18.7 28.3 18.7 45.3l0 149.5-48 0 0-128-88 0c-39.8 0-72-32.2-72-72l0-88zM348.1 160L256 67.9 256 136c0 13.3 10.7 24 24 24l68.1 0zM240 380l32 0c33.1 0 60 26.9 60 60s-26.9 60-60 60l-12 0 0 28c0 11-9 20-20 20s-20-9-20-20l0-128c0-11 9-20 20-20zm32 80c11 0 20-9 20-20s-9-20-20-20l-12 0 0 40 12 0zm96-80l32 0c28.7 0 52 23.3 52 52l0 64c0 28.7-23.3 52-52 52l-32 0c-11 0-20-9-20-20l0-128c0-11 9-20 20-20zm32 128c6.6 0 12-5.4 12-12l0-64c0-6.6-5.4-12-12-12l-12 0 0 88 12 0zm76-108c0-11 9-20 20-20l48 0c11 0 20 9 20 20s-9 20-20 20l-28 0 0 24 28 0c11 0 20 9 20 20s-9 20-20 20l-28 0 0 44c0 11-9 20-20 20s-20-9-20-20l0-128z" + ), + github: ( + "0 0 496 512", "M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z" + ), + spinner: ( + "0 0 512 512", + "M304 48c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zm0 416c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zM48 304c26.5 0 48-21.5 48-48s-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48zm464-48c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zM142.9 437c18.7-18.7 18.7-49.1 0-67.9s-49.1-18.7-67.9 0s-18.7 49.1 0 67.9s49.1 18.7 67.9 0zm0-294.2c18.7-18.7 18.7-49.1 0-67.9S93.7 56.2 75 75s-18.7 49.1 0 67.9s49.1 18.7 67.9 0zM369.1 437c18.7 18.7 49.1 18.7 67.9 0s18.7-49.1 0-67.9s-49.1-18.7-67.9 0s-18.7 49.1 0 67.9z" + ), + left: ( + "0 0 320 512", + "M41.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l160 160c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L109.3 256 246.6 118.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-160 160z" + ), + right: ( + "0 0 320 512", + "M278.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-160 160c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L210.7 256 73.4 118.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l160 160z" + ), + eye: ( + "0 0 576 512", + "M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM432 256c0 79.5-64.5 144-144 144s-144-64.5-144-144s64.5-144 144-144s144 64.5 144 144zM288 192c0 35.3-28.7 64-64 64c-11.5 0-22.3-3-31.6-8.4c-.2 2.8-.4 5.5-.4 8.4c0 53 43 96 96 96s96-43 96-96s-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6z", + ), + eye-slash: ( + "0 0 640 512", + "M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zM223.1 149.5C248.6 126.2 282.7 112 320 112c79.5 0 144 64.5 144 144c0 24.9-6.3 48.3-17.4 68.7L408 294.5c5.2-11.8 8-24.8 8-38.5c0-53-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6c0 10.2-2.4 19.8-6.6 28.3l-90.3-70.8zm223.1 298L373 389.9c-16.4 6.5-34.3 10.1-53 10.1c-79.5 0-144-64.5-144-144c0-6.9 .5-13.6 1.4-20.2L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5z", + ), + copy: ( + "0 0 512 512", + "M502.6 70.63l-61.25-61.25C435.4 3.371 427.2 0 418.7 0H255.1c-35.35 0-64 28.66-64 64l.0195 256C192 355.4 220.7 384 256 384h192c35.2 0 64-28.8 64-64V93.25C512 84.77 508.6 76.63 502.6 70.63zM464 320c0 8.836-7.164 16-16 16H255.1c-8.838 0-16-7.164-16-16L239.1 64.13c0-8.836 7.164-16 16-16h128L384 96c0 17.67 14.33 32 32 32h47.1V320zM272 448c0 8.836-7.164 16-16 16H63.1c-8.838 0-16-7.164-16-16L47.98 192.1c0-8.836 7.164-16 16-16H160V128H63.99c-35.35 0-64 28.65-64 64l.0098 256C.002 483.3 28.66 512 64 512h192c35.2 0 64-28.8 64-64v-32h-47.1L272 448z" + ), + play: ( + "0 0 384 512", + "M73 39c-14.8-9.1-33.4-9.4-48.5-.9S0 62.6 0 80V432c0 17.4 9.4 33.4 24.5 41.9s33.7 8.1 48.5-.9L361 297c14.3-8.7 23-24.2 23-41s-8.7-32.2-23-41L73 39z" + ), + clock-rotate-left: ( + "0 0 512 512", + "M75 75L41 41C25.9 25.9 0 36.6 0 57.9V168c0 13.3 10.7 24 24 24H134.1c21.4 0 32.1-25.9 17-41l-30.8-30.8C155 85.5 203 64 256 64c106 0 192 86 192 192s-86 192-192 192c-40.8 0-78.6-12.7-109.7-34.4c-14.5-10.1-34.4-6.6-44.6 7.9s-6.6 34.4 7.9 44.6C151.2 495 201.7 512 256 512c141.4 0 256-114.6 256-256S397.4 0 256 0C185.3 0 121.3 28.7 75 75zm181 53c-13.3 0-24 10.7-24 24V256c0 6.4 2.5 12.5 7 17l72 72c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-65-65V152c0-13.3-10.7-24-24-24z" + ) +) + +#let builtin-icon(name, class: "fa-svg", id: none) = { + assert(name in icons, message: "Missing icon `"+name+"`") + let (viewBox, path) = icons.at(name) + html.span( + class: class, + ..(if id != none { (id: id) }), + html.elem( + "svg", + attrs: ( + xmlns: "http://www.w3.org/2000/svg", + viewBox: viewBox + ), + html.elem("path", attrs: (d: path)), + ) + ) +} \ No newline at end of file diff --git a/typ/mdbook/lib.typ b/typ/mdbook/lib.typ new file mode 100644 index 00000000..ce5994df --- /dev/null +++ b/typ/mdbook/lib.typ @@ -0,0 +1,418 @@ +#import "icons.typ": builtin-icon +#import "scripts.typ": builtin-script + +#let lbl(path) = label(path.replace("/","-")) + +#let to-string(content) = { + if content.has("text") { + content.text + } else if content.has("children") { + content.children.map(to-string).join("") + } else if content.has("body") { + to-string(content.body) + } else if content == [] { + " " + } +} + +#let html_head_common(pth) = [ + #html.link(rel: "stylesheet", href: pth+"css/variables-8adf115d.css") + #html.link(rel: "stylesheet", href: pth+"css/general-e96d0476.css") + #html.link(rel: "stylesheet", href: pth+"css/chrome-d279d366.css") + #html.link(rel: "stylesheet", href: pth+"css/print-9e4910d8.css", media: "print") + // Fonts + #html.link(rel: "stylesheet", href: pth+"fonts/fonts-9644e21d.css") +] + +#let html_head(title, root_pth) = html.head[ + #html.meta(charset: "utf-8") + #html.title(title) + + #html.meta(name: "description", content:"") + #html.meta(name: "viewport", content: "width=device-width, initial-scale=1") + #html.meta(name: "theme-color", content: "#ffffff") + + // Custom HTML head + #html.link(rel: "icon", href: root_pth+"favicon-de23e50b.svg") + #html.elem("link", attrs: (rel: "shortcut icon", href: root_pth+"favicon-8114d1fc.png")) + #html_head_common(root_pth) + + #html.style("#language-list { left: auto; right: 10px; } #language-list a { color: inherit; }") + + // Highlight.js Stylesheets + #html.link(rel: "stylesheet", id: "mdbook-highlight-css", href: root_pth+"highlight-493f70e1.css") + #html.link(rel: "stylesheet", id: "mdbook-tomorrow-night-css", href: root_pth+"tomorrow-night-4c0ae647.css") + #html.link(rel: "stylesheet", id: "mdbook-ayu-highlight-css", href: root_pth+"ayu-highlight-3fdfc3ac.css") + + // Provide site root and default themes to javascript + #builtin-script("provide-themes", root_pth: root_pth) + + // Start loading toc.js asap + #html.script(src: root_pth+"toc-7ac66f26.js") +] + +#let help_container() = [ + #html.div(id: "mdbook-help-container")[ + #html.div(id: "mdbook-help-popup")[ + #html.h2(class: "mdbook-help-title")[Keyboard shortcuts] + #html.div[ + #html.p[Press #html.kbd[←] or #html.kbd[→] to navigate between chapters] + #html.p[Press #html.kbd[S] or #html.kbd[/] to search in the book] + #html.p[Press #html.kbd[?] to show this help] + #html.p[Press #html.kbd[Esc] to hide this help] + ] + ] + ] +] + +#let body_header() = [ + // Work around some values being stored in localStorage wrapped in quotes + #builtin-script("quote-workaround") + // Set the theme before any content is loaded, prevents flash + #builtin-script("init-theme") + #html.input(type: "checkbox", id: "mdbook-sidebar-toggle-anchor", class: "hidden") + // Hide / unhide sidebar before it is displayed + #builtin-script("hide-sidebar") +] + +#let icon_templates() = [ + #html.template(id: "fa-eye", builtin-icon("eye")) + #html.template(id: "fa-eye-slash", builtin-icon("eye-slash")) + #html.template(id: "fa-copy", builtin-icon("copy")) + #html.template(id: "fa-play", builtin-icon("play")) + #html.template(id: "fa-clock-rotate-left", builtin-icon("clock-rotate-left")) +] + +#let footer_scripts(pth) = [ + #html.script("window.playground_copyable = true;") + #html.script(src: pth+"elasticlunr-ef4e11c1.min.js") + #html.script(src: pth+"mark-09e88c2c.min.js") + #html.script(src: pth+"searcher-09f2665d.js") + #html.script(src: pth+"clipboard-1626706a.min.js") + #html.script(src: pth+"highlight-abc7f01d.js") + #html.script(src: pth+"book-609e4cb8.js") +] + +#let source_array(sources) = { + let items = () + for (path, src) in sources.pairs() { + items.push((path, src.at("content"), src.at("title"))) + let sub = src.at("sub", default: none) + if sub != none { + for (path, src) in sub.pairs() { + items.push((path, src.at("content"), src.at("title"))) + let sub = src.at("sub", default: none) + if sub != none { + for (path, src) in sub.pairs() { + items.push((path, src.at("content"), src.at("title"))) + } + } + } + } + } + items +} + +#let page_header(pth, root_pth, lang, languages, default_lang) = [ + #html.div(id: "mdbook-menu-bar-hover-placeholder") + #html.div(id: "mdbook-menu-bar", class: ("menu-bar", "sticky"))[ + #html.div(class: "left-buttons")[ + #let attrs = (id: "mdbook-sidebar-toggle", class: "icon-button", title: "Toggle Table of Contents", aria-label: "Toggle Table of Contents", aria-controls: "mdbook-sidebar") + #attrs.insert("for", "mdbook-sidebar-toggle-anchor") + #html.elem("label", attrs: attrs, builtin-icon("bars")) + #html.button(id: "mdbook-theme-toggle", class: "icon-button", type: "button", title: "Change theme", aria-label: "Change theme", aria-haspopup: true, aria-expanded: false, aria-controls: "mdbook-theme-list", builtin-icon("paintbrush")) + #html.ul(id: "mdbook-theme-list", class: "theme-popup", aria-label: "Themes", role: "menu")[ + #let themes = ( + default_theme: "Auto", + light: "Light", + rust: "Rust", + coal: "Coal", + navy: "Navy", + ayu: "Ayu", + ) + #for (id, name) in themes.pairs() { + html.li(role: none)[ + #html.button(role: "menuitem", class: "theme", id: "mdbook-theme-"+id)[#name] + ] + } + ] + #html.button(id: "mdbook-search-toggle", class: "icon-button", type: "button", title: "Search (`/`)", aria-label: "Toggle Searchbar", aria-expanded: false, aria-keyshortcuts: "/ s", aria-controls: "mdbook-searchbar", builtin-icon("search")) + ] + #html.h1(class: "menu-title")[The Embedded Rust Book] + #html.div(class: "right-buttons")[ + #html.button(id: "language-toggle", class: "icon-button", type: "button", title: "Change language", aria-label: "Change language", aria-haspopup: true, aria-expanded: false, aria-controls: "language-list", builtin-icon("globe")) + #html.ul(id: "language-list", class: "theme-popup", aria-label: "Languages", role: "menu")[ + #for (l, name) in languages [ + #html.li(role: none, + html.a(role: "menuitem", class: "theme", id: "lang-"+l, name, href: root_pth+if l == default_lang { + "" + } else { + l+"/" + }+"index.html")) + ] + ] + #builtin-script("show-language-list") + #html.a(href: root_pth+"book_"+lang+".pdf", title: "PDF version of this book", aria-label: "PDF", builtin-icon("pdf", id: "book-pdf")) + #html.a(href: "https://github.com/rust-embedded/book", title: "Git repository", aria-label: "Git repository", builtin-icon("github")) + ] + ] + #html.div(id: "mdbook-search-wrapper", class: "hidden")[ + #html.form(id: "mdbook-searchbar-outer", class: "searchbar-outer")[ + #html.div(class: "search-wrapper")[ + #html.input(type: "search", id: "mdbook-searchbar", name: "searchbar", placeholder: "Search this book ...", aria-controls: "mdbook-searchresults-outer", aria-describedby: "searchresults-header") + #html.div(class: "spinner-wrapper", builtin-icon("spinner", id: "fa-spin")) + ] + ] + #html.div(id: "mdbook-searchresults-outer", class: "searchresults-outer hidden")[ + #html.div(id: "mdbook-searchresults-header", class: "searchresults-header") + #html.ul(id: "mdbook-searchresults") + ] + ] + + // Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM + #builtin-script("apply-aria") +] + +#let sidebar(depth) = html.nav(id: "mdbook-sidebar", class: "sidebar", aria-label: "Table of contents")[ + #html.elem("mdbook-sidebar-scrollbox", attrs:(class: "sidebar-scrollbox")) + #html.noscript()[ + #let pth = (("../",)*depth).join("") + #html.iframe(class: "sidebar-iframe-outer", src: pth+"toc.html") + ] + #html.div(id: "mdbook-sidebar-resize-handle", class:"sidebar-resize-handle")[ + #html.div(class: "sidebar-resize-indicator") + ] +] +#let toc(sources, out, lang, default_lang) = document(out)[ + #html.html(lang: lang, class: "light", dir: ltr)[ + #html.head[ + #html.meta(charset: "utf-8") + #html.meta(name:"robots", content: "noindex") + #html.meta(name: "viewport", content: "width=device-width, initial-scale=1") + #html.meta(name: "theme-color", content: "#ffffff") + #html_head_common(if lang == default_lang { + "" + } else { + "../" + }) + #html.title() + ] + #html.body(class: "sidebar-iframe-inner")[ + #html.ol(class: "chapter")[ + #let num = (0,) + #for (path, src) in sources.pairs() { + let d = 0 + num = num.slice(0, d+1) + num.at(d) = num.at(d) + 1 + html.li(class: ("chapter-item", "expanded"))[#html.span(class: "chapter-link-wrapper")[ + #html.a(href: path+".html", target: "_parent", [#html.strong(aria-hidden: true)[#num.map(i => [#i.]).join[]] #src.at("title")]) + ]] + if "sub" in src { + html.ol(class: "section")[ + #num.push(0) + #for (path, src) in src.at("sub").pairs() { + let d = 1 + num = num.slice(0, d+1) + num.at(d) = num.at(d) + 1 + html.li(class: ("chapter-item", "expanded"))[#html.span(class: "chapter-link-wrapper")[ + #html.a(href: path+".html", target: "_parent", [#html.strong(aria-hidden: true)[#num.map(i => [#i.]).join[]] #src.at("title")]) + ]] + if "sub" in src { + html.ol(class: "section")[ + #num.push(0) + #for (path, src) in src.at("sub").pairs() { + let d = 2 + num = num.slice(0, d+1) + num.at(d) = num.at(d) + 1 + html.li(class: ("chapter-item", "expanded"))[#html.span(class: "chapter-link-wrapper")[ + #html.a(href: path+".html", target: "_parent", [#html.strong(aria-hidden: true)[#num.map(i => [#i.]).join[]] #src.at("title")]) + ]] + } + ] + } + } + ] + } + } + ] + ] + ] +] + +// Navigation buttons +#let nav_buttons(pth, prev, next, wide: false) = html.nav(class: if wide { "nav-wide-wrapper" } else { "nav-wrapper" }, aria-label: "Page navigation")[ + #let c = if wide { "nav-chapters" } else { "mobile-nav-chapters" } + #if prev != none [ + #html.a(rel: "prev", href: pth+prev+".html", class: c + " previous", title: "Previous chapter", aria-label: "Previous chapter", aria-keyshortcuts: "Left", builtin-icon("left")) + ] + #if next != none [ + #html.a(rel: "next", href: pth+next+".html", class: c + " next", title: "Next chapter", aria-label: "Next chapter", aria-keyshortcuts: "Right", builtin-icon("right")) + ] +] + +#let book_page( + source, + out, + lang, + languages, + default_lang, + title, + depth, + sidebar, + prev_path, + next_path, +) = document(out)[ + #html.html(lang: lang, class: ("light", "sidebar-visible"), dir: ltr)[ + #let pth = (("../",)*depth).join("") + #let root_pth = if lang == default_lang { + "" + } else { + "../" + }+pth + #html_head(title, root_pth) + #html.body[ + #help_container() + #html.div(id: "mdbook-body-container")[ + #body_header() + #sidebar + #html.div(id: "mdbook-page-wrapper", class: "page-wrapper")[ + #html.div(class: "page")[ + #page_header(pth, root_pth, lang, languages, default_lang) + #html.div(id: "mdbook-content", class: "content")[ + #html.main[ + #source + ] + #nav_buttons(pth, prev_path, next_path) + ] + ] + #nav_buttons(pth, prev_path, next_path, wide: true) + ] + #icon_templates() + #footer_scripts(root_pth) + ] + ] + ] +] + +#let asset_list = ( + "ayu-highlight-3fdfc3ac.css", + "book-609e4cb8.js", + "clipboard-1626706a.min.js", + "css/chrome-d279d366.css", + "css/general-e96d0476.css", + "css/print-9e4910d8.css", + "css/variables-8adf115d.css", + "elasticlunr-ef4e11c1.min.js", + "favicon-8114d1fc.png", + "favicon-de23e50b.svg", + "fonts/fonts-9644e21d.css", + "fonts/OPEN-SANS-LICENSE.txt", + "fonts/open-sans-v17-all-charsets-300-7736aa35.woff2", + "fonts/open-sans-v17-all-charsets-300italic-2c7b95c0.woff2", + "fonts/open-sans-v17-all-charsets-600-486c6759.woff2", + "fonts/open-sans-v17-all-charsets-600italic-1a3e8659.woff2", + "fonts/open-sans-v17-all-charsets-700-c22fe8c7.woff2", + "fonts/open-sans-v17-all-charsets-700italic-238ae959.woff2", + "fonts/open-sans-v17-all-charsets-800-3d2c812a.woff2", + "fonts/open-sans-v17-all-charsets-800italic-ba1521ec.woff2", + "fonts/open-sans-v17-all-charsets-italic-6c9463f7.woff2", + "fonts/open-sans-v17-all-charsets-regular-2e3b1d34.woff2", + "fonts/SOURCE-CODE-PRO-LICENSE.txt", + "fonts/source-code-pro-v11-all-charsets-500-2bdd9410.woff2", + "highlight-493f70e1.css", + "highlight-abc7f01d.js", + "mark-09e88c2c.min.js", + "searcher-09f2665d.js", + "searchindex-8ec871e3.js", + "toc-7ac66f26.js", + "tomorrow-night-4c0ae647.css", +) + +#let html_book( + sources, + lang, + languages, + book_title, + default_lang, +) = { + let sa = source_array(sources) + if lang == default_lang { + for path in asset_list { + asset(path, read("assets/"+path, encoding: none)) + } + } + let prefix = if lang != default_lang { lang+"/" } else { "" } + toc(sources, prefix+"toc.html", lang, default_lang) + for (i, (path, source, title)) in sa.enumerate() { + let (prev_path, _, _) = if i > 0 { sa.at(i - 1) } else { (none, none, none) } + let (next_path, _, _) = sa.at(i + 1, default: (none, none, none)) + let out = prefix+path + ".html" + let depth = path.split("/").len() - 1 + [ + #book_page(source, out, lang, languages, default_lang, to-string(title) + " - " + book_title, depth, sidebar(depth), prev_path, next_path) #lbl(path) + ] + if i == 0 { + let depth = 0 + book_page(source, prefix+"index.html", lang, languages, default_lang, to-string(title) + " - " + book_title, depth, sidebar(depth), prev_path, next_path) + } + } +} + +#let pdf_book(title_page, sources) = [ + #let sa = source_array(sources) + #set par(justify: true) + #set heading(numbering: "1.") + #show heading.where(level: 1): it => { pagebreak();it } + + #show link: it => { text(fill: blue, it) } + #show raw.where(block: true): it => { + block( + fill: rgb("#F6F6F6"), + outset: 5pt, + it + ) + } + + #title_page + + #outline(depth: 3) + #let part = "main" + #for (path, source, _) in sa { + if part == "main" and path.starts-with("appendix/") { + path = "appendix" + counter(heading).update(0) + set heading(numbering: "A.1.") + source + } else { + source + } + } +] + +#let book( + tgt, + sources, + lang, + languages, + book_title: "The Book", + default_lang: "en", +) = { + if tgt == "pdf" { + pdf_book( + [ + #v(1fr) + #align(center, text(size: 40pt, [#book_title\ (#languages.at(lang))])) + #v(1fr) + ], + sources, + ) + } else if tgt == "html" { + html_book( + sources, + lang, + languages, + book_title, + default_lang + ) + } +} diff --git a/typ/mdbook/scripts.typ b/typ/mdbook/scripts.typ new file mode 100644 index 00000000..c4043bfb --- /dev/null +++ b/typ/mdbook/scripts.typ @@ -0,0 +1,72 @@ + +#let builtin-script(name, ..attrs) = { + let s = if name == "provide-themes" { + let root_pth = attrs.at("root_pth") + " + const path_to_root = \""+root_pth+"\"; + const default_light_theme = \"light\"; + const default_dark_theme = \"navy\"; + window.path_to_searchindex_js = \""+root_pth+"searchindex-8ec871e3.js\"; + " + } else if name == "quote-workaround" { + " + try { + let theme = localStorage.getItem('mdbook-theme'); + let sidebar = localStorage.getItem('mdbook-sidebar'); + + if (theme.startsWith('\"') && theme.endsWith('\"')) { + localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1)); + } + + if (sidebar.startsWith('\"') && sidebar.endsWith('\"')) { + localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1)); + } + } catch (e) { } + " + } else if name == "init-theme" { + " + const default_theme = window.matchMedia(\"(prefers-color-scheme: dark)\").matches ? default_dark_theme : default_light_theme; + let theme; + try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { } + if (theme === null || theme === undefined) { theme = default_theme; } + const html = document.documentElement; + html.classList.remove('light') + html.classList.add(theme); + html.classList.add(\"js\"); + " + } else if name == "hide-sidebar" { + " + let sidebar = null; + const sidebar_toggle = document.getElementById(\"mdbook-sidebar-toggle-anchor\"); + if (document.body.clientWidth >= 1080) { + try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { } + sidebar = sidebar || 'visible'; + } else { + sidebar = 'hidden'; + sidebar_toggle.checked = false; + } + if (sidebar === 'visible') { + sidebar_toggle.checked = true; + } else { + html.classList.remove('sidebar-visible'); + } + " + } else if name == "apply-aria" { + " + document.getElementById('mdbook-sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible'); + document.getElementById('mdbook-sidebar').setAttribute('aria-hidden', sidebar !== 'visible'); + Array.from(document.querySelectorAll('#mdbook-sidebar a')).forEach(function(link) { + link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1); + }); + " + } else if name == "show-language-list" { + " + let langToggle = document.getElementById(\"language-toggle\"); + let langList = document.getElementById(\"language-list\"); + langToggle.addEventListener(\"click\", (event) => { + langList.style.display = langList.style.display == \"block\" ? \"none\" : \"block\"; + }); + " + } + html.script(s) +} \ No newline at end of file diff --git a/typ/mdbook/typst.toml b/typ/mdbook/typst.toml new file mode 100644 index 00000000..e26a01f0 --- /dev/null +++ b/typ/mdbook/typst.toml @@ -0,0 +1,4 @@ +[package] +name = "typbook" +version = "0.1.0" +entrypoint = "lib.typ" diff --git a/typ/peripherals/a-first-attempt.typ b/typ/peripherals/a-first-attempt.typ new file mode 100644 index 00000000..85b446a8 --- /dev/null +++ b/typ/peripherals/a-first-attempt.typ @@ -0,0 +1,309 @@ +#import "../config.typ": * + +#h1(offset: whole, + if lang == "en" [A First Attempt] + else if lang == "de" [Ein erster Versuch in Rust] + else { todo }) +#set heading(offset: whole*2) + += #(if lang == "en" [The Registers] + else if lang == "de" [Die Register] + else { todo }) + +#let url_arm = "http://infocenter.arm.com/help/topic/com.arm.doc.dui0553a/Babieigh.html" +#if lang == "en" [ + Let's look at the 'SysTick' peripheral - a simple timer which comes with + every Cortex-M processor core. Typically you'll be looking these up in + the chip manufacturer's data sheet or _Technical Reference Manual_, + but this example is common to all ARM Cortex-M cores, let's look in the + #link(url_arm)[ARM reference manual]. + We see there are four registers: +] else if lang == "de" [ + Betrachten wir die „SysTick"-Peripherie -- einen einfachen Timer, der in + jedem Cortex-M-Prozessorkern enthalten ist. Normalerweise würde man + hierfür im Datenblatt des Chip-Herstellers oder im _Technical + Reference Manual_ nachschlagen; da dieses Beispiel jedoch für alle + ARM-Cortex-M-Kerne gilt, werfen wir einen Blick in das + #link(url_arm)[ARM-Referenzhandbuch]. + Wir sehen, dass es vier Register gibt: +] else { todo } + +#figure( + kind: table, + table( + columns: 4, + align: (auto,auto,auto,auto,), + table.header( + if lang in ("en", "de") [Offset] + else { todo }, + if lang in ("en", "de") [Name] + else { todo }, + if lang == "en" [Description] + else if lang == "de" [Beschreibung] + else { todo }, + if lang == "en" [Width] + else if lang == "de" [Breite] + else { todo }, + ), + [0x00], [SYST_CSR], + if lang == "en" [Control and Status Register] + else if lang == "de" [Steuerung und Status Register] + else { todo }, + [32 bits], + [0x04], [SYST_RVR], + if lang == "en" [Reload Value Register] + else if lang == "de" [Wert-Register neu laden] + else { todo }, + [32 bits], + [0x08], [SYST_CVR], + if lang == "en" [Current Value Register] + else if lang == "de" [Register für den aktuellen Wert] + else { todo }, + [32 bits], + [0x0C], [SYST_CALIB], + if lang == "en" [Calibration Value Register] + else if lang == "de" [Kalibrierwert-Register] + else { todo }, + [32 bits], + ) +) + += #(if lang == "en" [The C Approach] + else if lang == "de" [Der C-Ansatz] + else { todo }) + +#if lang == "en" [ + In Rust, we can represent a collection of registers in exactly the same + way as we do in C - with a `struct`. +] else if lang == "de" [ + In Rust können wir eine Sammlung von Registern auf genau dieselbe Weise + darstellen wie in C -- mit einem `struct`. +] else { todo } + +```rust +#[repr(C)] +struct SysTick { + pub csr: u32, + pub rvr: u32, + pub cvr: u32, + pub calib: u32, +} +``` + +#if lang == "en" [ + The qualifier `#[repr(C)]` tells the Rust compiler to lay this structure + out like a C compiler would. That's very important, as Rust allows + structure fields to be re-ordered, while C does not. You can imagine the + debugging we'd have to do if these fields were silently re-arranged by + the compiler! With this qualifier in place, we have our four 32-bit + fields which correspond to the table above. But of course, this `struct` + is of no use by itself - we need a variable. +] else if lang == "de" [ + Der Qualifizierer `#[repr(C)]` weist den Rust-Compiler an, diese + Struktur wie ein C-Compiler anzulegen. Das ist sehr wichtig, da Rust die + Neuordnung von Strukturfeldern zulässt, C jedoch nicht. Sie können sich + vorstellen, wie viel Debugging wir durchführen müssten, wenn diese + Felder vom Compiler stillschweigend neu angeordnet würden! Mit diesem + Qualifizierer verfügen wir über unsere vier 32-Bit-Felder, die der + obigen Tabelle entsprechen. Aber diese „Struktur" allein nützt natürlich + nichts -- wir brauchen eine Variable. +] else { todo } + +```rust +let systick = 0xE000_E010 as *mut SysTick; +let time = unsafe { (*systick).cvr }; +``` + += #(if lang == "en" [Volatile Accesses] + else if lang == "de" [Volatile-Zugriffe] + else { todo }) + +#if lang == "en" [ + Now, there are a couple of problems with the approach above. + + We have to use unsafe every time we want to access our Peripheral. + + We've got no way of specifying which registers are read-only or + read-write. + + Any piece of code anywhere in your program could access the hardware + through this structure. + + Most importantly, it doesn't actually work… +] else if lang == "de" [ + Nun gibt es bei dem oben genannten Ansatz einige Probleme. + + Wir müssen `unsafe` verwenden, wann immer wir auf unsere Peripherie + zugreifen wollen. + + Wir haben keine Möglichkeit festzulegen, welche Register nur lesbar + oder sowohl les- als auch schreibbar sind. + + Jeder Code-Abschnitt an beliebiger Stelle Ihres Programms könnte über + diese Struktur auf die Hardware zugreifen. + + Vor allem aber funktioniert es eigentlich nicht … +] else { todo } + +#if lang == "en" [ + Now, the problem is that compilers are clever. If you make two writes to + the same piece of RAM, one after the other, the compiler can notice this + and just skip the first write entirely. In C, we can mark variables as + `volatile` to ensure that every read or write occurs as intended. In + Rust, we instead mark the _accesses_ as volatile, not the variable. +] else if lang == "de" [ + Das Problem ist nun, dass Compiler clever sind. Wenn man zwei + Schreibzugriffe nacheinander auf denselben Speicherbereich durchführt, + kann der Compiler dies erkennen und den ersten Schreibvorgang einfach + komplett überspringen. In C können wir Variablen als `volatile` + kennzeichnen, um sicherzustellen, dass jeder Lese- oder Schreibvorgang + wie vorgesehen ausgeführt wird. In Rust hingegen kennzeichnen wir die + _Zugriffe_ als volatile, nicht die Variable. +] else { todo } + +```rust +let systick = unsafe { &mut *(0xE000_E010 as *mut SysTick) }; +let time = unsafe { core::ptr::read_volatile(&mut systick.cvr) }; +``` + +#let ln_volatile = link("https://crates.io/crates/volatile_register")[`volatile_register`] +#if lang == "en" [ + So, we've fixed one of our four problems, but now we have even more + `unsafe` code! Fortunately, there's a third party crate which can help - #ln_volatile. +] else if lang == "de" [ + Wir haben also eines unserer vier Probleme gelöst, aber jetzt haben wir + noch mehr `unsafe`-Code! Glücklicherweise gibt es ein Crate eines + Drittanbieters, das hierbei helfen kann -- #ln_volatile. +] else { todo } + +```rust +use volatile_register::{RW, RO}; + +#[repr(C)] +struct SysTick { + pub csr: RW, + pub rvr: RW, + pub cvr: RW, + pub calib: RO, +} + +fn get_systick() -> &'static mut SysTick { + unsafe { &mut *(0xE000_E010 as *mut SysTick) } +} + +fn get_time() -> u32 { + let systick = get_systick(); + systick.cvr.read() +} +``` + +#if lang == "en" [ + Now, the volatile accesses are performed automatically through the + `read` and `write` methods. It's still `unsafe` to perform writes, but + to be fair, hardware is a bunch of mutable state and there's no way for + the compiler to know whether these writes are actually safe, so this is + a good default position. +] else if lang == "de" [ + Die volatilen Zugriffe erfolgen nun automatisch über die Methoden `read` + und `write`. Schreibzugriffe sind zwar weiterhin als `unsafe` + eingestuft, doch fairerweise muss man sagen, dass Hardware im Grunde aus + veränderlichem Zustand besteht und der Compiler nicht wissen kann, ob + diese Schreibvorgänge tatsächlich sicher sind; daher ist dies eine + sinnvolle Standardeinstellung. +] else { todo } + += #(if lang == "en" [The Rusty Wrapper] + else if lang == "de" [Der Rusty Wrapper] + else { todo }) + +#if lang == "en" [ + We need to wrap this `struct` up into a higher-layer API that is safe + for our users to call. As the driver author, we manually verify the + unsafe code is correct, and then present a safe API for our users so + they don't have to worry about it (provided they trust us to get it + right!). +] else if lang == "de" [ + Wir müssen diese `struct` in eine API der höheren Ebene verpacken, deren + Aufruf für unsere Nutzer sicher ist. Als Treiberentwickler überprüfen + wir manuell die Korrektheit des `unsafe`-Codes und stellen unseren + Nutzern dann eine sichere API zur Verfügung, sodass sie sich darum nicht + kümmern müssen (vorausgesetzt, sie vertrauen darauf, dass wir alles + richtig gemacht haben!). +] else { todo } + +#if lang == "en" [ + One example might be: +] else if lang == "de" [ + Ein Beispiel hierfür wäre: +] else { todo } + +```rust +use volatile_register::{RW, RO}; + +pub struct SystemTimer { + p: &'static mut RegisterBlock +} + +#[repr(C)] +struct RegisterBlock { + pub csr: RW, + pub rvr: RW, + pub cvr: RW, + pub calib: RO, +} + +impl SystemTimer { + pub fn new() -> SystemTimer { + SystemTimer { + p: unsafe { &mut *(0xE000_E010 as *mut RegisterBlock) } + } + } + + pub fn get_time(&self) -> u32 { + self.p.cvr.read() + } + + pub fn set_reload(&mut self, reload_value: u32) { + unsafe { self.p.rvr.write(reload_value) } + } +} + +pub fn example_usage() -> String { + let mut st = SystemTimer::new(); + st.set_reload(0x00FF_FFFF); + format!("Time is now 0x{:08x}", st.get_time()) +} +``` + +#if lang == "en" [ + Now, the problem with this approach is that the following code is + perfectly acceptable to the compiler: +] else if lang == "de" [ + Das Problem bei diesem Ansatz ist nun, dass der folgende Code für den + Compiler völlig zulässig ist: +] else { todo } + +```rust +fn thread1() { + let mut st = SystemTimer::new(); + st.set_reload(2000); +} + +fn thread2() { + let mut st = SystemTimer::new(); + st.set_reload(1000); +} +``` + +#if lang == "en" [ + Our `&mut self` argument to the `set_reload` function checks that there + are no other references to _that_ particular `SystemTimer` struct, + but they don't stop the user creating a second `SystemTimer` which + points to the exact same peripheral! Code written in this fashion will + work if the author is diligent enough to spot all of these 'duplicate' + driver instances, but once the code is spread out over multiple modules, + drivers, developers, and days, it gets easier and easier to make these + kinds of mistakes. +] else if lang == "de" [ + Das `&mut self`-Argument der Funktion `set_reload` stellt zwar sicher, + dass keine weiteren Referenzen auf _genau diese_ + `SystemTimer`-Struktur existieren, verhindert jedoch nicht, dass der + Benutzer einen zweiten `SystemTimer` erstellt, der auf dieselbe + Peripherieeinheit verweist. Auf diese Weise geschriebener Code + funktioniert zwar, sofern der Autor sorgfältig genug ist, all diese + „doppelten" Treiberinstanzen zu erkennen; sobald sich der Code jedoch + über mehrere Module, Treiber, Entwickler und Zeiträume hinweg erstreckt, + schleichen sich derartige Fehler immer leichter ein. +] else { todo } diff --git a/typ/peripherals/borrowck.typ b/typ/peripherals/borrowck.typ new file mode 100644 index 00000000..7815530d --- /dev/null +++ b/typ/peripherals/borrowck.typ @@ -0,0 +1,76 @@ +#import "../config.typ": * + +#h1(offset: whole, + if lang == "en" [Mutable Global State] + else if lang == "de" [Veränderbarer globaler Zustand] + else { todo }) +#set heading(offset: whole*2) + +#if lang == "en" [ + Unfortunately, hardware is basically nothing but mutable global state, + which can feel very frightening for a Rust developer. Hardware exists + independently from the structures of the code we write, and can be + modified at any time by the real world. +] else if lang == "de" [ + Leider ist Hardware im Grunde nichts anderes als veränderbarer globaler + Zustand, was auf einen Rust-Entwickler sehr beängstigend wirken kann. + Hardware existiert unabhängig von den Strukturen des Codes, den wir + schreiben, und kann jederzeit durch die reale Welt verändert werden. +] else { todo } + += #(if lang == "en" [What should our rules be?] + else if lang == "de" [Wie sollten unsere Regeln sein?] + else { todo }) + +#if lang == "en" [ +How can we reliably interact with these peripherals? ++ Always use `volatile` methods to read or write to peripheral memory, + as it can change at any time ++ In software, we should be able to share any number of read-only + accesses to these peripherals ++ If some software should have read-write access to a peripheral, it + should hold the only reference to that peripheral +] else if lang == "de" [ + Wie können wir zuverlässig mit diesen Peripheriegeräten interagieren? + + Verwenden Sie stets `volatile`-Methoden für Lese- oder Schreibzugriffe + auf Peripheriespeicher, da sich dieser jederzeit ändern kann. + + Auf Softwareebene sollten wir in der Lage sein, eine beliebige Anzahl + von Lesezugriffen auf diese Peripheriegeräte bereitzustellen. + + Wenn eine Software Lese- und Schreibzugriff auf ein Peripheriegerät + haben soll, sollte sie die einzige Referenz auf dieses Peripheriegerät + besitzen. +] else { todo } + += #(if lang == "en" [The Borrow Checker] + else if lang == "de" [Der Borrow-Checker] + else { todo }) + +#if lang == "en" [ + The last two of these rules sound suspiciously similar to what the + Borrow Checker does already! +] else if lang == "de" [ + Die letzten beiden dieser Regeln klingen verdächtig ähnlich wie das, was + der Borrow Checker bereits tut! +] else { todo } + +#if lang == "en" [ + Imagine if we could pass around ownership of these peripherals, or offer + immutable or mutable references to them? +] else if lang == "de" [ + Stellen Sie sich vor, wir könnten die Besitzrechte an diesen + Peripheriegeräten weitergeben oder unveränderliche bzw. veränderliche + Referenzen darauf anbieten? +] else { todo } + +#if lang == "en" [ + Well, we can, but for the Borrow Checker, we need to have exactly one + instance of each peripheral, so Rust can handle this correctly. Well, + luckily in the hardware, there is only one instance of any given + peripheral, but how can we expose that in the structure of our code? +] else if lang == "de" [ + Nun, das ist möglich, aber für den Borrow Checker benötigen wir genau + eine Instanz jedes Peripheriegeräts, damit Rust dies korrekt verarbeiten + kann. Glücklicherweise existiert in der Hardware nur eine Instanz jedes + Peripheriegeräts, aber wie können wir dies in der Struktur unseres Codes + sichtbar machen? +] else { todo } diff --git a/typ/peripherals/index.typ b/typ/peripherals/index.typ new file mode 100644 index 00000000..66e7f214 --- /dev/null +++ b/typ/peripherals/index.typ @@ -0,0 +1,221 @@ +#import "../config.typ": * + +#h1(if lang == "en" [Peripherals] + else if lang == "de" [Peripheriegeräte] + else { todo }) + +#set heading(offset: whole) + += #(if lang == "en" [What are Peripherals?] + else if lang == "de" [Was sind Peripheriegeräte?] + else { todo }) + +#if lang == "en" [ + Most Microcontrollers have more than just a CPU, RAM, or Flash Memory - + they contain sections of silicon which are used for interacting with + systems outside of the microcontroller, as well as directly and + indirectly interacting with their surroundings in the world via sensors, + motor controllers, or human interfaces such as a display or keyboard. + These components are collectively known as Peripherals. +] else if lang == "de" [ + Die meisten Mikrocontroller verfügen über mehr als nur eine CPU, einen + RAM oder einen Flash-Speicher -- sie enthalten Siliziumabschnitte, die + für die Interaktion mit Systemen außerhalb des Mikrocontrollers sowie + für die direkte und indirekte Interaktion mit ihrer Umgebung in der Welt + über Sensoren, Motorsteuerungen oder menschliche Schnittstellen wie ein + Display oder eine Tastatur verwendet werden. Diese Komponenten werden + zusammenfassend als Peripheriegeräte bezeichnet. +] else { todo } + +#if lang == "en" [ + These peripherals are useful because they allow a developer to offload + processing to them, avoiding having to handle everything in software. + Similar to how a desktop developer would offload graphics processing to + a video card, embedded developers can offload some tasks to peripherals + allowing the CPU to spend its time doing something else important, or + doing nothing in order to save power. +] else if lang == "de" [ + Diese Peripheriegeräte sind nützlich, weil sie es einem Entwickler + ermöglichen, die Verarbeitung auf sie auszulagern, sodass er sich nicht + um alles in der Software kümmern muss. Ähnlich wie ein + Desktop-Entwickler die Grafikverarbeitung auf eine Grafikkarte verlagern + würde, können Embedded-Entwickler einige Aufgaben auf Peripheriegeräte + verlagern, sodass die CPU ihre Zeit mit anderen wichtigen Dingen + verbringen oder gar nichts tun kann, um Strom zu sparen. +] else { todo } + +#if lang == "en" [ + If you look at the main circuit board in an old-fashioned home computer + from the 1970s or 1980s (and actually, the desktop PCs of yesterday are + not so far removed from the embedded systems of today) you would expect + to see: + - A processor + - A RAM chip + - A ROM chip + - An I/O controller +] else if lang == "de" [ + Wenn Sie sich die Hauptplatine eines altmodischen Heimcomputers aus den + 1970er oder 1980er Jahren ansehen (und tatsächlich sind die Desktop-PCs + von gestern nicht so weit von den eingebetteten Systemen von heute + entfernt), würden Sie Folgendes erwarten: + - Einen Prozessor + - Einen RAM-Chip + - Einen ROM-Chip + - Einen Ein-/Ausgabe-Controller +] else { todo } + +#if lang == "en" [ + The RAM chip, ROM chip and I/O controller (the peripheral in this + system) would be joined to the processor through a series of parallel + traces known as a 'bus'. This bus carries address information, which + selects which device on the bus the processor wishes to communicate + with, and a data bus which carries the actual data. In our embedded + microcontrollers, the same principles apply - it's just that everything + is packed on to a single piece of silicon. +] else if lang == "de" [ + Der RAM-Chip, der ROM-Chip und der I/O-Controller (das Peripheriegerät + in diesem System) wären über eine Reihe paralleler Leiterbahnen, die als + „Bus" bekannt sind, mit dem Prozessor verbunden. Dieser Bus trägt + Adressinformationen, die auswählen, mit welchem ​​Gerät auf dem Bus der + Prozessor kommunizieren möchte, und einen Datenbus, der die eigentlichen + Daten überträgt. In unseren eingebetteten Mikrocontrollern gelten die + gleichen Prinzipien -- nur dass alles auf einem einzigen Stück Silizium + untergebracht ist. +] else { todo } + +#if lang == "en" [ + However, unlike graphics cards, which typically have a Software API like + Vulkan, Metal, or OpenGL, peripherals are exposed to our Microcontroller + with a hardware interface, which is mapped to a chunk of the memory. +] else if lang == "de" [ + Im Gegensatz zu Grafikkarten, die normalerweise über eine Software-API + wie Vulkan, Metal oder OpenGL verfügen, werden Peripheriegeräte unserem + Mikrocontroller über eine Hardwareschnittstelle zugänglich gemacht, die + einem Teil des Speichers zugeordnet ist. +] else { todo } + += #(if lang == "en" [Linear and Real Memory Space] + else if lang == "de" [Linearer und realer Speicherraum] + else { todo }) + +#if lang == "en" [ + On a microcontroller, writing some data to some other arbitrary address, + such as `0x4000_0000` or `0x0000_0000`, may also be a completely valid + action. +] else if lang == "de" [ + Bei einem Mikrocontroller kann auch das Schreiben von Daten an eine + beliebige andere Adresse -- etwa `0x4000_0000` oder `0x0000_0000` -- ein + völlig zulässiger Vorgang sein. +] else { todo } + +#if lang == "en" [ + On a desktop system, access to memory is tightly controlled by the MMU, + or Memory Management Unit. This component has two major + responsibilities: enforcing access permission to sections of memory + (preventing one process from reading or modifying the memory of another + process); and re-mapping segments of the physical memory to virtual + memory ranges used in software. Microcontrollers do not typically have + an MMU, and instead only use real physical addresses in software. +] else if lang == "de" [ + Auf einem Desktop-System wird der Speicherzugriff streng von der MMU + (Memory Management Unit, Speicherverwaltungseinheit) kontrolliert. Diese + Komponente hat zwei Hauptaufgaben: die Durchsetzung von + Zugriffsberechtigungen für Speicherbereiche (um zu verhindern, dass ein + Prozess den Speicher eines anderen Prozesses liest oder verändert) sowie + die Abbildung von Segmenten des physischen Speichers auf virtuelle + Speicherbereiche, die von der Software genutzt werden. Mikrocontroller + verfügen in der Regel nicht über eine MMU; stattdessen verwendet die + Software dort ausschließlich reale physische Adressen. +] else { todo } + +#if lang == "en" [ + Although 32 bit microcontrollers have a real and linear address space + from `0x0000_0000`, and `0xFFFF_FFFF`, they generally only use a few + hundred kilobytes of that range for actual memory. This leaves a + significant amount of address space remaining. In earlier chapters, we + were talking about RAM being located at address `0x2000_0000`. If our + RAM was 64 KiB long (i.e.~with a maximum address of 0xFFFF) then + addresses `0x2000_0000` to `0x2000_FFFF` would correspond to our RAM. + When we write to a variable which lives at address `0x2000_1234`, what + happens internally is that some logic detects the upper portion of the + address (0x2000 in this example) and then activates the RAM so that it + can act upon the lower portion of the address (0x1234 in this case). On + a Cortex-M we also have our Flash ROM mapped in at address `0x0000_0000` + up to, say, address `0x0007_FFFF` (if we have a 512 KiB Flash ROM). + Rather than ignore all remaining space between these two regions, + Microcontroller designers instead mapped the interface for peripherals + in certain memory locations. This ends up looking something like this: +] else if lang == "de" [ + Obwohl 32-Bit-Mikrocontroller über einen echten, linearen Adressraum von + `0x0000_0000` bis `0xFFFF_FFFF` verfügen, nutzen sie für den + eigentlichen Speicher meist nur einige hundert Kilobyte dieses Bereichs. + Dadurch bleibt ein beträchtlicher Teil des Adressraums ungenutzt. In + früheren Kapiteln war die Rede davon, dass sich der RAM an der Adresse + `0x2000_0000` befindet. Wäre unser RAM 64 KiB groß (d.~h. mit einer + maximalen Adresse von 0xFFFF), so entsprächen die Adressen `0x2000_0000` + bis `0x2000_FFFF` diesem Speicherbereich. Wenn wir in eine Variable + schreiben, die an der Adresse `0x2000_1234` liegt, geschieht intern + Folgendes: Eine Logikeinheit erkennt den oberen Teil der Adresse (in + diesem Beispiel `0x2000`) und aktiviert den RAM, sodass dieser auf den + unteren Teil der Adresse (in diesem Fall `0x1234`) reagieren kann. Bei + einem Cortex-M-System ist zudem das Flash-ROM eingeblendet -- etwa im + Bereich von `0x0000_0000` bis `0x0007_FFFF` (bei einem + 512-KiB-Flash-ROM). Anstatt den gesamten verbleibenden Raum zwischen + diesen beiden Bereichen ungenutzt zu lassen, haben die Entwickler der + Mikrocontroller die Schnittstellen für Peripheriegeräte bestimmten + Speicheradressen zugeordnet. Das Ergebnis sieht dann ungefähr so ​​aus: +] else { todo } + +#box(image("../assets/nrf52-memory-map.png")) + +#link("http://infocenter.nordicsemi.com/pdf/nRF52832_PS_v1.1.pdf")[Nordic nRF52832 Datasheet (pdf)] + += #(if lang == "en" [Memory Mapped Peripherals] + else if lang == "de" [Im Speicher abgebildete Peripheriegeräte] + else { todo }) + +#if lang == "en" [ + Interaction with these peripherals is simple at a first glance - write + the right data to the correct address. For example, sending a 32 bit + word over a serial port could be as direct as writing that 32 bit word + to a certain memory address. The Serial Port Peripheral would then take + over and send out the data automatically. +] else if lang == "de" [ + Die Interaktion mit diesen Peripheriegeräten ist auf den ersten Blick + einfach: Schreiben Sie die richtigen Daten an die richtige Adresse. + Beispielsweise könnte das Senden eines 32-Bit-Worts über eine serielle + Schnittstelle genauso direkt sein wie das Schreiben dieses 32-Bit-Worts + an eine bestimmte Speicheradresse. Das Serial-Port-Peripheriegerät würde + dann übernehmen und die Daten automatisch versenden. +] else { todo } + +#if lang == "en" [ + Configuration of these peripherals works similarly. Instead of calling a + function to configure a peripheral, a chunk of memory is exposed which + serves as the hardware API. Write `0x8000_0000` to a SPI Frequency + Configuration Register, and the SPI port will send data at 8 Megabits + per second. Write `0x0200_0000` to the same address, and the SPI port + will send data at 125 Kilobits per second. These configuration registers + look a little bit like this: +] else if lang == "de" [ + Die Konfiguration dieser Peripheriegeräte funktioniert ähnlich. Anstatt + eine Funktion zum Konfigurieren eines Peripheriegeräts aufzurufen, wird + ein Teil des Speichers verfügbar gemacht, der als Hardware-API dient. + Schreiben Sie „0x8000\_0000" in ein SPI-Frequenzkonfigurationsregister, + und der SPI-Port sendet Daten mit 8 Megabit pro Sekunde. Schreiben Sie + „0x0200\_0000" an dieselbe Adresse und der SPI-Port sendet Daten mit 125 + Kilobit pro Sekunde. Diese Konfigurationsregister sehen in etwa so aus: +] else { todo } + +#box(image("../assets/nrf52-spi-frequency-register.png")) + +#link("http://infocenter.nordicsemi.com/pdf/nRF52832_PS_v1.1.pdf")[Nordic nRF52832 Datasheet (pdf)] + +#if lang == "en" [ + This interface is how interactions with the hardware are made, no matter + what language is used, whether that language is Assembly, C, or Rust. +] else if lang == "de" [ + Über diese Schnittstelle erfolgen die Interaktionen mit der Hardware, + unabhängig davon, welche Sprache verwendet wird -- sei es Assembler, C + oder Rust. +] else { todo } diff --git a/typ/peripherals/singletons.typ b/typ/peripherals/singletons.typ new file mode 100644 index 00000000..d15f2404 --- /dev/null +++ b/typ/peripherals/singletons.typ @@ -0,0 +1,338 @@ +#import "../config.typ": * + +#h1(offset: whole, + if lang in ("en", "de") [Singletons] + else { todo }) +#set heading(offset: whole*2) + +#quote(block: true)[ +#if lang == "en" [ + In software engineering, the singleton pattern is a software design + pattern that restricts the instantiation of a class to one object. +] else if lang == "de" [ + In der Softwaretechnik ist das Singleton-Muster ein Entwurfsmuster, das + die Instanziierung einer Klasse auf ein einziges Objekt beschränkt. +] else { todo } + +#let url_singleton = "https://en.wikipedia.org/wiki/Singleton_pattern" +#if lang == "en" [ + _Wikipedia: #link(url_singleton)[Singleton Pattern]_ +] else if lang == "de" [ + _Wikipedia: #link(url_singleton)[Singleton-Muster]_ +] else { todo } +] + += #(if lang == "en" [But why can't we just use global variable(s)?] + else if lang == "de" [Aber warum können wir nicht einfach globale Variable(n) verwenden?] + else { todo }) + +#if lang == "en" [ + We could make everything a public static, like this +] else if lang == "de" [ + Wir könnten alles als „public static" definieren, etwa so: +] else { todo } + +```rust +static mut THE_SERIAL_PORT: SerialPort = SerialPort; + +fn main() { + let _ = unsafe { + THE_SERIAL_PORT.read_speed(); + }; +} +``` + +#if lang == "en" [ + But this has a few problems. It is a mutable global variable, and in + Rust, these are always unsafe to interact with. These variables are also + visible across your whole program, which means the borrow checker is + unable to help you track references and ownership of these variables. +] else if lang == "de" [ + Dies bringt jedoch einige Probleme mit sich. Es handelt sich um eine + veränderbare globale Variable, und der Zugriff auf solche Variablen ist + in Rust stets „unsafe". Zudem sind diese Variablen im gesamten Programm + sichtbar, was bedeutet, dass der Borrow Checker nicht dabei helfen kann, + die Referenzen und die Ownership dieser Variablen zu verfolgen. +] else { todo } + += #(if lang == "en" [How do we do this in Rust?] + else if lang == "de" [Wie machen wir das in Rust?] + else { todo }) + +#if lang == "en" [ + Instead of just making our peripheral a global variable, we might + instead decide to make a structure, in this case called `PERIPHERALS`, + which contains an `Option` for each of our peripherals. +] else if lang == "de" [ + Anstatt unser Peripheriegerät einfach als globale Variable zu + definieren, könnten wir uns stattdessen dazu entschließen, eine Struktur + -- in diesem Fall mit dem Namen `PERIPHERALS` -- zu erstellen, die für + jedes unserer Peripheriegeräte ein `Option` enthält. +] else { todo } + +```rust +struct Peripherals { + serial: Option, +} +impl Peripherals { + fn take_serial(&mut self) -> SerialPort { + let p = replace(&mut self.serial, None); + p.unwrap() + } +} +static mut PERIPHERALS: Peripherals = Peripherals { + serial: Some(SerialPort), +}; +``` + +#if lang == "en" [ + This structure allows us to obtain a single instance of our peripheral. + If we try to call `take_serial()` more than once, our code will panic! +] else if lang == "de" [ + Diese Struktur ermöglicht es uns, eine einzelne Instanz unserer + Peripheriekomponente zu erhalten. Wenn wir versuchen, `take_serial()` + mehr als einmal aufzurufen, wird unser Code eine Panic auslösen! +] else { todo } + +#raw(block: true, lang: "rust", +"fn main() { + let serial_1 = unsafe { PERIPHERALS.take_serial() }; + // " + if lang == "en" { + "This panics!" + } else if lang == "de" { + "Dies loest eine panic aus!" + } else { todos } + " + // let serial_2 = unsafe { PERIPHERALS.take_serial() }; +} +") + +#if lang == "en" [ + Although interacting with this structure is `unsafe`, once we have the + `SerialPort` it contained, we no longer need to use `unsafe`, or the + `PERIPHERALS` structure at all. +] else if lang == "de" [ + Obwohl die Interaktion mit dieser Struktur als `unsafe` gilt, benötigen + wir -- sobald wir über die darin enthaltene `SerialPort`-Instanz + verfügen -- weder `unsafe`-Code noch die `PERIPHERALS`-Struktur selbst. +] else { todo } + +#if lang == "en" [ + This has a small runtime overhead because we must wrap the `SerialPort` + structure in an option, and we'll need to call `take_serial()` once, + however this small up-front cost allows us to leverage the borrow + checker throughout the rest of our program. +] else if lang == "de" [ + Dies bringt einen geringen Laufzeit-Overhead mit sich, da wir die + `SerialPort`-Struktur in eine `Option` einbetten und einmalig + `take_serial()` aufrufen müssen; dieser geringe anfängliche Aufwand + ermöglicht es uns jedoch, den Borrow-Checker im weiteren Verlauf des + Programms voll zu nutzen. +] else { todo } + += #(if lang == "en" [Existing library support] + else if lang == "de" [Vorhandene Bibliotheksunterstützung] + else { todo }) + +#if lang == "en" [ + Although we created our own `Peripherals` structure above, it is not + necessary to do this for your code. the `cortex_m` crate contains a + macro called `singleton!()` that will perform this action for you. +] else if lang == "de" [ + Obwohl wir oben unsere eigene `Peripherals`-Struktur erstellt haben, ist + dies für Ihren Code nicht erforderlich; das `cortex_m`-Crate enthält ein + Makro namens `singleton!()`, das diese Aufgabe für Sie übernimmt. +] else { todo } + +#raw(block: true, lang: "rust", +"use cortex_m::singleton; + +fn main() { + // " + if lang == "en" { + "OK if `main` is executed only once" + } else if lang == "de" { + "In Ordnung, wenn `main` nur einmal ausgefuehrt wird." + } else { todos } + " + let x: &'static mut bool = + singleton!(: bool = false).unwrap(); +} +") + +#let url_sinmacro = "https://docs.rs/cortex-m/latest/cortex_m/macro.singleton.html" +#if lang == "en" [ + #link(url_sinmacro)[cortex_m docs] +] else if lang == "de" [ + #link(url_sinmacro)[cortex_m Dokumente] +] else { todo } + +#let ln_rtic = link("https://github.com/rtic-rs/cortex-m-rtic")[`cortex-m-rtic`] +#if lang == "en" [ + Additionally, if you use #ln_rtic, the + entire process of defining and obtaining these peripherals are + abstracted for you, and you are instead handed a `Peripherals` structure + that contains a non-`Option` version of all of the items you define. +] else if lang == "de" [ + Wenn Sie zudem #ln_rtic + verwenden, wird der gesamte Prozess der Definition und Bereitstellung + dieser Peripheriekomponenten für Sie abstrahiert; stattdessen erhalten + Sie eine `Peripherals`-Struktur, die eine Version aller von Ihnen + definierten Elemente enthält, die nicht als `Option` vorliegen. +] else { todo } + + +#raw(block: true, lang: "rust", +"// cortex-m-rtic v0.5.x +#[rtic::app(device = lm3s6965, peripherals = true)] +const APP: () = { + #[init] + fn init(cx: init::Context) { + static mut X: u32 = 0; + + // " + if lang == "en" { + "Cortex-M peripherals" + } else if lang == "de" { + "Cortex-M-Peripherie" + } else { todos } + " + let core: cortex_m::Peripherals = cx.core; + + // " + if lang == "en" { + "Device specific peripherals" + } else if lang == "de" { + "Geraetespezifische Peripheriegeraete" + } else { todos } + " + let device: lm3s6965::Peripherals = cx.device; + } +} +") + += #(if lang == "en" [But why?] + else if lang == "de" [Aber warum?] + else { todo }) + +#if lang == "en" [ + But how do these Singletons make a noticeable difference in how our Rust + code works? +] else if lang == "de" [ + Aber wie bewirken diese Singletons einen spürbaren Unterschied in der + Funktionsweise unseres Rust-Codes? +] else { todo } + +#raw(block: true, lang: "rust", +"impl SerialPort { + const SER_PORT_SPEED_REG: *mut u32 = 0x4000_1000 as _; + + fn read_speed( + &self // " + if lang == "en" { + "<------ This is really, really important" + } else if lang == "de" { + "<------ Das ist wirklich, wirklich wichtig." + } else { todos } + " + ) -> u32 { + unsafe { + ptr::read_volatile(Self::SER_PORT_SPEED_REG) + } + } +} +") + +#if lang == "en" [ + There are two important factors in play here: + - Because we are using a singleton, there is only one way or place to + obtain a `SerialPort` structure + - To call the `read_speed()` method, we must have ownership or a + reference to a `SerialPort` structure +] else if lang == "de" [ + Hier spielen zwei wichtige Faktoren eine Rolle: + - Da wir ein Singleton verwenden, gibt es nur eine Möglichkeit bzw. + einen Ort, um eine `SerialPort`-Struktur zu erhalten. + - Um die Methode `read_speed()` aufzurufen, benötigen wir den Besitz + einer `SerialPort`-Struktur oder eine Referenz darauf. +] else { todo } + +#if lang == "en" [ + These two factors put together means that it is only possible to access + the hardware if we have appropriately satisfied the borrow checker, + meaning that at no point do we have multiple mutable references to the + same hardware! +] else if lang == "de" [ + Zusammengenommen bedeuten diese beiden Faktoren, dass ein Zugriff auf + die Hardware nur möglich ist, wenn wir die Anforderungen des + Borrow-Checkers ordnungsgemäß erfüllt haben -- das heißt, dass zu keinem + Zeitpunkt mehrere veränderbare Referenzen auf dieselbe Hardware + existieren! +] else { todo } + +#raw(block: true, lang: "rust", +"fn main() { + // " + if lang == "en" { + "missing reference to `self`! Won't work." + } else if lang == "de" { + "Verweis auf `self` fehlt! Das wird nicht funktionieren." + } else { todos } + " + // SerialPort::read_speed(); + + let serial_1 = unsafe { PERIPHERALS.take_serial() }; + + // " + if lang == "en" { + "you can only read what you have access to" + } else if lang == "de" { + "Sie koennen nur lesen, worauf Sie Zugriff haben" + } else { todos } + " + let _ = serial_1.read_speed(); +} +") + += #(if lang == "en" [Treat your hardware like data] + else if lang == "de" [Behandle deine Hardware wie Daten] + else { todo }) + +#if lang == "en" [ + Additionally, because some references are mutable, and some are + immutable, it becomes possible to see whether a function or method could + potentially modify the state of the hardware. For example, +] else if lang == "de" [ + Da es zudem sowohl veränderbare als auch unveränderbare Referenzen gibt, + lässt sich erkennen, ob eine Funktion oder Methode potenziell den + Zustand der Hardware ändern könnte. Zum Beispiel: +] else { todo } + +#if lang == "en" [ + This is allowed to change hardware settings: +] else if lang == "de" [ + Dies darf Hardware-Einstellungen ändern: +] else { todo } + +```rust +fn setup_spi_port( + spi: &mut SpiPort, + cs_pin: &mut GpioPin +) -> Result<()> { + // ... +} +``` + +#if lang == "en" [ + This isn't: +] else if lang == "de" [ + Das nicht: +] else { todo } + +```rust +fn read_button(gpio: &GpioPin) -> bool { + // ... +} +``` + +#if lang == "en" [ + This allows us to enforce whether code should or should not make changes + to hardware at *compile time*, rather than at runtime. As a note, + this generally only works across one application, but for bare metal + systems, our software will be compiled into a single application, so + this is not usually a restriction. +] else if lang == "de" [ + Dies ermöglicht es uns, bereits zur *Kompilierzeit* -- und nicht + erst zur Laufzeit -- festzulegen, ob Code Änderungen an der Hardware + vornehmen darf oder nicht. Zu beachten ist, dass dies im Allgemeinen nur + innerhalb einer einzelnen Anwendung funktioniert; da unsere Software für + Bare-Metal-Systeme jedoch als eine einzige Anwendung kompiliert wird, + stellt dies üblicherweise keine Einschränkung dar. +] else { todo } diff --git a/typ/portability/index.typ b/typ/portability/index.typ new file mode 100644 index 00000000..852cbf4d --- /dev/null +++ b/typ/portability/index.typ @@ -0,0 +1,281 @@ +#import "../config.typ": * + +#h1(if lang == "en" [Portability] + else if lang == "de" [Portabilität] + else { todo }) + +#set heading(offset: whole) + +#if lang == "en" [ + In embedded environments portability is a very important topic: Every + vendor and even each family from a single manufacturer offers different + peripherals and capabilities and similarly the ways to interact with the + peripherals will vary. +] else if lang == "de" [ + In eingebetteten Systemen ist Portabilität ein sehr wichtiges Thema: + Jeder Hersteller und sogar jede Produktfamilie eines einzelnen + Herstellers bietet unterschiedliche Peripheriegeräte und Funktionen an, + und dementsprechend variieren auch die Interaktionsmöglichkeiten mit den + Peripheriegeräten. +] else { todo } + +#if lang == "en" [ + A common way to equalize such differences is via a layer called Hardware + Abstraction layer or *HAL*. +] else if lang == "de" [ + Eine gängige Methode, diese Unterschiede auszugleichen, ist die + sogenannte Hardware-Abstraktionsschicht (HAL). +] else { todo } + +#quote(block: true)[ +#if lang == "en" [ + Hardware abstractions are sets of routines in software that emulate some + platform-specific details, giving programs direct access to the hardware + resources. +] else if lang == "de" [ + Hardware-Abstraktionen sind Sammlungen von Software-Routinen, die + bestimmte plattformspezifische Details emulieren und Programmen so + direkten Zugriff auf die Hardwareressourcen ermöglichen. +] else { todo } + +#if lang == "en" [ + They often allow programmers to write device-independent, high + performance applications by providing standard operating system (OS) + calls to hardware. +] else if lang == "de" [ + Sie ermöglichen es Programmierern häufig, geräteunabhängige + Hochleistungsanwendungen zu schreiben, indem sie standardisierte + Betriebssystemaufrufe für den Hardwarezugriff bereitstellen. +] else { todo } + +#let url_hal = "https://en.wikipedia.org/wiki/Hardware_abstraction" +#if lang == "en" [ + _Wikipedia: #link(url_hal)[Hardware Abstraction Layer]_ +] else if lang == "de" [ + _Wikipedia: #link(url_hal)[Hardware Abstraction Layer]_ +] else { todo } +] + +#if lang == "en" [ + Embedded systems are a bit special in this regard since we typically do + not have operating systems and user installable software but firmware + images which are compiled as a whole as well as a number of other + constraints. So while the traditional approach as defined by Wikipedia + could potentially work it is likely not the most productive approach to + ensure portability. +] else if lang == "de" [ + Eingebettete Systeme nehmen hier eine Sonderstellung ein, da wir es + typischerweise nicht mit Betriebssystemen und vom Benutzer + installierbarer Software zu tun haben, sondern mit Firmware-Images, die + als Ganzes kompiliert werden -- und zudem mit einer Reihe weiterer + Einschränkungen konfrontiert sind. Der klassische, auf Wikipedia + beschriebene Ansatz könnte zwar theoretisch funktionieren, ist aber + wahrscheinlich nicht der effizienteste Weg, um Portabilität zu + gewährleisten. +] else { todo } + +#let ln_hal = link("https://crates.io/crates/embedded-hal")[embedded-hal] +#if lang == "en" [ + How do we do this in Rust? Enter *#ln_hal*… +] else if lang == "de" [ + Wie gehen wir dabei in Rust vor? Hier kommt *#ln_hal* ins Spiel… +] else { todo } + +== #(if lang == "en" [What is #ln_hal?] + else if lang == "de" [Was ist #ln_hal?] + else { todo }) + +#if lang == "en" [ + In a nutshell it is a set of traits which define implementation + contracts between *HAL implementations*, *drivers* and + *applications (or firmwares)*. Those contracts include both + capabilities (i.e.~if a trait is implemented for a certain type, the + *HAL implementation* provides a certain capability) and methods + (i.e.~if you can construct a type implementing a trait it is guaranteed + that you have the methods specified in the trait available). +] else if lang == "de" [ + Kurz gesagt handelt es sich um eine Reihe von Merkmalen, die + Implementierungsverträge zwischen *HAL-Implementierungen*, + *Treibern* und *Anwendungen (oder Firmware)* definieren. + Diese Verträge umfassen sowohl Fähigkeiten (d.~h., wenn ein Merkmal für + einen bestimmten Typ implementiert ist, stellt die + *HAL-Implementierung* eine bestimmte Fähigkeit bereit) als auch + Methoden (d.~h., wenn ein Typ, der ein Merkmal implementiert, erstellt + werden kann, ist garantiert, dass die im Merkmal spezifizierten Methoden + verfügbar sind). +] else { todo } + +#if lang == "en" [ + A typical layering might look like this: +] else if lang == "de" [ + Ein typischer Schichtaufbau könnte wie folgt aussehen: +] else { todo } + +#box(image("../assets/rust_layers.svg")) + +#if lang == "en" [ + Some of the defined traits in *#ln_hal* are: + - GPIO (input and output pins) + - Serial communication + - I2C + - SPI + - Timers/Countdowns + - Analog Digital Conversion +] else if lang == "de" [ + Einige der in *#ln_hal* definierten Traits sind: + - GPIO (Eingangs- und Ausgangspins) + - Serielle Kommunikation + - I2C + - SPI + - Timers/Countdowns + - Analog-Digital-Umsetzung +] else { todo } + +#if lang == "en" [ + The main reason for having the *embedded-hal* traits and crates + implementing and using them is to keep complexity in check. If you + consider that an application might have to implement the use of the + peripheral in the hardware as well as the application and potentially + drivers for additional hardware components, then it should be easy to + see that the re-usability is very limited. Expressed mathematically, if + *M* is the number of peripheral HAL implementations and + *N* the number of drivers then if we were to reinvent the wheel + for every application then we would end up with *M\*N* + implementations while by using the _API_ provided by the *#ln_hal* + traits will make the implementation complexity approach *M+N*. Of + course there're additional benefits to be had, such as less + trial-and-error due to a well-defined and ready-to-use APIs. +] else if lang == "de" [ + Der Hauptgrund für die Existenz der *embedded-hal*-Traits und der + sie implementierenden sowie nutzenden Crates liegt darin, die + Komplexität beherrschbar zu halten. Bedenkt man, dass eine Anwendung + sowohl die Nutzung der Hardware-Peripherie als auch die eigentliche + Anwendungslogik sowie möglicherweise Treiber für zusätzliche + Hardwarekomponenten implementieren muss, wird schnell klar, dass die + Wiederverwendbarkeit stark eingeschränkt ist. Mathematisch ausgedrückt: + Wenn *M* die Anzahl der HAL-Implementierungen für + Peripheriegeräte und *N* die Anzahl der Treiber ist, würde man -- + müsste man für jede Anwendung das Rad neu erfinden -- bei *M × N* + Implementierungen landen; durch die Verwendung der von den *#ln_hal*-Traits + bereitgestellten API nähert sich die Implementierungskomplexität + hingegen *M + N* an. Natürlich ergeben sich daraus weitere + Vorteile, wie etwa ein geringerer Aufwand durch „Trial-and-Error" dank + wohldefinierter und sofort einsatzbereiter APIs. +] else { todo } + +== #(if lang == "en" [Users of the #ln_hal] + else if lang == "de" [Nutzer von #ln_hal] + else { todo }) + +#if lang == "en" [ + As said above there are three main users of the HAL: +] else if lang == "de" [ + Wie bereits erwähnt, gibt es drei Hauptnutzer einer HAL: +] else { todo } + +=== #(if lang == "en" [HAL implementation] + else if lang == "de" [HAL-Implementierung] + else { todo }) + +#if lang == "en" [ + A HAL implementation provides the interfacing between the hardware and + the users of the HAL traits. Typical implementations consist of three + parts: + - One or more hardware specific types + - Functions to create and initialize such a type, + often providing various configuration options + (speed, operation mode, use pins, etc.) + - one or more `trait` `impl` of *#ln_hal* traits for that type +] else if lang == "de" [ + Eine HAL-Implementierung stellt die Schnittstelle zwischen der Hardware + und den Nutzern der HAL-Traits bereit. Typische Implementierungen + bestehen aus drei Teilen: + - Ein oder mehrere hardwarespezifische Typen + - Funktionen zum Erstellen und Initialisieren eines solchen Typs, die + häufig verschiedene Konfigurationsoptionen (Geschwindigkeit, + Betriebsmodus, verwendete Pins usw.) bereitstellen. + - eine oder mehrere `trait`-Implementierungen (`impl`) von + *#ln_hal*-Traits für diesen Typ +] else { todo } + +#if lang == "en" [ + Such a *HAL implementation* can come in various flavours: + - Via low-level hardware access, e.g.~via registers + - Via operating system, e.g.~by using the `sysfs` under Linux + - Via adapter, e.g.~a mock of types for unit testing + - Via driver for hardware adapters, e.g.~I2C multiplexer or GPIO expander +] else if lang == "de" [ + Eine solche *HAL-Implementierung* kann in verschiedenen + Ausprägungen vorliegen: + - Über hardwarenahen Zugriff, z. B. über Register + - Über das Betriebssystem, z. B. durch Verwendung von `sysfs` unter Linux + - Über einen Adapter, z. B. ein Mock von Typen für Unit-Tests + - Via-Treiber für Hardware-Adapter, z. B. I2C-Multiplexer oder GPIO-Expander +] else { todo } + +=== #(if lang == "en" [Driver] + else if lang == "de" [Treiber] + else { todo }) + +#if lang == "en" [ + A driver implements a set of custom functionality for an internal or + external component, connected to a peripheral implementing the + #ln_hal traits. + Typical examples for such drivers include various sensors (temperature, + magnetometer, accelerometer, light), display devices (LED arrays, LCD + displays) and actuators (motors, transmitters). +] else if lang == "de" [ + Ein Treiber implementiert eine Reihe benutzerdefinierter Funktionen für + eine interne oder externe Komponente, die mit einem Peripheriegerät + verbunden ist, das die #ln_hal;-Traits + implementiert. Typische Beispiele für solche Treiber sind verschiedene + Sensoren (Temperatur-, Magnetometer-, Beschleunigungs- und + Lichtsensoren), Anzeigegeräte (LED-Arrays, LCD-Displays) und Aktoren + (Motoren, Sender). +] else { todo } + +#if lang == "en" [ + A driver has to be initialized with an instance of type that implements + a certain `trait` of the #ln_hal which is + ensured via trait bound and provides its own type instance with a custom + set of methods allowing to interact with the driven device. +] else if lang == "de" [ + Ein Treiber muss mit einer Instanz eines Typs initialisiert werden, der + ein bestimmtes `Trait` von + #ln_hal + implementiert. Dies wird durch Trait-Bound sichergestellt. Der Treiber + stellt eine eigene Typinstanz mit einem benutzerdefinierten Satz von + Methoden bereit, die die Interaktion mit dem angesteuerten Gerät +] else { todo } + +=== #(if lang == "en" [Application] + else if lang == "de" [Anwendung] + else { todo }) + +#if lang == "en" [ + The application binds the various parts together and ensures that the + desired functionality is achieved. When porting between different + systems, this is the part which requires the most adaptation efforts, + since the application needs to correctly initialize the real hardware + via the HAL implementation and the initialisation of different hardware + differs, sometimes drastically so. Also the user choice often plays a + big role, since components can be physically connected to different + terminals, hardware buses sometimes need external hardware to match the + configuration or there are different trade-offs to be made in the use of + internal peripherals (e.g.~multiple timers with different capabilities + are available or peripherals conflict with others). +] else if lang == "de" [ + Die Anwendung verknüpft die verschiedenen Komponenten miteinander und + stellt sicher, dass die gewünschte Funktionalität erreicht wird. Bei der + Portierung zwischen verschiedenen Systemen ist dies der Teil, der den + größten Anpassungsaufwand erfordert, da die Anwendung die eigentliche + Hardware über die HAL-Implementierung korrekt initialisieren muss -- und + die Initialisierung unterschiedlicher Hardware variiert, bisweilen sogar + drastisch. Zudem spielen oft anwenderspezifische Entscheidungen eine + wichtige Rolle: Komponenten können physisch an unterschiedliche + Anschlüsse angebunden sein, Hardware-Busse erfordern mitunter externe + Hardware zur Konfigurationsanpassung, oder es müssen Abwägungen bei der + Nutzung interner Peripherie getroffen werden (etwa wenn mehrere Timer + mit unterschiedlichen Fähigkeiten zur Verfügung stehen oder Konflikte + zwischen verschiedenen Peripherieeinheiten auftreten). +] else { todo } diff --git a/typ/start/exceptions.typ b/typ/start/exceptions.typ new file mode 100644 index 00000000..89b8a472 --- /dev/null +++ b/typ/start/exceptions.typ @@ -0,0 +1,529 @@ +#import "../config.typ": * + +#h1(offset: whole, + if lang == "en" [Exceptions] + else if lang == "de" [Ausnahmen (Exceptions)] + else { todo }) + +#set heading(offset: whole*2) + +#if lang == "en" [ + Exceptions, and interrupts, are a hardware mechanism by which the + processor handles asynchronous events and fatal errors (e.g.~executing + an invalid instruction). Exceptions imply preemption and involve + exception handlers, subroutines executed in response to the signal that + triggered the event. +] else if lang == "de" [ + Ausnahmen und Interrupts sind ein Hardwaremechanismus, mit dem der + Prozessor asynchrone Ereignisse und schwerwiegende Fehler (z. B. die + Ausführung einer ungültigen Anweisung) behandelt. Ausnahmen implizieren + Präemption und erfordern Ausnahmebehandlungsroutinen, die als Reaktion + auf das auslösende Signal ausgeführt werden. +] else { todo } + +#let ln_ex = link("https://docs.rs/cortex-m-rt-macros/latest/cortex_m_rt_macros/attr.exception.html")[`exception`] +#if lang == "en" [ + The `cortex-m-rt` crate provides an #ln_ex attribute to declare exception handlers. +] else if lang == "de" [ + Die `cortex-m-rt`-Crate bietet das Attribut #ln_ex zur Deklaration von Ausnahmebehandlungsroutinen. +] else { todo } + +#raw(block: true, lang: "rust", +"// " + if lang == "en" { + "Exception handler for the SysTick (System Timer) exception" + } else if lang == "de" { + "Ausnahmebehandlungsroutine fuer die SysTick-Ausnahme (System-Timer)" + } else { todos } + " +#[exception] +fn SysTick() { + // .. +} +") + +#if lang == "en" [ + Other than the `exception` attribute exception handlers look like plain + functions but there's one more difference: `exception` handlers can + _not_ be called by software. Following the previous example, the + statement `SysTick();` would result in a compilation error. +] else if lang == "de" [ + Abgesehen vom `exception`-Attribut sehen Exception-Handler wie + gewöhnliche Funktionen aus, doch es gibt noch einen weiteren + Unterschied: `exception`-Handler können _nicht_ per Software + aufgerufen werden. Bezogen auf das vorherige Beispiel würde die + Anweisung `SysTick();` zu einem Kompilierfehler führen. +] else { todo } + +#if lang == "en" [ + This behavior is pretty much intended and it's required to provide a + feature: `static mut` variables declared _inside_ `exception` + handlers are _safe_ to use. +] else if lang == "de" [ + Dieses Verhalten ist durchaus beabsichtigt und notwendig, um eine + bestimmte Eigenschaft zu gewährleisten: `static mut`-Variablen, die + _innerhalb_ von `exception`-Handlern deklariert werden, sind + _sicher_ in der Verwendung. +] else { todo } + +#raw(block: true, lang: "rust", +"#[exception] +fn SysTick() { + static mut COUNT: u32 = 0; + // " + if lang == "en" { + "`COUNT` has transformed to type `&mut u32` and it's safe to use" + } else if lang == "de" { + "`COUNT` wurde in den Typ `&mut u32` umgewandelt und ist sicher in der + // Verwendung." + } else { todos } + " + *COUNT += 1; +") + +#let url_re = "https://en.wikipedia.org/wiki/Reentrancy_(computing)" +#if lang == "en" [ + As you may know, using `static mut` variables in a function makes it + #link(url_re)[_non-reentrant_]. + It's undefined behavior to call a non-reentrant function, directly or + indirectly, from more than one exception / interrupt handler or from + `main` and one or more exception / interrupt handlers. +] else if lang == "de" [ + Wie Ihnen vielleicht bekannt ist, führt die Verwendung von + `static mut`-Variablen in einer Funktion dazu, dass diese + #link(url_re)[_nicht wiedereintrittsfähig_] + ist. Es stellt undefiniertes Verhalten dar, eine nicht + wiedereintrittsfähige Funktion direkt oder indirekt aus mehr als einem + Ausnahme- oder Interrupt-Handler oder aus `main` und einem oder mehreren + Ausnahme- oder Interrupt-Handlern heraus aufzurufen. +] else { todo } + +#if lang == "en" [ + Safe Rust must never result in undefined behavior so non-reentrant + functions must be marked as `unsafe`. Yet I just told that `exception` + handlers can safely use `static mut` variables. How is this possible? + This is possible because `exception` handlers can _not_ be called + by software thus reentrancy is not possible. These handlers are called + by the hardware itself which is assumed to be physically non-concurrent. +] else if lang == "de" [ + Safe Rust darf niemals zu undefiniertem Verhalten führen; daher müssen + nicht-wiedereintrittsfähige Funktionen als `unsafe` gekennzeichnet + werden. Dennoch habe ich gerade erwähnt, dass Ausnahme-Handler + (`exception handlers`) sicher `static mut`-Variablen verwenden können. + Wie ist das möglich? Dies ist möglich, da Ausnahme-Handler _nicht_ + per Software aufgerufen werden können und somit kein Wiedereintritt + (Reentrancy) stattfinden kann. Diese Handler werden von der Hardware + selbst aufgerufen, bei der davon ausgegangen wird, dass sie physisch + keine Nebenläufigkeit aufweist. +] else { todo } + +#if lang == "en" [ + As a result, in the context of exception handlers in embedded systems, + the absence of concurrent invocations of the same handler ensures that + there are no reentrancy issues, even if the handler uses static mutable + variables. +] else if lang == "de" [ + Im Kontext von Ausnahme-Handlern in eingebetteten Systemen stellt das + Ausbleiben gleichzeitiger Aufrufe desselben Handlers folglich sicher, + dass keine Probleme mit der Wiedereintrittsfähigkeit auftreten -- selbst + dann nicht, wenn der Handler veränderbare statische Variablen verwendet +] else { todo } + +#if lang == "en" [ + In a multicore system, where multiple processor cores are executing code + concurrently, the potential for reentrancy issues becomes relevant + again, even within exception handlers. While each core may have its own + set of exception handlers, there can still be scenarios where multiple + cores attempt to execute the same exception handler simultaneously. \ + To address this concern in a multicore environment, proper synchronization + mechanisms need to be employed within the exception handlers to ensure + that access to shared resources is properly coordinated among the cores. + This typically involves the use of techniques such as locks, semaphores, + or atomic operations to prevent data races and maintain data integrity +] else if lang == "de" [ + In einem Multicore-System, in dem mehrere Prozessorkerne gleichzeitig + Code ausführen, gewinnt die Problematik der Wiedereintrittsfähigkeit + (Reentrancy) erneut an Bedeutung -- selbst innerhalb von + Exception-Handlern. Auch wenn jeder Kern über eigene Exception-Handler + verfügen mag, kann es dennoch zu Situationen kommen, in denen mehrere + Kerne versuchen, denselben Exception-Handler simultan auszuführen. Um + dieser Herausforderung in einer Multicore-Umgebung zu begegnen, müssen + innerhalb der Exception-Handler geeignete Synchronisationsmechanismen + eingesetzt werden; so wird sichergestellt, dass der Zugriff auf + gemeinsam genutzte Ressourcen zwischen den Kernen korrekt koordiniert + wird. Dies geschieht typischerweise durch den Einsatz von Techniken wie + Locks, Semaphoren oder atomaren Operationen, um Race Conditions zu + vermeiden und die Datenintegrität zu wahren. +] else { todo } + +#quote(block: true)[ +#if lang == "en" [ + Note that the `exception` attribute transforms definitions of static + variables inside the function by wrapping them into `unsafe` blocks and + providing us with new appropriate variables of type `&mut` of the same + name. Thus we can dereference the reference via `*` to access the values + of the variables without needing to wrap them in an `unsafe` block. +] else if lang == "de" [ + Beachten Sie, dass das Attribut `exception` Definitionen statischer + Variablen innerhalb der Funktion umwandelt, indem es sie in + `unsafe`-Blöcke einschließt und uns neue, passende Variablen des Typs + `&mut` mit demselben Namen zur Verfügung stellt. Auf diese Weise können + wir die Referenz mittels `*` dereferenzieren, um auf die Werte der + Variablen zuzugreifen, ohne sie selbst in einen `unsafe`-Block + einschließen zu müssen. +] else { todo } +] + += #(if lang == "en" [A complete example] + else if lang == "de" [Ein vollständiges Beispiel] + else { todo }) + +#if lang == "en" [ + Here's an example that uses the system timer to raise a `SysTick` + exception roughly every second. The `SysTick` exception handler keeps + track of how many times it has been called in the `COUNT` variable and + then prints the value of `COUNT` to the host console using semihosting. + of the variables without needing to wrap them in an `unsafe` block. +] else if lang == "de" [ + Hier ist ein Beispiel, das den System-Timer verwendet, um etwa jede + Sekunde eine `SysTick`-Exception auszulösen. Der + `SysTick`-Exception-Handler protokolliert in der Variablen `COUNT`, wie + oft er aufgerufen wurde, und gibt anschließend den Wert von `COUNT` + mittels Semihosting auf der Host-Konsole aus. +] else { todo } + +#quote(block: true)[ +#if lang == "en" [ + *NOTE*: You can run this example on any Cortex-M device; you can + also run it on QEMU +] else if lang == "de" [ + *HINWEIS*: Sie können dieses Beispiel auf jedem Cortex-M-Gerät + ausführen; es lässt sich auch unter QEMU ausführen. +] else { todo } +] + +#raw(block: true, lang: "rust", +"#![deny(unsafe_code)] +#![no_main] +#![no_std] + +use panic_halt as _; + +use core::fmt::Write; + +use cortex_m::peripheral::syst::SystClkSource; +use cortex_m_rt::{entry, exception}; +use cortex_m_semihosting::{ + debug, + hio::{self, HostStream}, +}; + +#[entry] +fn main() -> ! { + let p = cortex_m::Peripherals::take().unwrap(); + let mut syst = p.SYST; + + // " + if lang == "en" { + "configures the system timer to trigger a SysTick exception every second" + } else if lang == "de" { + "konfiguriert den System-Timer so, dass er jede Sekunde eine SysTick- + // Exception ausloest" + } else { todos } + " + syst.set_clock_source(SystClkSource::Core); + // " + if lang == "en" { + "this is configured for the LM3S6965 which has a default CPU clock of 12 MHz" + } else if lang == "de" { + "Dies ist für den LM3S6965 konfiguriert, der einen Standard-CPU-Takt von + // 12 MHz hat" + } else { todos } + " + syst.set_reload(12_000_000); + syst.clear_current(); + syst.enable_counter(); + syst.enable_interrupt(); + + loop {} +} + +#[exception] +fn SysTick() { + static mut COUNT: u32 = 0; + static mut STDOUT: Option = None; + + *COUNT += 1; + + // " + if lang == "en" { + "Lazy initialization" + } else if lang == "de" { + "Verzoegerte Initialisierung" + } else { todos } + " + if STDOUT.is_none() { + *STDOUT = hio::hstdout().ok(); + } + + if let Some(hstdout) = STDOUT.as_mut() { + write!(hstdout, \"{}\", *COUNT).ok(); + } + + // " + if lang == "en" { + "IMPORTANT omit this `if` block if running on real hardware or your + // debugger will end in an inconsistent state" + } else if lang == "de" { + "WICHTIG: Lassen Sie diesen `if`-Block weg, wenn Sie das Programm auf + // echter Hardware ausfuehren, da sich Ihr Debugger sonst in + // einem inkonsistenten Zustand befinden wird." + } else { todos } + " + if *COUNT == 9 { + // " + if lang == "en" { + "This will terminate the QEMU process" + } else if lang == "de" { + "Dies beendet den QEMU-Prozess." + } else { todos } + " + debug::exit(debug::EXIT_SUCCESS); + } +} +") + +```console +tail -n5 Cargo.toml +``` + +```toml +[dependencies] +cortex-m = "0.5.7" +cortex-m-rt = "0.6.3" +panic-halt = "0.2.0" +cortex-m-semihosting = "0.3.1" +``` + +```text +$ cargo run --release + Running `qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb (..) +123456789 +``` + +#if lang == "en" [ + If you run this on the Discovery board you'll see the output on the + OpenOCD console. Also, the program will _not_ stop when the count + reaches 9. +] else if lang == "de" [ + Wenn Sie dies auf dem Discovery-Board ausführen, sehen Sie die Ausgabe + in der OpenOCD-Konsole. Außerdem hält das Programm _nicht_ an, wenn + der Zählerstand 9 erreicht. +] else { todo } + += #(if lang == "en" [The default exception handler] + else if lang == "de" [Der Standard-Exception-Handler] + else { todo }) + +#if lang == "en" [ + What the `exception` attribute actually does is _override_ the + default exception handler for a specific exception. If you don't + override the handler for a particular exception it will be handled by + the `DefaultHandler` function, which defaults to: +] else if lang == "de" [ + Das `exception`-Attribut bewirkt eigentlich, dass der + Standard-Exception-Handler für eine bestimmte Exception + _überschrieben_ wird. Wenn Sie den Handler für eine bestimmte + Exception nicht überschreiben, wird sie von der Funktion + `DefaultHandler` behandelt, die standardmäßig Folgendes tut: +] else { todo } + +```rust +fn DefaultHandler() { + loop {} +} +``` + +#if lang == "en" [ + This function is provided by the `cortex-m-rt` crate and marked as + `#[no_mangle]` so you can put a breakpoint on "DefaultHandler" and catch + _unhandled_ exceptions. +] else if lang == "de" [ + Diese Funktion wird vom `cortex-m-rt`-Crate bereitgestellt und ist mit + `#[no_mangle]` markiert, sodass Sie einen Breakpoint auf + „DefaultHandler" setzen und _unbehandelte_ Exceptions abfangen + können. +] else { todo } + +#if lang == "en" [ + It's possible to override this `DefaultHandler` using the `exception` + attribute: +] else if lang == "de" [ + Es ist möglich, diesen `DefaultHandler` mithilfe des + `exception`-Attributs zu überschreiben: +] else { todo } + +#raw(block: true, lang: "rust", +"#[exception] +fn DefaultHandler(irqn: i16) { + // " + if lang == "en" { + "custom default handler" + } else if lang == "de" { + "benutzerdefinierter Standard-Handler" + } else { todos } + " +} +") + +#if lang == "en" [ + The `irqn` argument indicates which exception is being serviced. A + negative value indicates that a Cortex-M exception is being serviced; + and zero or a positive value indicate that a device specific exception, + AKA interrupt, is being serviced. +] else if lang == "de" [ + Das Argument `irqn` gibt an, welche Exception gerade bearbeitet wird. + Ein negativer Wert zeigt an, dass eine Cortex-M-Exception bearbeitet + wird, während ein Wert von null oder ein positiver Wert darauf hinweist, + dass eine gerätespezifische Exception -- auch als Interrupt bezeichnet + -- bearbeitet wird. +] else { todo } + += #(if lang == "en" [The hard fault handler] + else if lang == "de" [Der "Schwere Fehler"-Handler] + else { todo }) + +#if lang == "en" [ + The `HardFault` exception is a bit special. This exception is fired when + the program enters an invalid state so its handler can _not_ return + as that could result in undefined behavior. Also, the runtime crate does + a bit of work before the user defined `HardFault` handler is invoked to + improve debuggability. +] else if lang == "de" [ + Die `HardFault`-Exception nimmt eine Sonderstellung ein. Sie wird + ausgelöst, wenn das Programm in einen ungültigen Zustand gerät; daher + darf ihr Handler nicht zurückkehren, da dies zu undefiniertem Verhalten + führen könnte. Zudem führt die Runtime-Crate einige Vorarbeiten durch, + bevor der benutzerdefinierte `HardFault`-Handler aufgerufen wird, um die + Fehlersuche (Debugging) zu erleichtern. +] else { todo } + +#if lang == "en" [ + The result is that the `HardFault` handler must have the following + signature: `fn(&ExceptionFrame) -> !`. The argument of the handler is a + pointer to registers that were pushed into the stack by the exception. + These registers are a snapshot of the processor state at the moment the + exception was triggered and are useful to diagnose a hard fault. +] else if lang == "de" [ + Daraus ergibt sich, dass der `HardFault`-Handler folgende Signatur + aufweisen muss: `fn(&ExceptionFrame) -> !`. Das Argument des Handlers + ist ein Zeiger auf Register, die beim Auftreten der Exception auf den + Stack gesichert wurden. Diese Register stellen eine Momentaufnahme des + Prozessorzustands zum Zeitpunkt der Auslösung der Exception dar und sind + für die Diagnose eines Hard Faults hilfreich. +] else { todo } + +#if lang == "en" [ + Here's an example that performs an illegal operation: a read to a + nonexistent memory location. +] else if lang == "de" [ + Hier ist ein Beispiel für eine unzulässige Operation: der Lesezugriff + auf eine nicht existierende Speicheradresse. +] else { todo } + +#quote(block: true)[ +#if lang == "en" [ + *NOTE*: This program won't work, i.e.~it won't crash, on QEMU + because `qemu-system-arm -machine lm3s6965evb` doesn't check memory + loads and will happily return `0`on reads to invalid memory. +] else if lang == "de" [ + *HINWEIS*: Dieses Programm wird unter QEMU nicht fehlschlagen + (d.~h. es stürzt nicht ab), da `qemu-system-arm -machine lm3s6965evb` + keine Überprüfung von Speicherzugriffen durchführt und beim Lesen von + ungültigem Speicher problemlos den Wert `0` zurückgibt. +] else { todo } +] + + +#raw(block: true, lang: "rust", +"#![no_main] +#![no_std] + +use panic_halt as _; + +use core::fmt::Write; +use core::ptr; + +use cortex_m_rt::{entry, exception, ExceptionFrame}; +use cortex_m_semihosting::hio; + +#[entry] +fn main() -> ! { + // " + if lang == "en" { + "read a nonexistent memory location" + } else if lang == "de" { + "liest einen nicht existierenden Speicherbereich" + } else { todos } + " + unsafe { + ptr::read_volatile(0x3FFF_0000 as *const u32); + } + + loop {} +} + +#[exception] +fn HardFault(ef: &ExceptionFrame) -> ! { + if let Ok(mut hstdout) = hio::hstdout() { + writeln!(hstdout, \"{:#?}\", ef).ok(); + } + + loop {} +} +") + +#if lang == "en" [ + The `HardFault` handler prints the `ExceptionFrame` value. If you run + this you'll see something like this on the OpenOCD console. +] else if lang == "de" [ + Der `HardFault`-Handler gibt den Wert des `ExceptionFrame` aus. Wenn Sie + dies ausführen, sehen Sie auf der OpenOCD-Konsole eine Ausgabe wie diese. +] else { todo } + +```text +$ openocd +(..) +ExceptionFrame { + r0: 0x3fff0000, + r1: 0x00000003, + r2: 0x080032e8, + r3: 0x00000000, + r12: 0x00000000, + lr: 0x080016df, + pc: 0x080016e2, + xpsr: 0x61000000, +} +``` + +#if lang == "en" [ + The `pc` value is the value of the Program Counter at the time of the + exception and it points to the instruction that triggered the exception. +] else if lang == "de" [ + Der Wert `pc` ist der Wert des Programmzählers zum Zeitpunkt der + Ausnahme und verweist auf die Anweisung, die die Ausnahme ausgelöst hat. +] else { todo } + +#if lang == "en" [ + If you look at the disassembly of the program: +] else if lang == "de" [ + Wenn Sie sich den Disassembler des Programms ansehen: +] else { todo } + +```text +$ cargo objdump --bin app --release -- -d --no-show-raw-insn --print-imm-hex +(..) +ResetTrampoline: + 8000942: movw r0, #0xfffe + 8000946: movt r0, #0x3fff + 800094a: ldr r0, [r0] + 800094c: b #-0x4 +``` + +#if lang == "en" [ + You can lookup the value of the program counter `0x0800094a` in the + disassembly. You'll see that a load operation (`ldr r0, [r0]` ) caused + the exception. The `r0` field of `ExceptionFrame` will tell you the + value of register `r0` was `0x3fff_fffe` at that time. +] else if lang == "de" [ + Den Wert des Programmzählers `0x0800094a` können Sie in der + Disassemblierung nachschlagen. + + Sie werden sehen, dass eine Ladeoperation (`ldr r0, [r0]`) die Ausnahme + verursacht hat. + + Das Feld `r0` von `ExceptionFrame` gibt an, dass der Wert des Registers + `r0` zu diesem Zeitpunkt `0x3fff_fffe` war. +] else { todo } diff --git a/typ/start/hardware.typ b/typ/start/hardware.typ new file mode 100644 index 00000000..14b0c483 --- /dev/null +++ b/typ/start/hardware.typ @@ -0,0 +1,619 @@ +#import "../config.typ": * + +#h1(offset: whole, + if lang in ("en", "de") [Hardware] + else { todo }) + +#set heading(offset: whole*2) + +#if lang == "en" [ + By now you should be somewhat familiar with the tooling and the + development process. In this section we'll switch to real hardware; the + process will remain largely the same. Let's dive in. +] else if lang == "de" [ + Mittlerweile sollten Sie mit den Werkzeugen und dem Entwicklungsprozess + einigermaßen vertraut sein. In diesem Abschnitt wechseln wir zur echten + Hardware; der Prozess bleibt im Großen und Ganzen gleich. Tauchen wir + ein. +] else { todo } + += #(if lang == "en" [Know your hardware] + else if lang == "de" [Kennen Sie Ihre Hardware] + else { todo }) + +#if lang == "en" [ + Before we begin you need to identify some characteristics of the target + device as these will be used to configure the project: + - The ARM core. e.g.~Cortex-M3. + - Does the ARM core include an FPU? Cortex-M4#[*F*] and + Cortex-M7#[*F*] cores do. + - How much Flash memory and RAM does the target device have? e.g.~256 + KiB of Flash and 32 KiB of RAM. + - Where are Flash memory and RAM mapped in the address space? e.g.~RAM + is commonly located at address `0x2000_0000`. +] else if lang == "de" [ + Bevor wir beginnen, müssen Sie einige Eigenschaften des Zielgeräts + identifizieren da diese zur Konfiguration des Projekts verwendet werden: + - Der ARM-Kern, z.B. Cortex-M3. + - Enthält der ARM-Kern eine FPU? Cortex-M4#[*F*] und + Cortex-M7#[*F*]-Kerne tun dies. + - Wie viel Flash-Speicher und RAM hat das Zielgerät? z.B. 256 KiB von + Flash und 32 KiB RAM. + - Wo werden Flash-Speicher und RAM im Adressraum abgebildet? zB RAM ist + befindet sich üblicherweise an der Adresse `0x2000_0000`. +] else { todo } + +#if lang == "en" [ + You can find this information in the data sheet or the reference manual + of your device. +] else if lang == "de" [ + Diese Informationen finden Sie im Datenblatt oder im Referenzhandbuch + Ihres/Ihrer Microcontrollers/Microcontrollerplatine. +] else { todo } + +#if lang == "en" [ + In this section we'll be using our reference hardware, the + STM32F3DISCOVERY. This board contains an STM32F303VCT6 microcontroller. + This microcontroller has: + - A Cortex-M4F core that includes a single precision FPU + - 256 KiB of Flash located at address 0x0800_0000. + - 40 KiB of RAM located at address 0x2000_0000. (There's another RAM + region but for simplicity we'll ignore it). +] else if lang == "de" [ + In diesem Abschnitt verwenden wir unsere Referenzhardware, die + STM32F3DISCOVERY. Diese Platine enthält einen Mikrocontroller + STM32F303VCT6. Dieser Mikrocontroller verfügt über: + - Ein Cortex-M4F-Kern, der eine einzelne Präzisions-FPU enthält + - 256 KiB Flash befinden sich an der Adresse 0x0800_0000. + - 440 KiB RAM an der Adresse 0x2000_0000. (Es gibt noch eine weitere + RAM-Region, aber der Einfachheit halber ignorieren wir das). +] else { todo } + += #(if lang == "en" [Configuring] + else if lang == "de" [Konfigurieren] + else { todo }) + +#if lang == "en" [ + We'll start from scratch with a fresh template instance. Refer to the + #link( )[previous section on QEMU] for a refresher on how to do + this without `cargo-generate`. +] else if lang == "de" [ + Wir beginnen bei Null mit einer neuen Vorlageninstanz. Zur Auffrischung + siehe #link()[vorheriger Abschnitt zu QEMU], wie man das ohne + `cargo-generate` macht. +] else { todo } + +```text +$ cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart + Project Name: app + Creating project called `app`... + Done! New project created /tmp/app + +$ cd app +``` + +#if lang == "en" [ + Step number one is to set a default compilation target in `.cargo/config.toml`. +] else if lang == "de" [ + Schritt Nummer eins besteht darin, ein Standardkompilierungsziel in + `.cargo/config.toml` festzulegen. +] else { todo } + +```console +tail -n5 .cargo/config.toml +``` + +#if lang == "en" [ + ```toml + # Pick ONE of these compilation targets + # target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ + # target = "thumbv7m-none-eabi" # Cortex-M3 + # target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU) + target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) + ``` +] else if lang == "de" [ + ```toml + # Waehlen Sie EINES von diesen Kompilierungszielen aus + # target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ + # target = "thumbv7m-none-eabi" # Cortex-M3 + # target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU) + target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) + ``` +] else { todo } + +#if lang == "en" [ + We'll use `thumbv7em-none-eabihf` as that covers the Cortex-M4F core. +] else if lang == "de" [ + Wir verwenden `thumbv7em-none-eabihf`, da es den Cortex-M4F-Kern abdeckt. +] else { todo } + +#quote(block: true)[ +#if lang == "en" [ + *NOTE*: As you may remember from the previous chapter, we have to + install all targets and this is a new one. So don't forget to run the + installation process `rustup target add thumbv7em-none-eabihf` for + this target. +] else if lang == "de" [ + *HINWEIS*: Wie Sie sich vielleicht aus dem vorherigen + Kapitel erinnern, müssen wir alle Ziele installieren, dies ist ein + neues Ziel. Vergessen Sie also nicht, die Installationsprozess + `rustup target add thumbv7em-none-eabihf` für dieses Ziel auszuführen. +] else { todo } +] + +#if lang == "en" [ + The second step is to enter the memory region information into the `memory.x` file. +] else if lang == "de" [ + Der zweite Schritt besteht darin, die Speicherbereichsinformationen in die Datei `memory.x` einzugeben +] else { todo } + +```text +$ cat memory.x +/* Linker script for the STM32F303VCT6 */ +MEMORY +{ + /* NOTE 1 K = 1 KiBi = 1024 bytes */ + FLASH : ORIGIN = 0x08000000, LENGTH = 256K + RAM : ORIGIN = 0x20000000, LENGTH = 40K +} +``` + +#quote(block: true)[ +#if lang == "en" [ + *NOTE*: If you for some reason changed the `memory.x` file after + you had made the first build of a specific build target, then do + `cargo clean` before `cargo build`, because `cargo build` may not track + updates of `memory.x`. +] else if lang == "de" [ + *NOTE*: Wenn Sie aus irgendeinem Grund die Datei `memory.x` + geändert haben, nachdem Sie den ersten Build für ein bestimmtes + Build-Ziel erstellt haben, dann führen Sie `cargo clean` vor + `cargo build` aus, da `cargo build` keine Änderungen von `memory.x` verfolgt. +] else { todo } +] + +#if lang == "en" [ + We'll start with the hello example again, but first we have to make a + small change. +] else if lang == "de" [ + Wir beginnen noch einmal mit dem Hallo-Beispiel, aber zuerst müssen wir + eine kleine Änderung vornehmen. +] else { todo } + +#if lang == "en" [ + In `examples/hello.rs`, make sure the `debug::exit()` call is commented + out or removed. It is used only for running in QEMU. +] else if lang == "de" [ + Stellen Sie in `examples/hello.rs`sicher, daß `debug::exit()` auskommentiert + oder entfernt wurde. Es wird nur zum Ausführen in QEMU verwendet. +] else { todo } + +#raw(block: true, lang: "rust", +"#[entry] +fn main() -> ! { + hprintln!(\"Hello, world!\").unwrap(); + + // " + if lang == "en" { + "exit QEMU + // NOTE do not run this on hardware; it can corrupt OpenOCD state + // debug::exit(debug::EXIT_SUCCESS);" + } else if lang == "de" { + "exit QEMU + // HINWEIS: Fuehren Sie dies nicht auf der Hardware aus; es kann den + // OpenOCD-Zustand beschaedigen. + // debug::exit(debug::EXIT_SUCCESS);" + } else { todos } + " + + loop {} +} +") + +#if lang == "en" [ + You can now cross compile programs using `cargo build` and inspect the + binaries using `cargo-binutils` as you did before. The `cortex-m-rt` + crate handles all the magic required to get your chip running, as + helpfully, pretty much all Cortex-M CPUs boot in the same fashion. +] else if lang == "de" [ + Sie können nun Programme mittels `cargo build` cross-kompilieren und die + Binärdateien mit `cargo-binutils` untersuchen, genau wie zuvor. Das + `cortex-m-rt`-Crate übernimmt die gesamte für den Betrieb des Chips + erforderliche „Magie", da erfreulicherweise so gut wie alle + Cortex-M-CPUs auf die gleiche Weise booten. +] else { todo } + +```console +cargo build --example hello +``` + += #(if lang == "en" [Debugging] + else if lang == "de" [Fehlerbehebung (Debugging)] + else { todo }) + +#let url_dn = "https://github.com/rust-embedded/debugonomicon" +#if lang == "en" [ + Debugging will look a bit different. In fact, the first steps can look + different depending on the target device. In this section we'll show the + steps required to debug a program running on the STM32F3DISCOVERY. This + is meant to serve as a reference; for device specific information about + debugging check out #link(url_dn)[the Debugonomicon]. +] else if lang == "de" [ + Das Debugging gestaltet sich etwas anders. Tatsächlich können sich + bereits die ersten Schritte je nach Zielgerät unterscheiden. In diesem + Abschnitt zeigen wir die Schritte, die zum Debuggen eines auf dem + STM32F3DISCOVERY laufenden Programms erforderlich sind. Dies soll als + Referenz dienen; für gerätespezifische Informationen zum Debugging + konsultieren Sie bitte #link(url_dn)[das Debugonomicon]. +] else { todo } + +#if lang == "en" [ + As before we'll do remote debugging and the client will be a GDB + process. This time, however, the server will be OpenOCD. +] else if lang == "de" [ + Wie zuvor führen wir Remote-Debugging durch, wobei der Client ein + GDB-Prozess ist. Diesmal ist der Server jedoch OpenOCD. +] else { todo } + +#if lang == "en" [ + As done during the #link()[verify] section + connect the discovery board to your laptop / PC and check that the + ST-LINK header is populated. +] else if lang == "de" [ + Verbinden Sie, wie im Abschnitt + "#link()[Die Installation überprüfen]" + beschrieben, das Discovery Board mit Ihrem Laptop/PC und prüfen Sie, ob + der ST-LINK-Header ausgefüllt ist. +] else { todo } + +#if lang == "en" [ + On a terminal run `openocd` to connect to the ST-LINK on the discovery + board. Run this command from the root of the template; `openocd` will + pick up the `openocd.cfg` file which indicates which interface file and + target file to use. +] else if lang == "de" [ + Führen Sie in einem Terminal `openocd` aus, um eine Verbindung zum + ST-LINK auf dem Discovery-Board herzustellen. Führen Sie diesen Befehl + im Stammverzeichnis der Vorlage aus; `openocd` greift dabei auf die + Datei `openocd.cfg` zu, in der festgelegt ist, welche Schnittstellen- + und Zieldateien verwendet werden sollen. +] else { todo } + +```console +cat openocd.cfg +``` + +#if lang == "en" [ + ```text + # Sample OpenOCD configuration for the STM32F3DISCOVERY development board + + # Depending on the hardware revision you got you'll have to pick ONE of these + # interfaces. At any time only one interface should be commented out. + + # Revision C (newer revision) + source [find interface/stlink.cfg] + + # Revision A and B (older revisions) + # source [find interface/stlink-v2.cfg] + + source [find target/stm32f3x.cfg] + ``` +] else if lang == "de" [ + ```text + # Beispiel-OpenOCD-Konfiguration für das STM32F3DISCOVERY-Entwicklungsboard + + # Je nach der vorliegenden Hardware-Revision muessen Sie eine dieser + # Schnittstellen auswaehlen. Es sollte jeweils nur eine Schnittstelle + # auskommentiert sein. + + # Revision C (neuere Revision) + source [find interface/stlink.cfg] + + # Revision A and B (aeltere Revisionen) + # source [find interface/stlink-v2.cfg] + + source [find target/stm32f3x.cfg] + ``` +] else { todo } + +#quote(block: true)[ +#if lang == "en" [ + *NOTE*: If you found out that you have an older revision of the + discovery board during the #link()[verify] + section then you should modify the `openocd.cfg` file at this point to + use `interface/stlink-v2.cfg`. +] else if lang == "de" [ + *HINWEIS* Falls Sie im Abschnitt + "#link()[Die Installation überprüfen]" + feststellen, dass Sie eine ältere Revision des Discovery-Boards + besitzen, sollten Sie an dieser Stelle die Datei `openocd.cfg` so + anpassen, dass `interface/stlink-v2.cfg` verwendet wird. +] else { todo } +] + +```text +$ openocd +Open On-Chip Debugger 0.10.0 +Licensed under GNU GPL v2 +For bug reports, read + http://openocd.org/doc/doxygen/bugs.html +Info : auto-selecting first available session transport "hla_swd". To override use 'transport select '. +adapter speed: 1000 kHz +adapter_nsrst_delay: 100 +Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD +none separate +Info : Unable to match requested speed 1000 kHz, using 950 kHz +Info : Unable to match requested speed 1000 kHz, using 950 kHz +Info : clock speed 950 kHz +Info : STLINK v2 JTAG v27 API v2 SWIM v15 VID 0x0483 PID 0x374B +Info : using stlink api v2 +Info : Target voltage: 2.913879 +Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints +``` + +#if lang == "en" [ + On another terminal run GDB, also from the root of the template. +] else if lang == "de" [ + Starten Sie in einem weiteren Terminal GDB, ebenfalls vom + Stammverzeichnis des Templates aus. +] else { todo } + +```text +gdb-multiarch -q target/thumbv7em-none-eabihf/debug/examples/hello +``` + +#if lang == "en" [ + *NOTE*: like before you might need another version of gdb instead + of `gdb-multiarch` depending on which one you installed in the + installation chapter. This could also be `arm-none-eabi-gdb` or just `gdb`. +] else if lang == "de" [ + *HINWEIS*: Wie bereits erwähnt, benötigen Sie möglicherweise + anstelle von `gdb-multiarch` eine andere Version von gdb, je nachdem, + welche Version Sie im Installationskapitel installiert haben. Dies + könnte beispielsweise `arm-none-eabi-gdb` oder einfach `gdb` sein. +] else { todo } + +#if lang == "en" [ + Next connect GDB to OpenOCD, which is waiting for a TCP connection on port 3333. +] else if lang == "de" [ + Verbinden Sie nun GDB mit OpenOCD, das auf eine TCP-Verbindung an Port 3333 wartet. +] else { todo } + +```console +(gdb) target remote :3333 +Remote debugging using :3333 +0x00000000 in ?? () +``` + +#if lang == "en" [ + Now proceed to _flash_ (load) the program onto the microcontroller + using the `load` command. +] else if lang == "de" [ + Fahren Sie nun damit fort, das Programm mithilfe des Befehls `load` auf + den Mikrocontroller zu _flashen_ (zu laden). +] else { todo } + +```console +(gdb) load +Loading section .vector_table, size 0x400 lma 0x8000000 +Loading section .text, size 0x1518 lma 0x8000400 +Loading section .rodata, size 0x414 lma 0x8001918 +Start address 0x08000400, load size 7468 +Transfer rate: 13 KB/sec, 2489 bytes/write. +``` + +#if lang == "en" [ + The program is now loaded. This program uses semihosting so before we do + any semihosting call we have to tell OpenOCD to enable semihosting. You + can send commands to OpenOCD using the `monitor` command. +] else if lang == "de" [ + Das Programm ist nun geladen. Da dieses Programm Semihosting verwendet, + müssen wir OpenOCD anweisen, Semihosting zu aktivieren, bevor wir einen + entsprechenden Aufruf tätigen. Befehle können mithilfe des Befehls + `monitor` an OpenOCD gesendet werden. +] else { todo } + +```console +(gdb) monitor arm semihosting enable +semihosting is enabled +``` + +#quote(block: true)[ +#if lang == "en" [ + You can see all the OpenOCD commands by invoking the `monitor help` command. +] else if lang == "de" [ + Sie können sich alle OpenOCD-Befehle anzeigen lassen, indem Sie den + Befehl `monitor help` aufrufen. +] else { todo } +] + +#if lang == "en" [ + Like before we can skip all the way to `main` using a breakpoint and the + `continue` command. +] else if lang == "de" [ + Wie zuvor können wir mithilfe eines Breakpoints und des Befehls + `continue` direkt zu `main` springen. +] else { todo } + +```console +(gdb) break main +Breakpoint 1 at 0x8000490: file examples/hello.rs, line 11. +Note: automatically using hardware breakpoints for read-only addresses. + +(gdb) continue +Continuing. + +Breakpoint 1, hello::__cortex_m_rt_main_trampoline () at examples/hello.rs:11 +11 #[entry] +``` + +#quote(block: true)[ +#if lang == "en" [ + *NOTE* If GDB blocks the terminal instead of hitting the + breakpoint after you issue the `continue` command above, you might want + to double check that the memory region information in the `memory.x` + file is correctly set up for your device (both the starts _and_ lengths). +] else if lang == "de" [ + *HINWEIS* Falls GDB das Terminal blockiert, anstatt am Haltepunkt + (Breakpoint) anzuhalten, nachdem Sie den oben genannten Befehl + `continue` eingegeben haben, sollten Sie überprüfen, ob die Angaben zum + Speicherbereich in der Datei `memory.x` für Ihr Gerät korrekt + konfiguriert sind (sowohl die Startadressen _als auch_ die Längen). +] else { todo } +] + +#if lang == "en" [ + Step into the main function with `step`. +] else if lang == "de" [ + Springen Sie mit `step` in die main-Funktion. +] else { todo } + +```console +(gdb) step +halted: PC: 0x08000496 +hello::__cortex_m_rt_main () at examples/hello.rs:13 +13 hprintln!("Hello, world!").unwrap(); +``` + +#if lang == "en" [ + After advancing the program with `next` you should see "Hello, world!" + printed on the OpenOCD console, among other stuff. +] else if lang == "de" [ + Nachdem Sie das Programm mit `next` weitergeführt haben, sollten Sie + unter anderem „Hello, world!" auf der OpenOCD-Konsole ausgegeben sehen. +] else { todo } + +```console +$ openocd +(..) +Info : halted: PC: 0x08000502 +Hello, world! +Info : halted: PC: 0x080004ac +Info : halted: PC: 0x080004ae +Info : halted: PC: 0x080004b0 +Info : halted: PC: 0x080004b4 +Info : halted: PC: 0x080004b8 +Info : halted: PC: 0x080004bc +``` + +#if lang == "en" [ + The message is only displayed once as the program is about to enter the + infinite loop defined in line 19: `loop {}` +] else if lang == "de" [ + Die Meldung wird nur einmal angezeigt, kurz bevor das Programm in die in + Zeile 19 definierte Endlosschleife eintritt: `loop {}` +] else { todo } + +#if lang == "en" [ + You can now exit GDB using the `quit` command. +] else if lang == "de" [ + Sie können GDB nun mit dem Befehl `quit` beenden. +] else { todo } + +```console +(gdb) quit +A debugging session is active. + + Inferior 1 [Remote target] will be detached. + +Quit anyway? (y or n) +``` + +#if lang == "en" [ + Debugging now requires a few more steps so we have packed all those + steps into a single GDB script named `openocd.gdb`. The file was created + during the `cargo generate` step, and should work without any + modifications. Let's have a peek: +] else if lang == "de" [ + Das Debugging erfordert nun einige zusätzliche Schritte; daher haben wir + all diese Schritte in einem einzigen GDB-Skript namens `openocd.gdb` + zusammengefasst. Die Datei wurde während des Schritts `cargo generate` + erstellt und sollte ohne Änderungen funktionieren. Werfen wir einen Blick darauf: +] else { todo } + +```console +cat openocd.gdb +``` + +```text +target extended-remote :3333 + +# print demangled symbols +set print asm-demangle on + +# detect unhandled exceptions, hard faults and panics +break DefaultHandler +break HardFault +break rust_begin_unwind + +monitor arm semihosting enable + +load + +# start the process but immediately halt the processor +stepi +``` + +#if lang == "en" [ + Now running ` -x openocd.gdb target/thumbv7em-none-eabihf/debug/examples/hello` + will immediately connect GDB to OpenOCD, enable semihosting, load the + program and start the process. +] else if lang == "de" [ + Wenn Sie nun den Befehl + ` -x openocd.gdb target/thumbv7em-none-eabihf/debug/examples/hello` + ausführen, verbindet sich GDB sofort mit OpenOCD, aktiviert Semihosting, + lädt das Programm und startet den Prozess. +] else { todo } + +#if lang == "en" [ + Alternatively, you can turn ` -x openocd.gdb` into a custom runner + to make `cargo run` build a program _and_ start a GDB session. This + runner is included in `.cargo/config.toml` but it's commented out. +] else if lang == "de" [ + Alternativ können Sie ` -x openocd.gdb` als benutzerdefinierten + Runner einrichten, sodass `cargo run` das Programm baut _und_ eine + GDB-Sitzung startet. Dieser Runner ist bereits in der Datei + `.cargo/config.toml` enthalten, jedoch auskommentiert. +] else { todo } + +```console +head -n10 .cargo/config.toml +``` + +#if lang == "en" [ + ```toml + [target.thumbv7m-none-eabi] + # uncomment this to make `cargo run` execute programs on QEMU + # runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" + + [target.'cfg(all(target_arch = "arm", target_os = "none"))'] + # uncomment ONE of these three option to make `cargo run` start a GDB session + # which option to pick depends on your system + runner = "arm-none-eabi-gdb -x openocd.gdb" + # runner = "gdb-multiarch -x openocd.gdb" + # runner = "gdb -x openocd.gdb" + ``` +] else if lang == "de" [ + ```toml + [target.thumbv7m-none-eabi] + # Dies auskommentieren, damit `cargo run` Programme auf QEMU ausfuehrt. + # runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" + + [target.'cfg(all(target_arch = "arm", target_os = "none"))'] + # Heben Sie die Auskommentierung einer dieser drei Optionen auf, damit + # `cargo run` eine GDB-Sitzung startet. + # Welche Option Sie waehlen sollten, haengt von Ihrem System ab. + runner = "arm-none-eabi-gdb -x openocd.gdb" + # runner = "gdb-multiarch -x openocd.gdb" + # runner = "gdb -x openocd.gdb" + ``` +] else { todo } + +```text +$ cargo run --example hello +(..) +Loading section .vector_table, size 0x400 lma 0x8000000 +Loading section .text, size 0x1e70 lma 0x8000400 +Loading section .rodata, size 0x61c lma 0x8002270 +Start address 0x800144e, load size 10380 +Transfer rate: 17 KB/sec, 3460 bytes/write. +(gdb) +``` diff --git a/typ/start/index.typ b/typ/start/index.typ new file mode 100644 index 00000000..bb577d32 --- /dev/null +++ b/typ/start/index.typ @@ -0,0 +1,27 @@ +#import "../config.typ": * + +#h1(if lang == "en" [Getting Started] + else if lang == "de" [Erste Schritte] + else { todo }) + +#set heading(offset: whole) + +#let ln_f3 = link("http://www.st.com/en/evaluation-tools/stm32f3discovery.html")[STM32F3DISCOVERY] +#if lang == "en" [ + In this section we'll walk you through the process of writing, building, + flashing and debugging embedded programs. You will be able to try most + of the examples without any special hardware as we will show you the + basics using QEMU, a popular open-source hardware emulator. The only + section where hardware is required is, naturally enough, the + #link()[Hardware] section, where we use OpenOCD to + program an #ln_f3. +] else if lang == "de" [ + In diesem Abschnitt führen wir Sie Schritt für Schritt durch den Prozess + des Schreibens, Kompilierens, Flashen und Debuggens von + Embedded-Programmen. Sie können die meisten der Beispiele ohne spezielle + Hardware ausprobieren, da wir Ihnen die Grundlagen mithilfe von QEMU, + einem beliebten Open-Source-Hardware-Emulator, vermitteln. Der einzige + Abschnitt, in dem Hardware erforderlich ist, ist natürlich der Abschnitt + #link()[Hardware], wo wir OpenOCD verwenden, um ein + #ln_f3 zu programmieren. +] else { todo } \ No newline at end of file diff --git a/typ/start/interrupts.typ b/typ/start/interrupts.typ new file mode 100644 index 00000000..a5c2dcff --- /dev/null +++ b/typ/start/interrupts.typ @@ -0,0 +1,166 @@ +#import "../config.typ": * + +#h1(offset: whole, + if lang in ("en", "de") [Interrupts] + else { todo }) +#set heading(offset: whole*2) + +#if lang == "en" [ + Interrupts differ from exceptions in a variety of ways but their + operation and use is largely similar and they are also handled by the + same interrupt controller. Whereas exceptions are defined by the + Cortex-M architecture, interrupts are always vendor (and often even + chip) specific implementations, both in naming and functionality. +] else if lang == "de" [ + Interrupts unterscheiden sich in vielerlei Hinsicht von Exceptions; ihre + Funktionsweise und Verwendung sind jedoch weitgehend ähnlich, und sie + werden zudem von demselben Interrupt-Controller verwaltet. Während + Exceptions durch die Cortex-M-Architektur definiert sind, handelt es + sich bei Interrupts -- sowohl hinsichtlich der Benennung als auch der + Funktionalität -- stets um herstellerspezifische (und oft sogar + chip-spezifische) Implementierungen. +] else { todo } + +#if lang == "en" [ + Interrupts do allow for a lot of flexibility which needs to be accounted + for when attempting to use them in an advanced way. We will not cover + those uses in this book, however it is a good idea to keep the following + in mind: + - Interrupts have programmable priorities which determine their + handlers' execution order + - Interrupts can nest and preempt, i.e.~execution of an interrupt + handler might be interrupted by another higher-priority interrupt + - In general the reason causing the interrupt to trigger needs to be + cleared to prevent re-entering the interrupt handler endlessly +] else if lang == "de" [ + Interrupts bieten ein hohes Maß an Flexibilität, das bei einer + fortgeschrittenen Nutzung berücksichtigt werden muss. Wir werden diese + Anwendungsfälle in diesem Buch zwar nicht behandeln, dennoch ist es + ratsam, Folgendes zu beachten: + - Interrupts verfügen über programmierbare Prioritäten, die die + Ausführungsreihenfolge ihrer Behandlungsroutinen bestimmen. + - Interrupts können geschachtelt werden und einander unterbrechen + (Preemption); das heißt, die Ausführung eines Interrupt-Handlers kann + durch einen anderen Interrupt mit höherer Priorität unterbrochen werden. + - Im Allgemeinen muss die Ursache für die Auslösung des Interrupts + beseitigt werden, um zu verhindern, dass der Interrupt-Handler endlos + erneut aufgerufen wird. +] else { todo } + +#if lang == "en" [ + The general initialization steps at runtime are always the same: + - Setup the peripheral(s) to generate interrupts requests at the desired occasions + - Set the desired priority of the interrupt handler in the interrupt controller + - Enable the interrupt handler in the interrupt controller +] else if lang == "de" [ + Die allgemeinen Initialisierungsschritte zur Laufzeit sind immer gleich: + - Konfigurieren Sie die Peripheriekomponente(n) so, dass + Interrupt-Anforderungen zu den gewünschten Zeitpunkten ausgelöst werden. + - Legen Sie die gewünschte Priorität des Interrupt-Handlers im + Interrupt-Controller fest. + - Aktivieren Sie den Interrupt-Handler im Interrupt-Controller. +] else { todo } + +#let ln_int = link("https://docs.rs/cortex-m-rt-macros/0.1.5/cortex_m_rt_macros/attr.interrupt.html")[`interrupt`] +#if lang == "en" [ + Similarly to exceptions, the cortex-m-rt crate exposes an #ln_int + attribute for declaring interrupt handlers. However, this attribute is + only available when the device feature is enabled. That said, this + attribute is not intended to be used directly---doing so will result in + a compilation error. +] else if lang == "de" [ + Ähnlich wie bei Exceptions stellt das `cortex-m-rt`-Crate ein + #ln_int;-Attribut zur Deklaration von Interrupt-Handlern bereit. + Dieses Attribut steht jedoch nur zur Verfügung, + wenn das Device-Feature aktiviert ist. Es ist + allerdings nicht für die direkte Verwendung vorgesehen; eine solche + würde zu einem Kompilierfehler führen. +] else { todo } + +#if lang == "en" [ + Instead, you should use the re-exported version of the interrupt + attribute provided by the device crate (usually generated using + svd2rust). This ensures that the compiler can verify that the interrupt + actually exists on the target device. The list of available + interrupts---and their position in the interrupt vector table---is + typically auto-generated from an SVD file by svd2rust. +] else if lang == "de" [ + Stattdessen sollten Sie die vom Device-Crate (das üblicherweise mit + `svd2rust` generiert wird) bereitgestellte, re-exportierte Version des + `interrupt`-Attributs verwenden. Dies stellt sicher, dass der Compiler + überprüfen kann, ob der Interrupt auf dem Zielgerät tatsächlich + existiert. Die Liste der verfügbaren Interrupts -- sowie deren Position + in der Interrupt-Vektortabelle -- wird typischerweise von `svd2rust` + automatisch aus einer SVD-Datei generiert. +] else { todo } + +#if lang == "en" [ + ```rust + use lm3s6965::interrupt; // Re-exported attribute from the device crate + + // Interrupt handler for the Timer2 interrupt + #[interrupt] + fn TIMER2A() { + // .. + // Clear reason for the generated interrupt request + } + ``` +] else if lang == "de" [ + ```rust + use lm3s6965::interrupt; // Aus dem Device-Crate zurueck-exportiertes Attribut + + // Interrupt-Handler fuer den Timer2-Interrupt + #[interrupt] + fn TIMER2A() { + // .. + // Eindeutiger Grund fuer die generierte Interrupt-Anforderung + } + ``` +] else { todo } + +#if lang == "en" [ + Interrupt handlers look like plain functions (except for the lack of + arguments) similar to exception handlers. However they can not be called + directly by other parts of the firmware due to the special calling + conventions. It is however possible to generate interrupt requests in + software to trigger a diversion to the interrupt handler. +] else if lang == "de" [ + Interrupt-Handler ähneln -- abgesehen vom Fehlen von Argumenten -- + gewöhnlichen Funktionen, ganz wie Exception-Handler. Aufgrund spezieller + Aufrufkonventionen können sie jedoch nicht direkt von anderen Teilen der + Firmware aufgerufen werden. Es ist allerdings möglich, + Interrupt-Anforderungen per Software zu erzeugen, um einen Sprung zum + Interrupt-Handler auszulösen. +] else { todo } + +#if lang == "en" [ + Similar to exception handlers it is also possible to declare + `static mut` variables inside the interrupt handlers for _safe_ state keeping. +] else if lang == "de" [ + Ähnlich wie bei Exception-Handlern ist es auch hier möglich, + `static mut`-Variablen innerhalb der Interrupt-Handler zu deklarieren, + um den Zustand auf _sichere_ Weise zu speichern. +] else { todo } + +#raw(block: true, lang: "rust", +"#[interrupt] +fn TIMER2A() { + static mut COUNT: u32 = 0; + + // " + if lang == "en" { + "`COUNT` has type `&mut u32` and it's safe to use" + } else if lang == "de" { + "`COUNT` hat den Typ `&mut u32` und ist sicher zu verwenden." + } else { todos } + " + *COUNT += 1; +} +") + +#if lang == "en" [ + For a more detailed description about the mechanisms demonstrated here + please refer to the #link()[exceptions section]. +] else if lang == "de" [ + Für eine detailliertere Beschreibung der hier veranschaulichten + Mechanismen konsultieren Sie bitte den + #link()[Abschnitt zu Ausnahmen]. +] else { todo } diff --git a/typ/start/panicking.typ b/typ/start/panicking.typ new file mode 100644 index 00000000..af11279e --- /dev/null +++ b/typ/start/panicking.typ @@ -0,0 +1,219 @@ +#import "../config.typ": * + +#h1(offset: whole, + if lang == "en" [Panicking] + else if lang == "de" [In Panik geraten] + else { todo }) + +#set heading(offset: whole*2) + +#if lang == "en" [ + Panicking is a core part of the Rust language. Built-in operations like + indexing are runtime checked for memory safety. When out of bounds + indexing is attempted this results in a panic. +] else if lang == "de" [ + Das Auslösen einer Panik ist ein wesentlicher Bestandteil der Sprache + Rust. Eingebaute Operationen wie der Zugriff per Index werden zur + Laufzeit auf Speichersicherheit überprüft. Erfolgt ein Zugriff außerhalb + der zulässigen Grenzen, führt dies zu einer Panik. +] else { todo } + +#if lang == "en" [ + In the standard library panicking has a defined behavior: it unwinds the + stack of the panicking thread, unless the user opted for aborting the + program on panics. +] else if lang == "de" [ + In der Standardbibliothek ist das Verhalten bei einer Panik definiert: + Der Stack des betroffenen Threads wird abgewickelt (Stack Unwinding), es + sei denn, der Benutzer hat sich für einen Programmabbruch im Falle einer + Panik entschieden. +] else { todo } + +#let ln_info = link("https://doc.rust-lang.org/core/panic/struct.PanicInfo.html")[`PanicInfo`] +#if lang == "en" [ + In programs without standard library, however, the panicking behavior is + left undefined. A behavior can be chosen by declaring a + `#[panic_handler]` function. This function must appear exactly + _once_ in the dependency graph of a program, and must have the + following signature: `fn(&PanicInfo) -> !`, where #ln_info + is a struct containing information about the location of the panic. +] else if lang == "de" [ + In Programmen ohne Standardbibliothek ist das Verhalten bei einer Panik + hingegen nicht definiert. Ein Verhalten lässt sich durch die Deklaration + einer `#[panic_handler]`-Funktion festlegen. Diese Funktion muss im + Abhängigkeitsgraphen des Programms genau _einmal_ vorkommen und + folgende Signatur aufweisen: `fn(&PanicInfo) -> !`, wobei #ln_info + eine Struktur ist, die Informationen über den Ort des Panic enthält. +] else { todo } + +#let ln_abort = link("https://crates.io/crates/panic-abort")[`panic-abort`] +#let ln_halt = link("https://crates.io/crates/panic-halt")[`panic-halt`] +#let ln_itm = link("https://crates.io/crates/panic-itm")[`panic-itm`] +#let ln_sh = link("https://crates.io/crates/panic-semihosting")[`panic-semihosting`] +#if lang == "en" [ + Given that embedded systems range from user facing to safety critical + (cannot crash) there's no one size fits all panicking behavior but there + are plenty of commonly used behaviors. These common behaviors have been + packaged into crates that define the `#[panic_handler]` function. Some + examples include: + - #ln_abort. A panic causes the abort instruction to be executed. + - #ln_halt. A panic causes the program, or the current thread, to halt by entering an infinite loop. + - #ln_itm. The panicking message is logged using the ITM, an ARM Cortex-M specific peripheral. + - #ln_sh. The panicking message is logged to the host using the semihosting technique. +] else if lang == "de" [ + Da das Spektrum eingebetteter Systeme von anwenderorientierten bis hin + zu sicherheitskritischen Anwendungen (bei denen ein Absturz + ausgeschlossen sein muss) reicht, gibt es kein universelles Verhalten im + Fehlerfall („Panic"); es existieren jedoch zahlreiche gängige Ansätze. + Diese verbreiteten Verhaltensweisen wurden in Crates zusammengefasst, + die die Funktion `#[panic_handler]` definieren. Zu den Beispielen + gehören: + - #ln_abort. Eine Panic bewirkt die Ausführung der Abbruchanweisung. + - #ln_halt. Ein Panic bewirkt, dass das Programm oder der aktuelle + Thread anhält, indem es bzw. er in eine Endlosschleife eintritt. + - #ln_itm. Die Panik-Meldung wird mithilfe des ITM protokolliert -- einer für ARM + Cortex-M spezifischen Peripheriekomponente. + - #ln_sh. Die Panik-Meldung wird mithilfe der Semihosting-Technik + auf dem Host protokolliert. +] else { todo } + +#let ln_handler = link("https://crates.io/keywords/panic-handler")[`panic-handler`] +#if lang == "en" [ + You may be able to find even more crates searching for the + #ln_handler keyword on crates.io. +] else if lang == "de" [ + Möglicherweise findest du noch mehr Crates, wenn du auf crates.io nach + dem Schlüsselwort #ln_handler suchst. +] else { todo } + +#if lang == "en" [ + A program can pick one of these behaviors simply by linking to the + corresponding crate. The fact that the panicking behavior is expressed + in the source of an application as a single line of code is not only + useful as documentation but can also be used to change the panicking + behavior according to the compilation profile. For example: +] else if lang == "de" [ + Ein Programm kann eines dieser Verhalten einfach dadurch auswählen, dass + es das entsprechende Crate einbindet. Dass das Verhalten im Fehlerfall + (Panic-Verhalten) im Quellcode einer Anwendung als einzelne Codezeile + ausgedrückt wird, ist nicht nur als Dokumentation nützlich, sondern + ermöglicht es auch, dieses Verhalten je nach Kompilierungsprofil + anzupassen. Zum Beispiel: +] else { todo } + +#raw(block: true, lang: "rust", +"#![no_main] +#![no_std] + +// " + if lang == "en" { + "dev profile: easier to debug panics; can put a breakpoint on `rust_begin_unwind`" + } else if lang == "de" { + "dev-Profil: einfacheres Debuggen von Panics; man kann einen Breakpoint bei +// `rust_begin_unwind` setzen." + } else { todos } + " +#[cfg(debug_assertions)] +use panic_halt as _; + +// " + if lang == "en" { + "release profile: minimize the binary size of the application" + } else if lang == "de" { + "Release-Profil: Minimierung der Binaergroesse der Anwendung" + } else { todos } + " +#[cfg(not(debug_assertions))] +use panic_abort as _; + +// .. +") + +#if lang == "en" [ + In this example the crate links to the `panic-halt` crate when built + with the dev profile (`cargo build`), but links to the `panic-abort` + crate when built with the release profile (`cargo build --release`). +] else if lang == "de" [ +In diesem Beispiel verlinkt der Crate beim Bauen mit dem Dev-Profil +(`cargo build`) auf den `panic-halt`-Crate, beim Bauen mit dem +Release-Profil (`cargo build --release`) hingegen auf den +`panic-abort`-Crate. +] else { todo } + +#quote(block: true)[ +#if lang == "en" [ + The `use panic_abort as _;` form of the `use` statement is used to + ensure the `panic_abort` panic handler is included in our final + executable while making it clear to the compiler that we won't + explicitly use anything from the crate. Without the `as _` rename, the + compiler would warn that we have an unused import. Sometimes you might + see `extern crate panic_abort` instead, which is an older style used + before the 2018 edition of Rust, and should now only be used for + "sysroot" crates (those distributed with Rust itself) such as + `proc_macro`, `alloc`, `std`, and `test`. +] else if lang == "de" [ + Die `use panic_abort as _;`-Variante der `use`-Anweisung wird verwendet, + um sicherzustellen, dass der `panic_abort`-Panic-Handler in die fertige + ausführbare Datei aufgenommen wird, während dem Compiler gleichzeitig + signalisiert wird, dass wir nichts aus diesem Crate explizit verwenden + werden. Ohne die Umbenennung mittels `as _` würde der Compiler eine + Warnung wegen eines ungenutzten Imports ausgeben. Gelegentlich stößt man + stattdessen auf `extern crate panic_abort`\; dabei handelt es sich um + einen älteren Stil, der vor der Rust-Edition 2018 üblich war und heute + nur noch für sogenannte „sysroot"-Crates (die gemeinsam mit Rust selbst + ausgeliefert werden) -- wie etwa `proc_macro`, `alloc`, `std` und `test` + -- verwendet werden sollte. +] else { todo } +] + += #(if lang == "en" [An example] + else if lang == "de" [Ein Beispiel] + else { todo }) + +#if lang == "en" [ + Here's an example that tries to index an array beyond its length. The + operation results in a panic. +] else if lang == "de" [ + Hier ist ein Beispiel, das versucht, auf ein Array an einer Position + zuzugreifen, die über dessen Länge hinausgeht. Der Vorgang führt zu einer Panic. +] else { todo } + +#raw(block: true, lang: "rust", +"#![no_main] +#![no_std] + +use panic_semihosting as _; + +use cortex_m_rt::entry; + +#[entry] +fn main() -> ! { + let xs = [0, 1, 2]; + let i = xs.len(); + let _y = xs[i]; // " + if lang == "en" { + "out of bounds access" + } else if lang == "de" { + "Zugriff ausserhalb der zulaessigen Grenzen" + } else { todos } + " + + loop {} +} +") + +#if lang == "en" [ + This example chose the `panic-semihosting` behavior which prints the + panic message to the host console using semihosting. +] else if lang == "de" [ +Für dieses Beispiel wurde das Verhalten `panic-semihosting` gewählt, das +die Panic-Meldung mittels Semihosting auf der Host-Konsole ausgibt. +] else { todo } + +```text +$ cargo run + Running `qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb (..) +panicked at 'index out of bounds: the len is 3 but the index is 4', src/main.rs:12:13 +``` + +#if lang == "en" [ + You can try changing the behavior to `panic-halt` and confirm that no + message is printed in that case. +] else if lang == "de" [ +Sie können versuchen, das Verhalten auf `panic-halt` zu ändern, und +bestätigen, dass in diesem Fall keine Meldung ausgegeben wird. +] else { todo } diff --git a/typ/start/qemu.typ b/typ/start/qemu.typ new file mode 100644 index 00000000..024635d2 --- /dev/null +++ b/typ/start/qemu.typ @@ -0,0 +1,1159 @@ +#import "../config.typ": * + +#h1(offset: whole, + [QEMU]) + +#set heading(offset: whole*2) + +// TODO: check +#if lang == "de" [ + QEMU (von englisch „Quick Emulator") ist eine freie + Virtualisierungssoftware, die die gesamte Hardware eines Computers + emuliert und durch die dynamische Übersetzung der Prozessorinstruktionen + des Gastprozessors (englisch guest) in Instruktionen für den + Wirtprozessor (englisch host) eine sehr gute Ausführungsgeschwindigkeit + erreicht. + + URL: #link("https://www.qemu.org/") + + QEMU emuliert Systeme mit den folgenden Prozessorarchitekturen: + - 68K, + - Alpha, + - ARM (32- und 64-Bit), + - CHRIS, + - HPPA, + - LatticeMico32, + - m68K bzw. Coldfire, + - MicroBlaze, + - MIPS, + - Moxie, + - Nios2, + - OpenRISC, + - PC + - Power + - PowerNV + - PowerPC (32- und 64-Bit), + - RISC-V, + - S/390, + - SH-4, + - Sparc32/64, + - TILE-Gx, + - TriCore, + - Unicore, + - x86 (x86-32 und x86-64), + - Xtensa + + (Stand 2026). +] + +#let lm3s6965 = link("http://www.ti.com/product/LM3S6965")[LM3S6965] +#let url_qemu = "https://wiki.qemu.org/Documentation/Platforms/ARM#Supported_in_qemu-system-arm" +#if lang == "en" [ + We'll start writing a program for the #lm3s6965, a Cortex-M3 + microcontroller. We have chosen this as our initial target because it + #link(url_qemu)[can be emulated] + using QEMU so you don't need to fiddle with hardware in this section and + we can focus on the tooling and the development process. +] else if lang == "de" [ + Wir beginnen mit der Entwicklung eines Programms für den + #lm3s6965, einen ARM + Cortex-M3-Mikrocontroller. Wir haben diesen als unser erstes Ziel + ausgewählt, da er mit QEMU + #link(url_qemu)[emuliert werden kann], + sodass Sie sich in diesem Abschnitt nicht mit der Hardware beschäftigen + müssen und wir uns auf die Werkzeuge und den Entwicklungsprozess + konzentrieren können. +] else { todo } + +#if lang == "en" [ + *IMPORTANT* We'll use the name "app" for the project name in this + tutorial. Whenever you see the word "app" you should replace it with the + name you selected for your project. Or, you could also name your project + "app" and avoid the substitutions. +] else if lang == "de" [ + *WICHTIG* In dieser Anleitung verwenden wir den Namen „app" als + Projektnamen. Wann immer Sie das Wort „app" sehen, sollten Sie es durch + den Namen ersetzen, den Sie für Ihr Projekt gewählt haben. Alternativ + können Sie Ihr Projekt auch „app" nennen und so die Ersetzungen + vermeiden. +] else { todo } + += #(if lang == "en" [Creating a non standard Rust program] + else if lang == "de" [Erstellen eines nicht standardmäßigen Rust-Programms] + else { todo }) + +#let ln_quick = link("https://github.com/rust-embedded/cortex-m-quickstart")[`cortex-m-quickstart`] +#if lang == "en" [ + We'll use the #ln_quick + project template to generate a new project from it. The created project + will contain a barebone application: a good starting point for a new + embedded rust application. In addition, the project will contain an + `examples` directory, with several separate applications, highlighting + some of the key embedded rust functionality. +] else if lang == "de" [ + Wir werden die Projektvorlage #ln_quick + verwenden, um daraus ein neues Projekt zu erstellen. Das erstellte + Projekt enthält eine Minimalanwendung: einen guten Ausgangspunkt für + eine neue Embedded-Rust-Anwendung. Darüber hinaus enthält das Projekt + ein Verzeichnis `examples` mit mehreren separaten Anwendungen, die + einige der wichtigsten Funktionen von Embedded Rust veranschaulichen. +] else { todo } + +== #(if lang == "en" [Using `cargo-generate`] + else if lang == "de" [Verwendung von `cargo-generate`] + else { todo }) + +#if lang == "en" [ + First install cargo-generate +] else if lang == "de" [ + Installieren Sie zunächst cargo-generate +] else { todo } + +```console +cargo install cargo-generate +``` + +#if lang == "en" [ + Then generate a new project +] else if lang == "de" [ + Erstellen Sie anschließend ein neues Projekt +] else { todo } + +```console +cargo generate --git https://github.com/knurling-rs/app-template +``` + +```text + Project Name: app + Creating project called `app`... + Done! New project created /tmp/app +``` + +```console +cd app +``` + +== #(if lang == "en" [Using `git`] + else if lang == "de" [Verwendung von `git`] + else { todo }) + +#if lang == "en" [ + Clone the repository +] else if lang == "de" [ + Das Repository klonen +] else { todo } + +```console +git clone https://github.com/rust-embedded/cortex-m-quickstart app +cd app +``` + +#if lang == "en" [ + And then fill in the placeholders in the `Cargo.toml` file +] else if lang == "de" [ + Und fülle dann die Platzhalter in der Datei `Cargo.toml` aus +] else { todo } + +```toml +[package] +authors = ["{{authors}}"] # "{{authors}}" -> "John Smith" +edition = "2018" +name = "{{project-name}}" # "{{project-name}}" -> "app" +version = "0.1.0" + +# .. + +[[bin]] +name = "{{project-name}}" # "{{project-name}}" -> "app" +test = false +bench = false +``` + +== #(if lang == "en" [Using neither] + else if lang == "de" [Keines von beiden verwenden] + else { todo }) + +#if lang == "en" [ + Grab the latest snapshot of the `cortex-m-quickstart` template and + extract it. +] else if lang == "de" [ + Laden Sie den neuesten Snapshot der Vorlage `cortex-m-quickstart` + herunter und entpacken Sie ihn. +] else { todo } + +```console +curl -LO https://github.com/rust-embedded/cortex-m-quickstart/archive/master.zip +unzip master.zip +mv cortex-m-quickstart-master app +cd app +``` + +#if lang == "en" [ + Or you can browse to #ln_quick, click the green "Clone or download" button and then click "Download ZIP". +] else if lang == "de" [ + Oder Sie navigieren zu #ln_quick, + klicken auf die grüne Schaltfläche „Clone or download" und anschließend + auf „Download ZIP". +] else { todo } + +#if lang == "en" [ + Then fill in the placeholders in the `Cargo.toml` file as done in the + second part of the "Using `git`" version. +] else if lang == "de" [ + Füllen Sie anschließend die Platzhalter in der Datei `Cargo.toml` aus, + wie im zweiten Teil der Version „Verwendung von `git` beschrieben. +] else { todo } + += #(if lang == "en" [Program Overview] + else if lang == "de" [Programmübersicht] + else { todo }) + +#if lang == "en" [ + For convenience here are the most important parts of the source code in `src/main.rs`: +] else if lang == "de" [ + Der Einfachheit halber sind hier die wichtigsten Teile des Quellcodes in + `src/main.rs` aufgeführt: +] else { todo } + +#raw(block: true, lang: "rust", +"#![no_std] +#![no_main] + +use panic_halt as _; + +use cortex_m_rt::entry; + +#[entry] +fn main() -> ! { + loop { + // " + if lang == "en" { + "your code goes here" + } else if lang == "de" { + "Hier kommt Ihr Code hin." + } else { todos } + " + } +} +") + +#if lang == "en" [ + This program is a bit different from a standard Rust program so let's + take a closer look. +] else if lang == "de" [ + Dieses Programm unterscheidet sich ein wenig von einem typischen + Rust-Programm, schauen wir es uns also einmal genauer an. +] else { todo } + +#if lang == "en" [ + `#![no_std]` indicates that this program will _not_ link to the + standard crate, `std`. Instead it will link to its subset: the `core` crate. +] else if lang == "de" [ + `#![no_std]` gibt an, dass dieses Programm _nicht_ mit der + Standard-Crate `std` verknüpft wird. Stattdessen wird es mit deren + Teilmenge verknüpft: der Crate `core`. +] else { todo } + +#if lang == "en" [ + `#![no_main]` indicates that this program won't use the standard `main` + interface that most Rust programs use. The main (no pun intended) reason + to go with `no_main` is that using the `main` interface in `no_std` + context requires nightly. +] else if lang == "de" [ + `#![no_main]` gibt an, dass dieses Programm nicht die Standard-`main`- + Schnittstelle verwendet, die die meisten Rust-Programme nutzen. Der + Hauptgrund (kein Wortspiel beabsichtigt) für die Verwendung von + `no_main` ist, dass die Nutzung der `main`-Schnittstelle im + `no_std`-Kontext „Nightly" erfordert. +] else { todo } + +#if lang == "en" [ + `use panic_halt as _;`. This crate provides a `panic_handler` that + defines the panicking behavior of the program. We will cover this in + more detail in the #link()[Panicking] chapter of the book. +] else if lang == "de" [ + `use panic_halt as _;`. Diese Crate stellt einen `panic_handler` bereit, + der das Verhalten des Programms im Panikfall definiert. Wir werden + darauf im Kapitel #link()[In Panik geraten] des Buches + näher eingehen. +] else { todo } + +#let ln_entry = link("https://docs.rs/cortex-m-rt-macros/latest/cortex_m_rt_macros/attr.entry.html")[`#[entry]`] +#let ln_rt = link("https://crates.io/crates/cortex-m-rt")[`cortex-m-rt`] +#if lang == "en" [ + #ln_entry is an attribute provided by the #ln_rt crate + that's used to mark the entry point of the program. As we are not using + the standard `main` interface we need another way to indicate the entry + point of the program and that'd be `#[entry]`. +] else if lang == "de" [ + #ln_entry ist ein Attribut des + #ln_rt;-Crate, das dazu dient, den Einstiegspunkt des Programms zu kennzeichnen. Da wir + nicht die Standard-Schnittstelle `main` verwenden, benötigen wir eine + andere Möglichkeit, den Einstiegspunkt des Programms anzugeben, und das + wäre `#[entry]`. +] else { todo } + +#let url_div = "https://doc.rust-lang.org/rust-by-example/fn/diverging.html" +#if lang == "en" [ + `fn main() -> !`. Our program will be the _only_ process running on + the target hardware so we don't want it to end! We use a + #link(url_div)[divergent function] + (the `-> !` bit in the function signature) to ensure at compile time + that'll be the case. +] else if lang == "de" [ + `fn main() -> !`. Unser Programm wird der _einzige_ Prozess sein, + der auf der Zielhardware läuft, deshalb soll es nicht beendet werden! + Wir verwenden eine #link(url_div)[divergent function] + (das `-> !` in der Funktionssignatur), um bereits zur Kompilierungszeit + sicherzustellen, dass dies auch der Fall ist. +] else { todo } + += #(if lang == "en" [Cross compiling] + else if lang == "de" [Cross-Kompilierung] + else { todo }) + +#if lang == "en" [ + First of all we will need the memory layout for the target + microcontroller, the LM3S6965 in our case. Otherwise the build will fail + to link the image. Create a file named `memory.x` at the root of the + project and paste the following content: +] else if lang == "de" [ + Zunächst benötigen wir das Speicherlayout für den Ziel-Mikrocontroller, + in unserem Fall den LM3S6965. Andernfalls schlägt die Verknüpfung des + Images beim Build fehl. Erstellen Sie eine Datei mit dem Namen + `memory.x` im Stammverzeichnis des Projekts und fügen Sie den folgenden + Inhalt ein: +] else { todo } + +#raw(block: true, lang: "text", +"MEMORY +{ + /* " + if lang == "en" { + "NOTE 1 K = 1 KiBi = 1024 bytes */ + /* TODO Adjust these memory regions to match your device memory layout */ + /* These values correspond to the LM3S6965, one of the few devices + /* QEMU can emulate */" + } else if lang == "de" { + "Hinweis: 1 K = 1 KiBi = 1024 bytes */ + /* TODO Passen Sie diese Speicherbereiche an das Speicherlayout Ihres Geräts an. */ + /* Diese Werte entsprechen dem LM3S6965, einem der wenigen Bausteine, die + QEMU emulieren kann */" + } else { todos } + " + FLASH : ORIGIN = 0x00000000, LENGTH = 256K + RAM : ORIGIN = 0x20000000, LENGTH = 64K +} + +/* " + if lang == "en" { + "This is where the call stack will be allocated. */ +/* The stack is of the full descending type. */ +/* You may want to use this variable to locate the call stack and static + variables in different memory regions. Below is shown the default value */" + } else if lang == "de" { + "Hier wird der Aufrufstapel zugewiesen. */ +/* Der Stapel ist vom Typ „vollständig absteigend“. */ +/* Möglicherweise möchten Sie diese Variable verwenden, um den Aufrufstapel und + statische Variablen in verschiedenen Speicherbereichen zu lokalisieren. + Nachstehend ist der Standardwert aufgeführt */" + } else { todos } + " +/* _stack_start = ORIGIN(RAM) + LENGTH(RAM); */ + +/* " + if lang == "en" { + "You can use this symbol to customize the location of the .text section */ +/* If omitted the .text section will be placed right after the .vector_table + section */ +/* This is required only on microcontrollers that store some configuration right + after the vector table */" + } else if lang == "de" { + "Mit diesem Symbol können Sie den Speicherort des .text-Abschnitts anpassen. */ +/* Wird dies weggelassen, wird der .text-Abschnitt direkt nach dem .vector_table- + Abschnitt platziert */ +/* Dies ist nur bei Mikrocontrollern erforderlich, die bestimmte + Konfigurationsdaten direkthinter der Vektortabelle speichern */" + } else { todos } + " +/* _stext = ORIGIN(FLASH) + 0x400; */ + +/* " + if lang == "en" { + "Example of putting non-initialized variables into custom RAM locations. */ +/* This assumes you have defined a region RAM2 above, and in the Rust + sources added the attribute `#[link_section = \".ram2bss\"]` to the data + you want to place there. */ +/* Note that the section will not be zero-initialized by the runtime! */" + } else if lang == "de" { + "Beispiel für die Zuweisung nicht initialisierter Variablen zu +benutzerdefinierten RAM-Speicherplätzen. */ +/* Dies setzt voraus, dass Sie oben einen Bereich namens „RAM2“ definiert und +in den Rust-Quelldateien das Attribut `#[link_section = „.ram2bss“]` zu den +Daten hinzugefügt haben, die Sie dort platzieren möchten. */ +/* Beachten Sie, dass der Bereich von der Laufzeitumgebung nicht auf Null +initialisiert wird! */" + } else { todos } + " +/* SECTIONS { + .ram2bss (NOLOAD) : ALIGN(4) { + *(.ram2bss); + . = ALIGN(4); + } > RAM2 + } INSERT AFTER .bss; +*/ +") + +#if lang == "en" [ + The next step is to _cross_ compile the program for the Cortex-M3 + architecture. That's as simple as running `cargo build --target $TRIPLE` + if you know what the compilation target (`$TRIPLE`) should be. Luckily, + the `.cargo/config.toml` in the template has the answer: +] else if lang == "de" [ + Der nächste Schritt besteht darin, das Programm für die + Cortex-M3-Architektur _cross_ zu kompilieren. Das geht ganz einfach + mit dem Befehl `cargo build --target $TRIPLE`, sofern Sie wissen, wie + das Kompilierungsziel (`$TRIPLE`) lauten soll. Glücklicherweise finden + Sie die Antwort in der Datei `.cargo/config.toml` in der Vorlage: +] else { todo } + +```console +tail -n6 .cargo/config.toml +``` + +#if lang == "en" [ + ```toml + [build] + # Pick ONE of these compilation targets + # target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ + target = "thumbv7m-none-eabi" # Cortex-M3 + # target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU) + # target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) + ``` +] else if lang == "de" [ + ```toml + [build] + # Waehlen Sie EINES dieser Kompilierungsziele aus + # target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ + target = "thumbv7m-none-eabi" # Cortex-M3 + # target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU) + # target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) + ``` +] else { todo } + +#if lang == "en" [ + To cross compile for the Cortex-M3 architecture we have to use + `thumbv7m-none-eabi`. That target is not automatically installed when + installing the Rust toolchain, it would now be a good time to add that + target to the toolchain, if you haven't done it yet: +] else if lang == "de" [ + Für die Cross-Kompilierung für die Cortex-M3-Architektur müssen wir + `thumbv7m-none-eabi` verwenden. Dieses Ziel wird bei der Installation + der Rust-Werkzeuge nicht automatisch installiert. Jetzt wäre ein guter + Zeitpunkt, dieses Ziel zu den Werkzeugen hinzuzufügen, falls Sie dies + noch nicht getan haben: +] else { todo } + +```console +rustup target add thumbv7m-none-eabi +``` + +#if lang == "en" [ + Since the `thumbv7m-none-eabi` compilation target has been set as the + default in your `.cargo/config.toml` file, the two commands below do the same: +] else if lang == "de" [ + Da das Kompilierungsziel `thumbv7m-none-eabi` in Ihrer Datei + `.cargo/config.toml` als Standard festgelegt wurde, haben die beiden + folgenden Befehle dieselbe Wirkung: +] else { todo } + +```console +cargo build --target thumbv7m-none-eabi +cargo build +``` + += #(if lang == "en" [Inspecting] + else if lang == "de" [Überprüfen] + else { todo }) + +#if lang == "en" [ + Now we have a non-native ELF binary in + `target/thumbv7m-none-eabi/debug/app`. We can inspect it using + `cargo-binutils`. +] else if lang == "de" [ + Nun haben wir eine nicht-native ELF-Binärdatei in + `target/thumbv7m-none-eabi/debug/app`. Wir können sie mit + `cargo-binutils` untersuchen. +] else { todo } + +#if lang == "en" [ + With `cargo-readobj` we can print the ELF headers to confirm that this + is an ARM binary. +] else if lang == "de" [ + Mit `cargo-readobj` können wir die ELF-Header ausgeben, um zu + überprüfen, ob es sich um eine ARM- Binärdatei handelt. +] else { todo } + +```console +cargo readobj --bin app -- --file-headers +``` + +#if lang == "en" [ + Note that: + - `--bin app` is sugar for inspect the binary at `target/$TRIPLE/debug/app` + - `--bin app` will also (re)compile the binary, if necessary +] else if lang == "de" [ + Bitte beachten Sie: + - `--bin app` ist eine vereinfachte Schreibweise + für die Überprüfung der Binärdatei unter `target/$TRIPLE/debug/app` + - `--bin app` kompiliert die Binärdatei bei Bedarf auch (neu) +] else { todo } + +```text +ELF Header: + Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 + Class: ELF32 + Data: 2's complement, little endian + Version: 1 (current) + OS/ABI: UNIX - System V + ABI Version: 0x0 + Type: EXEC (Executable file) + Machine: ARM + Version: 0x1 + Entry point address: 0x405 + Start of program headers: 52 (bytes into file) + Start of section headers: 153204 (bytes into file) + Flags: 0x5000200 + Size of this header: 52 (bytes) + Size of program headers: 32 (bytes) + Number of program headers: 2 + Size of section headers: 40 (bytes) + Number of section headers: 19 + Section header string table index: 18 +``` + +#if lang == "en" [ + `cargo-size` can print the size of the linker sections of the binary. +] else if lang == "de" [ + Mit `cargo-size` lässt sich die Größe der Linker-Abschnitte der + Binärdatei anzeigen. +] else { todo } + +```console +cargo size --bin app --release -- -A +``` + +#if lang == "en" [ + we use `--release` to inspect the optimized version +] else if lang == "de" [ + Wir verwenden `--release`, um die optimierte Version zu überprüfen. +] else { todo } + +```text +app : +section size addr +.vector_table 1024 0x0 +.text 92 0x400 +.rodata 0 0x45c +.data 0 0x20000000 +.bss 0 0x20000000 +.debug_str 2958 0x0 +.debug_loc 19 0x0 +.debug_abbrev 567 0x0 +.debug_info 4929 0x0 +.debug_ranges 40 0x0 +.debug_macinfo 1 0x0 +.debug_pubnames 2035 0x0 +.debug_pubtypes 1892 0x0 +.ARM.attributes 46 0x0 +.debug_frame 100 0x0 +.debug_line 867 0x0 +Total 14570 +``` + +#quote(block: true)[ +#if lang == "en" [ + A refresher on ELF linker sections + - `.text` contains the program instructions + - `.rodata` contains constant values like strings + - `.data` contains statically allocated variables whose initial values + are _not_ zero + - `.bss` also contains statically allocated variables whose initial + values _are_ zero + - `.vector_table` is a _non_-standard section that we use to store + the vector (interrupt) table + - `.ARM.attributes` and the `.debug_*` sections contain metadata and + will _not_ be loaded onto the target when flashing the binary. +] else if lang == "de" [ + Eine Auffrischung zum Thema ELF-Linker-Abschnitte + - `.text` enthält die Programmbefehle + - `.rodata` enthält konstante Werte wie Zeichenfolgen + - `.data` enthält statisch zugewiesene Variablen, deren Anfangswerte + _nicht_ Null + - Die Datei `.bss` enthält außerdem statisch zugewiesene Variablen, + deren Anfangswerte _sind_ Null + - `.vector_table` ist ein _nicht_ standardmäßiger Abschnitt, in dem + wir den Vektor speichern (interrupt) table + - Die Abschnitte `.ARM.attributes` und `.debug_*` enthalten Metadaten + und werden _nicht_ beim Flashen der Binärdatei auf das Zielgerät + geladen werden. +] else { todo } +] + +#if lang == "en" [ + *IMPORTANT*: ELF files contain metadata like debug information so + their _size on disk_ does _not_ accurately reflect the space + the program will occupy when flashed on a device. _Always_ use + `cargo-size` to check how big a binary really is. +] else if lang == "de" [ + *WICHTIG*: ELF-Dateien enthalten Metadaten wie + Debug-Informationen, sodass ihre _Größe auf der Festplatte_ + _nicht_ genau den Speicherplatz widerspiegelt, den das Programm + beanspruchen wird, wenn es auf ein Gerät geflasht wird. Verwenden Sie + _immer_ `cargo-size`, um zu überprüfen, wie groß eine Binärdatei + tatsächlich ist. +] + +#if lang == "en" [ +`cargo-objdump` can be used to disassemble the binary. +] else if lang == "de" [ + Mit `cargo-objdump` lässt sich die Binärdatei disassemblieren. +] + +```console +cargo objdump --bin app --release -- --disassemble --no-show-raw-insn --print-imm-hex +``` + +#quote(block: true)[ +#let ln = link("https://github.com/rust-embedded/book/issues/269") +#if lang == "en" [ + *NOTE* if the above command complains about + `Unknown command line argument` see the following bug report: #ln +] else if lang == "de" [ + *HINWEIS*: Falls der obige Befehl die Fehlermeldung „Unbekanntes + Befehlszeilenargument" ausgibt, siehe den folgenden Fehlerbericht: #ln +] else { todo } +] + +#quote(block: true)[ +#if lang == "en" [ + *NOTE* this output can differ on your system. New versions of + rustc, LLVM and libraries can generate different assembly. We truncated + some of the instructions to keep the snippet small. +] else if lang == "de" [ + *HINWEIS* Diese Ausgabe kann auf Ihrem System abweichen. Neuere + Versionen von rustc, LLVM und Bibliotheken können unterschiedlichen + Assemblercode erzeugen. Wir haben einige der Befehle gekürzt, um den + Ausschnitt kurz zu halten. +] else { todo } +] + +```text +app: file format ELF32-arm-little + +Disassembly of section .text: +main: + 400: bl #0x256 + 404: b #-0x4 + +Reset: + 406: bl #0x24e + 40a: movw r0, #0x0 + < .. truncated any more instructions .. > + +DefaultHandler_: + 656: b #-0x4 + +UsageFault: + 657: strb r7, [r4, #0x3] + +DefaultPreInit: + 658: bx lr + +__pre_init: + 659: strb r7, [r0, #0x1] + +__nop: + 65a: bx lr + +HardFaultTrampoline: + 65c: mrs r0, msp + 660: b #-0x2 + +HardFault_: + 662: b #-0x4 + +HardFault: + 663: +``` + += #(if lang == "en" [Running] + else if lang == "de" [Ausführen] + else { todo }) + +#if lang == "en" [ + Next, let's see how to run an embedded program on QEMU! This time we'll + use the `hello` example which actually does something. By default, this + example uses `[defmt]` and RTT to print text. +] else if lang == "de" [ + Als Nächstes schauen wir uns an, wie man ein eingebettetes Programm auf + QEMU ausführt! Diesmal verwenden wir das `hello`-Beispiel, das + tatsächlich etwas tut. Standardmäßig nutzt dieses Beispiel `[defmt]` und + RTT, um Text auszugeben. +] else { todo } + +#quote(block: true)[ +#if lang == "en" [ + *NOTE* `defmt` is a third-party dependency (i.e.~non-core) widely + used in the Embedded Rust ecosystem. +] else if lang == "de" [ + *HINWEIS* `defmt` ist eine Abhängigkeit eines Drittanbieters + (d. h. keine Kernkomponente), die im Embedded-Rust-Ökosystem weit + verbreitet ist. +] else { todo } +] + +#if lang == "en" [ + In order to read and decode the messages produced by `defmt` in the + host, we need to switch the RTT transport output to semihosting. When + using real hardware this requires a debug session but when using QEMU + this Just Works. +] else if lang == "de" [ + Um die von `defmt` im Host erzeugten Nachrichten lesen und entschlüsseln + zu können, müssen wir die RTT-Transportausgabe auf „Semihosting" + umstellen. Bei Verwendung von echter Hardware erfordert dies eine + Debug-Sitzung, bei Verwendung von QEMU funktioniert dies jedoch einfach + so. +] else { todo } + +#if lang == "en" [ + Let's switch the dependencies: +] else if lang == "de" [ + Stellen wir die Abhängigkeiten um: +] else { todo } + +```console +cargo remove defmt-rtt +cargo add defmt-semihosting +``` + +#if lang == "en" [ + Open `src/lib.rs` and replace `use defmt_rtt as _;` by `use defmt_semihosting as _;` +] else if lang == "de" [ + Öffnen Sie `src/lib.rs` und ersetzen Sie `use defmt_rtt as _;` durch + `use defmt_semihosting as _;`. +] else { todo } + +#if lang == "en" [ + Now we can build the example: +] else if lang == "de" [ + Nun können wir das Beispiel kompilieren: +] else { todo } + +```console +cargo build --bin hello +``` + +#if lang == "en" [ + The output binary will be located at `target/thumbv7m-none-eabi/debug/hello`. +] else if lang == "de" [ + Die ausgegebene Binärdatei befindet sich unter + `target/thumbv7m-none-eabi/debug/hello`. +] else { todo } + +#if lang == "en" [ + To run this binary on QEMU, the following command would be usually enough: +] else if lang == "de" [ + Um diese Binärdatei unter QEMU auszuführen, reicht in der Regel der + folgende Befehl aus: +] else { todo } + +```console +qemu-system-arm \ + -cpu cortex-m3 \ + -machine lm3s6965evb \ + -nographic \ + -semihosting-config enable=on,target=native \ + -kernel target/thumbv7m-none-eabi/debug/hello +``` + +#let ln_run = link("https://github.com/knurling-rs/defmt/tree/main/qemu-run/")[`qemu-run`] +#if lang == "en" [ + In our case, since we use `defmt`, the host will not be able to decode + the output. Instead, we will need a tool by Ferrous Systems named #ln_run: +] else if lang == "de" [ + Da wir in unserem Fall `defmt` verwenden, kann der Host die Ausgabe + nicht dekodieren. Stattdessen benötigen wir ein Werkzeug von Ferrous + Systems namens #ln_run: +] else { todo } + +```console +git clone git@github.com:knurling-rs/defmt.git +cd defmt/qemu-run/ +cargo run -- --machine lm3s6965evb ../qemu-rs/target/thumbv7m-none-eabi/debug/hello +``` + +```text +Hello, world! +``` + +#if lang == "en" [ + The command should successfully exit (exit code = 0) after printing the + text. On \*nix you can check that with the following command: +] else if lang == "de" [ + Der Befehl sollte nach der Ausgabe des Textes erfolgreich beendet werden + (Exit-Code = 0). Unter \*nix können Sie dies mit dem folgenden Befehl überprüfen: +] else { todo } + +```console +echo $? +``` + +```text +0 +``` + +#if lang == "en" [ + Let's break down that QEMU command: + - `qemu-system-arm`. This is the QEMU emulator. There are a few variants + of these QEMU binaries; this one does full _system_ emulation of + _ARM_ machines hence the name. + - `-cpu cortex-m3`. This tells QEMU to emulate a Cortex-M3 CPU. + Specifying the CPU model lets us catch some miscompilation errors: for + example, running a program compiled for the Cortex-M4F, which has a + hardware FPU, will make QEMU error during its execution. + - `-machine lm3s6965evb`. This tells QEMU to emulate the LM3S6965EVB, an + evaluation board that contains a LM3S6965 microcontroller. + - `-nographic`. This tells QEMU to not launch its GUI. + - `-semihosting-config (..)`. This tells QEMU to enable semihosting. + Semihosting lets the emulated device, among other things, use the host + stdout, stderr and stdin and create files on the host. + - `-kernel $file`. This tells QEMU which binary to load and run on the + emulated machine. +] else if lang == "de" [ + Schauen wir uns diesen QEMU-Befehl einmal genauer an: + - `qemu-system-arm`. Dies ist der QEMU-Emulator. Es gibt einige + Varianten dieser QEMU-Binärdateien; diese hier führt eine vollständige + _System_-Emulation von _ARM_-Rechnern durch, daher der Name. + - `-cpu cortex-m3`. Damit wird QEMU angewiesen, eine Cortex-M3-CPU zu + emulieren. Durch die Angabe des CPU-Modells lassen sich einige + Kompilierungsfehler erkennen: Wenn man beispielsweise ein Programm + ausführt, das für den Cortex-M4F kompiliert wurde, der über eine + Hardware-FPU verfügt, löst dies bei QEMU während der Ausführung einen Fehler aus. + - `-machine lm3s6965evb`. Damit wird QEMU angewiesen, das LM3S6965EVB zu + emulieren, ein Evaluierungsboard, das einen LM3S6965-Mikrocontroller enthält. + - `-nographic`. Damit wird QEMU angewiesen, seine grafische + Benutzeroberfläche nicht zu starten. + - `-semihosting-config (..)`. Damit wird QEMU angewiesen, Semihosting zu + aktivieren. Semihosting ermöglicht es dem emulierten Gerät unter + anderem, die Host-Ausgabe (stdout), die Host-Fehlerausgabe (stderr) + und die Host-Eingabe (stdin) zu nutzen sowie Dateien auf dem Host zu erstellen. + - `-kernel $file`. Damit wird QEMU mitgeteilt, welche Binärdatei auf der + emulierten Maschine geladen und ausgeführt werden soll. +] else { todo } + +#if lang == "en" [ + Typing out that long QEMU command is too much work! We can set a custom + runner to simplify the process. `.cargo/config.toml` has a commented out + runner that invokes QEMU; let's uncomment it: +] else if lang == "de" [ + Das Eintippen dieses langen QEMU-Befehls ist viel zu mühsam! Wir können + einen benutzerdefinierten Runner einrichten, um den Vorgang zu + vereinfachen. In der Datei `.cargo/config.toml` gibt es einen + auskommentierten Runner, der QEMU aufruft; entfernen wir die + Auskommentierung: +] else { todo } + +```console +head -n3 .cargo/config.toml +``` + +#raw(block: true, lang: "toml", +"[target.thumbv7m-none-eabi] +# " + if lang == "en" { + "uncomment this to make `cargo run` execute programs on QEMU" + } else if lang == "de" { + "Entferne den Kommentar hier, damit `cargo run` Programme auf QEMU ausfuehrt" + } else { todos } + " +runner = \"qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel\" +") + +#if lang == "en" [ + This runner only applies to the `thumbv7m-none-eabi` target, which is + our default compilation target. Now `cargo run` will compile the program + and run it on QEMU: +] else if lang == "de" [ + Dieser Runner gilt nur für das Ziel `thumbv7m-none-eabi`, das unser + Standard-Kompilierungsziel ist. Nun kompiliert `cargo run` das Programm + und führt es auf QEMU aus: +] else { todo } + +```console +cargo ru( + level: 2 + whole +)--release +``` + +```text + Compiling app v0.1.0 (file:///tmp/app) + Finished release [optimized + debuginfo] target(s) in 0.26s + Running `qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel target/thumbv7m-none-eabi/release/examples/hello` +Hello, world! +``` + += #(if lang == "en" [Debugging] + else if lang == "de" [Fehlerbehebung (Debugging)] + else { todo }) + +#if lang == "en" [ + Debugging is critical to embedded development. Let's see how it's done. +] else if lang == "de" [ + Das Debuggen ist für die Embedded-Entwicklung von entscheidender + Bedeutung. Schauen wir uns einmal an, wie es funktioniert. +] else { todo } + +#if lang == "en" [ + Debugging an embedded device involves _remote_ debugging as the + program that we want to debug won't be running on the machine that's + running the debugger program (GDB or LLDB). +] else if lang == "de" [ + Das Debuggen eines Embedded-Geräts erfolgt _remote_, da das + Programm, das wir debuggen möchten, nicht auf dem Rechner läuft, auf dem + das Debugger-Programm (GDB oder LLDB) ausgeführt wird. +] else { todo } + +#if lang == "en" [ + Remote debugging involves a client and a server. In a QEMU setup, the + client will be a GDB (or LLDB) process and the server will be the QEMU + process that's also running the embedded program. +] else if lang == "de" [ + Beim Remote-Debugging sind ein Client und ein Server beteiligt. In einer + QEMU-Umgebung ist der Client ein GDB- (oder LLDB-)Prozess und der Server + der QEMU-Prozess, auf dem auch das eingebettete Programm läuft. +] else { todo } + +#if lang == "en" [ + In this section we'll use the `hello` example we already compiled. +] else if lang == "de" [ + In diesem Abschnitt verwenden wir das bereits kompilierte Beispiel `hello`. +] else { todo } + +#if lang == "en" [ + The first debugging step is to launch QEMU in debugging mode: +] else if lang == "de" [ + Der erste Schritt beim Debuggen besteht darin, QEMU im Debugging-Modus zu starten: +] else { todo } + +```console +qemu-system-arm \ + -cpu cortex-m3 \ + -machine lm3s6965evb \ + -nographic \ + -semihosting-config enable=on,target=native \ + -gdb tcp::3333 \ + -S \ + -kernel target/thumbv7m-none-eabi/debug/examples/hello +``` + +#if lang == "en" [ + This command won't print anything to the console and will block the + terminal. We have passed two extra flags this time: + - `-gdb tcp::3333`. This tells QEMU to wait for a GDB connection on TCP + port 3333. + - `-S`. This tells QEMU to freeze the machine at startup. Without this + the program would have reached the end of main before we had a chance + to launch the debugger! +] else if lang == "de" [ + Dieser Befehl gibt nichts auf der Konsole aus und blockiert das + Terminal. Wir haben diesmal zwei zusätzliche Flags übergeben: + - `-gdb tcp::3333`. Damit wird QEMU angewiesen, auf eine GDB-Verbindung + über den TCP-Port 3333 zu warten. + - `-S`. Damit wird QEMU angewiesen, die Maschine beim Start + einzufrieren. Ohne diese Option hätte das Programm das Ende der + Funktion `main` erreicht, bevor wir die Gelegenheit gehabt hätten, den + Debugger zu starten! +] else { todo } + +#if lang == "en" [ + Next we launch GDB in another terminal and tell it to load the debug + symbols of the example: +] else if lang == "de" [ + Als Nächstes starten wir GDB in einem anderen Terminal und weisen es an, + die Debug-Symbole des Beispiels zu laden: +] else { todo } + +```console +gdb-multiarch -q target/thumbv7m-none-eabi/debug/examples/hello +``` + +#if lang == "en" [ + *NOTE*: you might need another version of gdb instead of + `gdb-multiarch` depending on which one you installed in the installation + chapter. This could also be `arm-none-eabi-gdb` or just `gdb`. +] else if lang == "de" [ + *HINWEIS*: Möglicherweise benötigen Sie anstelle von + `gdb-multiarch` eine andere Version von gdb, je nachdem, welche Sie im + Kapitel zur Installation installiert haben. Dies könnte auch + `arm-none-eabi-gdb` oder einfach nur `gdb` sein. +] else { todo } + +#if lang == "en" [ + Then within the GDB shell we connect to QEMU, which is waiting for a + connection on TCP port 3333. +] else if lang == "de" [ + Anschließend stellen wir innerhalb der GDB-Shell eine Verbindung zu QEMU + her, das auf dem TCP-Port 3333 auf eine Verbindung wartet. +] else { todo } + +```console +target remote :3333 +``` + +```text +Remote debugging using :3333 +Reset () at $REGISTRY/cortex-m-rt-0.6.1/src/lib.rs:473 +473 pub unsafe extern "C" fn Reset() -> ! { +``` + +#if lang == "en" [ + You'll see that the process is halted and that the program counter is + pointing to a function named `Reset`. That is the reset handler: what + Cortex-M cores execute upon booting. +] else if lang == "de" [ + Sie werden feststellen, dass der Prozess angehalten wurde und der + Programmzähler auf eine Funktion namens `Reset` zeigt. Das ist der + Reset-Handler: das, was Cortex-M-Kerne beim Booten ausführen. +] else { todo } + +#quote(block: true)[ +#if lang == "en" [ + Note that on some setup, instead of displaying the line + `Reset () at $REGISTRY/cortex-m-rt-0.6.1/src/lib.rs:473` as shown above, + gdb may print some warnings like: +] else if lang == "de" [ + Beachten Sie, dass gdb in manchen Konfigurationen anstelle der oben + gezeigten Zeile `Reset () at $REGISTRY/cortex-m-rt-0.6.1/src/lib.rs:473` + möglicherweise Warnungen wie die folgenden ausgibt: +] else { todo } + +`core::num::bignum::Big32x40::mul_small () at src/libcore/num/bignum.rs:254` +`src/libcore/num/bignum.rs: No such file or directory.` + +#if lang == "en" [ + That's a known glitch. You can safely ignore those warnings, you're most + likely at Reset(). +] else if lang == "de" [ + Das ist ein bekannter Fehler. Sie können diese Warnungen getrost + ignorieren, da Sie sich höchstwahrscheinlich bei `Reset()` befinden. +] else { todo } +] + +#if lang == "en" [ + This reset handler will eventually call our main function. Let's skip + all the way there using a breakpoint and the `continue` command. To set + the breakpoint, let's first take a look where we would like to break in + our code, with the `list` command. +] else if lang == "de" [ + Dieser Reset-Handler ruft schließlich unsere Hauptfunktion auf. Lassen + Sie uns den gesamten Weg dorthin mithilfe eines Haltepunkts und des + Befehls `continue` überspringen. Um den Haltepunkt zu setzen, schauen + wir uns zunächst mit dem Befehl `list` an, an welcher Stelle in unserem + Code wir anhalten möchten. +] else { todo } + +```console +list main +``` + +#if lang == "en" [ + This will show the source code, from the file examples/hello.rs. +] else if lang == "de" [ + Dadurch wird der Quellcode aus der Datei „examples/hello.rs" angezeigt. +] else { todo } + +```text +6 use panic_halt as _; +7 +8 use cortex_m_rt::entry; +9 use cortex_m_semihosting::{debug, hprintln}; +10 +11 #[entry] +12 fn main() -> ! { +13 hprintln!("Hello, world!").unwrap(); +14 +15 // exit QEMU +``` + +#if lang == "en" [ + We would like to add a breakpoint just before the "Hello, world!", which + is on line 13. We do that with the `break` command: +] else if lang == "de" [ + Wir möchten einen Haltepunkt direkt vor „Hello, world!" setzen, das sich + in Zeile 13 befindet. Dazu verwenden wir den Befehl `break`: +] else { todo } + +```console +break 13 +``` + +#if lang == "en" [ + We can now instruct gdb to run up to our main function, with the + `continue` command: +] else if lang == "de" [ + Wir können gdb nun mit dem Befehl `continue` anweisen, bis zu unserer + Hauptfunktion weiterzulaufen: +] else { todo } + +```console +continue +``` + +```text +Continuing. + +Breakpoint 1, hello::__cortex_m_rt_main () at examples\hello.rs:13 +13 hprintln!("Hello, world!").unwrap(); +``` + +#if lang == "en" [ + We are now close to the code that prints "Hello, world!". Let's move + forward using the `next` command. +] else if lang == "de" [ + Wir sind nun fast bei dem Code angelangt, der „Hello, world!" ausgibt. + Machen wir weiter mit dem Befehl `next`. +] else { todo } + +```console +next +``` + +```text +16 debug::exit(debug::EXIT_SUCCESS); +``` + +#if lang == "en" [ + At this point you should see "Hello, world!" printed on the terminal + that's running `qemu-system-arm`. +] else if lang == "de" [ + An dieser Stelle sollte auf dem Terminal, auf dem `qemu-system-arm` + läuft, „Hello, world!" angezeigt werden. +] else { todo } + +```text +$ qemu-system-arm (..) +Hello, world! +``` + +#if lang == "en" [ + Calling `next` again will terminate the QEMU process. +] else if lang == "de" [ + Ein erneuter Aufruf von `next` beendet den QEMU-Prozess. +] else { todo } + +```console +next +``` + +```text +[Inferior 1 (Remote target) exited normally] +``` + +#if lang == "en" [ + You can now exit the GDB session. +] else if lang == "de" [ + Sie können die GDB-Sitzung nun beenden. +] else { todo } + +```console +quit +``` diff --git a/typ/start/registers.typ b/typ/start/registers.typ new file mode 100644 index 00000000..4101e526 --- /dev/null +++ b/typ/start/registers.typ @@ -0,0 +1,571 @@ +#import "../config.typ": * + +#h1(offset: whole, + if lang == "en" [Memory Mapped Registers] + else if lang == "de" [Im Speicher abgebildete Register] + else { todo }) + +#set heading(offset: whole*2) + +#if lang == "en" [ + Embedded systems can only get so far by executing normal Rust code and + moving data around in RAM. If we want to get any information into or out + of our system (be that blinking an LED, detecting a button press or + communicating with an off-chip peripheral on some sort of bus) we're + going to have to dip into the world of Peripherals and their 'memory + mapped registers'. +] else if lang == "de" [ + Bei eingebetteten Systemen stößt man mit der reinen Ausführung von + Standard-Rust-Code und dem Verschieben von Daten im Arbeitsspeicher + (RAM) irgendwann an Grenzen. Wenn wir Informationen in das System + einspeisen oder daraus ausgeben wollen -- sei es das Blinken einer LED, + das Erkennen eines Tastendrucks oder die Kommunikation mit einer + externen Peripheriekomponente über einen Bus --, müssen wir uns mit der + Welt der Peripherieeinheiten und deren „im Speicher abgebildeten + Registern" (memory-mapped registers) befassen. +] else { todo } + +#let ln_cortex = link("https://crates.io/crates/cortex-m")[cortex-m] +#let ln_tm4c123x = link("https://crates.io/crates/tm4c123x")[tm4c123x] +#let ln_f30x = link("https://crates.io/crates/stm32f30x")[stm32f30x] +#let ln_hal = link("https://crates.io/crates/embedded-hal")[embedded-hal] +#let ln_f3 = link("https://crates.io/crates/stm32f3-discovery")[stm32f3-discovery] +#if lang == "en" [ + You may well find that the code you need to access the peripherals in + your micro-controller has already been written, at one of the following + levels: + - Micro-architecture Crate - This sort of crate handles any useful + routines common to the processor core your microcontroller is using, + as well as any peripherals that are common to all micro-controllers + that use that particular type of processor core. For example the + #ln_cortex crate gives you + functions to enable and disable interrupts, which are the same for all + Cortex-M based micro-controllers. It also gives you access to the + 'SysTick' peripheral included with all Cortex-M based + micro-controllers. + - Peripheral Access Crate (PAC) - This sort of crate is a thin wrapper + over the various memory-wrapper registers defined for your particular + part-number of micro-controller you are using. For example, + #ln_tm4c123x for the Texas Instruments Tiva-C TM4C123 series, or + #ln_f30x for the ST-Micro STM32F30x series. + Here, you'll be interacting with the + registers directly, following each peripheral's operating instructions + given in your micro-controller's Technical Reference Manual. + - HAL Crate - These crates offer a more user-friendly API for your + particular processor, often by implementing some common traits defined + in #ln_hal. For + example, this crate might offer a `Serial` struct, with a constructor + that takes an appropriate set of GPIO pins and a baud rate, and offers + some sort of `write_byte` function for sending data. See the chapter + on #link()[Portability] for more information + on #ln_hal. + - Board Crate - These crates go one step further than a HAL Crate by + pre-configuring various peripherals and GPIO pins to suit the specific + developer kit or board you are using, such as #ln_f3 + for the STM32F3DISCOVERY board. +] else if lang == "de" [ + Möglicherweise stellen Sie fest, dass der für den Zugriff auf die + Peripherie Ihres Mikrocontrollers erforderliche Code bereits auf einer + der folgenden Ebenen implementiert wurde: + - Mikroarchitektur-Crate -- Diese Art von Crate verwaltet alle + nützlichen Routinen, die dem Prozessorkern gemeinsam sind, den Ihr + Mikrocontroller verwendet, sowie alle Peripheriegeräte, die allen + Mikrocontrollern gemeinsam sind, die diesen bestimmten + Prozessorkerntyp verwenden. Das #ln_cortex - Crate bietet + Ihnen beispielsweise Funktionen zum Aktivieren und Deaktivieren von + Interrupts, die für alle Cortex-M-basierten Mikrocontroller gleich + sind. Außerdem erhalten Sie Zugriff auf die „SysTick"-Peripherie, die + in allen Cortex-M-basierten Mikrocontrollern enthalten ist. + - Peripheral Access Crate (PAC) -- Bei dieser Art von Crate handelt es + sich um einen schlanken Adapter (Wrapper) für die verschiedenen + Register, die für das spezifische Mikrocontroller-Modell definiert + sind, das Sie verwenden. Zum Beispiel, #ln_tm4c123x für die + Tiva-C-TM4C123-Serie von Texas Instruments oder #ln_f30x für die + STM32F30x-Serie von STMicroelectronics. Hierbei greifen Sie direkt auf + die Register zu und befolgen dabei die Betriebshinweise für die + jeweilige Peripherieeinheit, wie sie im technischen Referenzhandbuch + Ihres Mikrocontrollers beschrieben sind. + - HAL-Crate -- Diese Crates bieten eine benutzerfreundlichere API für + einen bestimmten Prozessor, häufig durch die Implementierung gängiger + Traits, die in #ln_hal definiert + sind. So könnte ein solches Crate beispielsweise eine + `Serial`-Struktur bereitstellen, deren Konstruktor eine geeignete + Kombination aus GPIO-Pins sowie eine Baudrate entgegennimmt und eine + Funktion wie `write_byte` zum Senden von Daten anbietet. Weitere + Informationen zu #ln_hal finden + Sie im Kapitel über #link()[Portabilität]. + - Board-Crate -- Diese Crates gehen noch einen Schritt weiter als + HAL-Crates, indem sie verschiedene Peripheriekomponenten und GPIO-Pins + passend für das jeweils verwendete Entwickler-Kit oder Board + vorkonfigurieren -- wie zum Beispiel + #ln_f3 für das STM32F3DISCOVERY-Board. +] else { todo } + += #(if lang == "en" [Board Crate] + else if lang == "de" [Board Crate] + else { todo }) + +#if lang == "en" [ + A board crate is the perfect starting point, if you're new to embedded + Rust. They nicely abstract the HW details that might be overwhelming + when starting studying this subject, and makes standard tasks easy, like + turning a LED on or off. The functionality it exposes varies a lot + between boards. Since this book aims at staying hardware agnostic, the + board crates won't be covered by this book. +] else if lang == "de" [ + Eine Board-Crate ist der ideale Ausgangspunkt für den Einstieg in + Embedded Rust. Sie abstrahiert auf angenehme Weise die Hardware-Details, + die Anfänger in diesem Bereich oft überfordern können, und erleichtert + Standardaufgaben wie das Ein- oder Ausschalten einer LED. Der + bereitgestellte Funktionsumfang variiert jedoch stark von Board zu + Board. Da dieses Buch hardwareunabhängig bleiben soll, werden + Board-Crates hier nicht behandelt. +] else { todo } + +#let url_disco = "https://rust-embedded.github.io/discovery/" +#if lang == "en" [ + If you want to experiment with the STM32F3DISCOVERY board, it is highly + recommended to take a look at the #ln_f3 + board crate, which provides functionality to blink the board LEDs, + access its compass, bluetooth and more. The + #link(url_disco)[Discovery] book + offers a great introduction to the use of a board crate. +] else if lang == "de" [ + Wenn Sie mit dem STM32F3DISCOVERY-Board experimentieren möchten, ist es + sehr empfehlenswert, sich das #ln_f3;-Board-Crate + anzusehen; dieses bietet Funktionen, um die LEDs des Boards blinken zu + lassen sowie auf den Kompass, Bluetooth und mehr zuzugreifen. Das + #link(url_disco)[Discovery]-Buch bietet eine hervorragende + Einführung in die Verwendung eines Board-Crates. +] else { todo } + +#if lang == "en" [ + But if you're working on a system that doesn't yet have dedicated board + crate, or you need functionality not provided by existing crates, read + on as we start from the bottom, with the micro-architecture crates. +] else if lang == "de" [ + Wenn Sie jedoch an einem System arbeiten, für das es noch kein + dediziertes Board-Crate gibt, oder wenn Sie Funktionen benötigen, die + von vorhandenen Crates nicht abgedeckt werden, lesen Sie weiter: Wir + beginnen ganz unten, bei den Mikroarchitektur-Crates. +] else { todo } + += #(if lang == "en" [Micro-architecture crate] + else if lang == "de" [Mikroarchitektur-Crate] + else { todo }) + +#if lang == "en" [ + Let's look at the SysTick peripheral that's common to all Cortex-M based + micro-controllers. We can find a pretty low-level API in the + #ln_cortex crate, and we can use it like this: +] else if lang == "de" [ + Betrachten wir die SysTick-Peripherie, die allen Mikrocontrollern auf + Cortex-M-Basis gemeinsam ist. Im #ln_cortex;-Crate finden wir + eine recht hardwarenahe API, die sich folgendermaßen verwenden lässt: +] else { todo } + +```rust +#![no_std] +#![no_main] +use cortex_m::peripheral::{syst, Peripherals}; +use cortex_m_rt::entry; +use panic_halt as _; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take().unwrap(); + let mut systick = peripherals.SYST; + systick.set_clock_source(syst::SystClkSource::Core); + systick.set_reload(1_000); + systick.clear_current(); + systick.enable_counter(); + while !systick.has_wrapped() { + // Loop + } + + loop {} +} +``` + +#if lang == "en" [ + The functions on the `SYST` struct map pretty closely to the + functionality defined by the ARM Technical Reference Manual for this + peripheral. There's nothing in this API about 'delaying for X + milliseconds' - we have to crudely implement that ourselves using a + `while` loop. Note that we can't access our `SYST` struct until we have + called `Peripherals::take()` - this is a special routine that guarantees + that there is only one `SYST` structure in our entire program. For more + on that, see the #link()[Peripherals] section. +] else if lang == "de" [ + Die Funktionen der `SYST`-Struktur entsprechen weitgehend der + Funktionalität, die im ARM Technical Reference Manual für diese + Peripherieeinheit definiert ist. Diese API bietet keine Möglichkeit, + eine Verzögerung von „X Millisekunden" direkt umzusetzen; wir müssen + dies stattdessen selbst auf einfache Weise mittels einer + `while`-Schleife implementieren. Beachten Sie, dass wir erst auf die + `SYST`-Struktur zugreifen können, nachdem wir `Peripherals::take()` + aufgerufen haben. Dabei handelt es sich um eine spezielle Routine, die + sicherstellt, dass im gesamten Programm nur eine einzige `SYST`-Instanz + existiert. Weitere Informationen dazu finden Sie im Abschnitt + "#link()[Peripherien]". +] else { todo } + += #(if lang == "en" [Using a Peripheral Access Crate (PAC)] + else if lang == "de" [Verwendung eines Peripheral Access Crate (PAC)] + else { todo }) + +#if lang == "en" [ + We won't get very far with our embedded software development if we + restrict ourselves to only the basic peripherals included with every + Cortex-M. At some point, we're going to need to write some code that's + specific to the particular micro-controller we're using. In this + example, let's assume we have an Texas Instruments TM4C123 - a middling + 80MHz Cortex-M4 with 256 KiB of Flash. We're going to pull in the + #ln_tm4c123x crate to make use of this chip. +] else if lang == "de" [ + Bei der Entwicklung von Embedded-Software kommen wir nicht weit, wenn + wir uns auf die grundlegenden Peripheriekomponenten beschränken, die in + jedem Cortex-M enthalten sind. Irgendwann müssen wir Code schreiben, der + speziell auf den verwendeten Mikrocontroller zugeschnitten ist. Gehen + wir in diesem Beispiel davon aus, dass wir einen Texas Instruments + TM4C123 verwenden -- einen soliden 80-MHz-Cortex-M4 mit 256 KiB + Flash-Speicher. Um diesen Chip nutzen zu können, binden wir das + #ln_tm4c123x;-Crate ein. +] else { todo } + +```rust +#![no_std] +#![no_main] + +use panic_halt as _; // panic handler + +use cortex_m_rt::entry; +use tm4c123x; + +#[entry] +pub fn init() -> (Delay, Leds) { + let cp = cortex_m::Peripherals::take().unwrap(); + let p = tm4c123x::Peripherals::take().unwrap(); + + let pwm = p.PWM0; + pwm.ctl.write(|w| w.globalsync0().clear_bit()); + // Mode = 1 => Count up/down mode + pwm._2_ctl.write(|w| w.enable().set_bit().mode().set_bit()); + pwm._2_gena.write(|w| w.actcmpau().zero().actcmpad().one()); + // 528 cycles (264 up and down) = 4 loops per video line (2112 cycles) + pwm._2_load.write(|w| unsafe { w.load().bits(263) }); + pwm._2_cmpa.write(|w| unsafe { w.compa().bits(64) }); + pwm.enable.write(|w| w.pwm4en().set_bit()); +} +``` + +#let ln_svd2rust = link("https://crates.io/crates/svd2rust")[svd2rust] +#if lang == "en" [ + We've accessed the `PWM0` peripheral in exactly the same way as we + accessed the `SYST` peripheral earlier, except we called + `tm4c123x::Peripherals::take()`. As this crate was auto-generated using + #ln_svd2rust, the access + functions for our register fields take a closure, rather than a numeric + argument. While this looks like a lot of code, the Rust compiler can use + it to perform a bunch of checks for us, but then generate machine-code + which is pretty close to hand-written assembler! Where the + auto-generated code isn't able to determine that all possible arguments + to a particular accessor function are valid (for example, if the SVD + defines the register as 32-bit but doesn't say if some of those 32-bit + values have a special meaning), then the function is marked as `unsafe`. + We can see this in the example above when setting the `load` and `compa` + sub-fields using the `bits()` function. +] else if lang == "de" [ + Wir haben auf die `PWM0`-Peripherie auf genau dieselbe Weise zugegriffen + wie zuvor auf die `SYST`-Peripherie, mit dem Unterschied, dass wir + `tm4c123x::Peripherals::take()` aufgerufen haben. Da dieses Crate + mithilfe von #link("https://crates.io/crates/svd2rust")[svd2rust] + automatisch generiert wurde, erwarten die Zugriffsfunktionen für unsere + Registerfelder einen Closure anstelle eines numerischen Arguments. Auch + wenn dies nach viel Code aussieht, kann der Rust-Compiler ihn nutzen, um + eine Reihe von Prüfungen für uns durchzuführen und anschließend + Maschinencode zu erzeugen, der handgeschriebenem Assemblercode sehr + nahekommt! Wenn der automatisch generierte Code nicht feststellen kann, + ob alle möglichen Argumente für eine bestimmte Zugriffsfunktion gültig + sind (zum Beispiel, wenn die SVD das Register als 32-Bit-Register + definiert, aber nicht angibt, ob bestimmte dieser 32-Bit-Werte eine + besondere Bedeutung haben), wird die Funktion als `unsafe` markiert. + Dies lässt sich im obigen Beispiel beim Setzen der Unterfelder `load` + und `compa` mittels der Funktion `bits()` beobachten. +] else { todo } + +== #(if lang == "en" [Reading] + else if lang == "de" [Lesen] + else { todo }) + +#let url_struct_r = "https://docs.rs/tm4c123x/0.7.0/tm4c123x/pwm0/ctl/struct.R.html" +#if lang == "en" [ + The `read()` function returns an object which gives read-only access to + the various sub-fields within this register, as defined by the + manufacturer's SVD file for this chip. You can find all the functions + available on special `R` return type for this particular register, in + this particular peripheral, on this particular chip, in the + #link(url_struct_r)[tm4c123x documentation]. +] else if lang == "de" [ + Die Funktion `read()` gibt ein Objekt zurück, das schreibgeschützten + Zugriff auf die verschiedenen Teilfelder dieses Registers gewährt -- + entsprechend der vom Hersteller für diesen Chip bereitgestellten + SVD-Datei. Alle für den speziellen Rückgabetyp `R` dieses spezifischen + Registers (innerhalb der jeweiligen Peripherieeinheit auf diesem Chip) + verfügbaren Funktionen finden Sie in der + #link(url_struct_r)[tm4c123x-Dokumentation]. +] else { todo } + +#raw(block: true, lang: "rust", +"if pwm.ctl.read().globalsync0().is_set() { + // " + if lang == "en" { + "Do a thing" + } else if lang == "de" { + "Tu etwas" + } else { todos } + " +} +") + +== #(if lang == "en" [Writing] + else if lang == "de" [Schreiben] + else { todo }) + +#let url_struct_w = "https://docs.rs/tm4c123x/0.7.0/tm4c123x/pwm0/ctl/struct.W.html" +#if lang == "en" [ + The `write()` function takes a closure with a single argument. Typically + we call this `w`. This argument then gives read-write access to the + various sub-fields within this register, as defined by the + manufacturer's SVD file for this chip. Again, you can find all the + functions available on the 'w' for this particular register, in this + particular peripheral, on this particular chip, in the + #link(url_struct_w)[tm4c123x documentation]. + Note that all of the sub-fields that we do not set will be set to a + default value for us - any existing content in the register will be lost. +] else if lang == "de" [ + Die Funktion `write()` erwartet einen Closure mit einem einzelnen + Argument. Üblicherweise bezeichnen wir dieses als `w`. Dieses Argument + gewährt Lese- und Schreibzugriff auf die verschiedenen Teilfelder + innerhalb des Registers, wie sie in der SVD-Datei des Herstellers für + diesen Chip definiert sind. Auch hier gilt: Alle für `w` verfügbaren + Funktionen -- spezifisch für dieses Register, diese Peripherieeinheit + und diesen Chip -- finden Sie in der + #link(url_struct_w)[tm4c123x-Dokumentation]. + Beachten Sie, dass alle Teilfelder, die wir nicht explizit setzen, + automatisch auf einen Standardwert gesetzt werden; bereits vorhandene + Inhalte des Registers gehen dabei verloren. +] else { todo } + +```rust +pwm.ctl.write(|w| w.globalsync0().clear_bit()); +``` + +== #(if lang == "en" [Modifying] + else if lang == "de" [Ändern] + else { todo }) + +#if lang == "en" [ + If we wish to change only one particular sub-field in this register and + leave the other sub-fields unchanged, we can use the `modify` function. + This function takes a closure with two arguments - one for reading and + one for writing. Typically we call these `r` and `w` respectively. The + `r` argument can be used to inspect the current contents of the + register, and the `w` argument can be used to modify the register contents. +] else if lang == "de" [ + Wenn wir in diesem Register nur ein bestimmtes Teilfeld ändern und die + übrigen Teilfelder unverändert lassen möchten, können wir die Funktion + `modify` verwenden. Diese Funktion nimmt eine Closure mit zwei + Argumenten entgegen -- eines zum Lesen und eines zum Schreiben. + Üblicherweise bezeichnen wir diese als `r` beziehungsweise `w`. Das + Argument `r` dient dazu, den aktuellen Inhalt des Registers zu + betrachten, während das Argument `w` genutzt werden kann, um den + Registerinhalt zu ändern. +] else { todo } + +```rust +pwm.ctl.modify(|r, w| w.globalsync0().clear_bit()); +``` + +#if lang == "en" [ + The `modify` function really shows the power of closures here. In C, + we'd have to read into some temporary value, modify the correct bits and + then write the value back. This means there's considerable scope for error: +] else if lang == "de" [ + Die Funktion `modify` veranschaulicht hier eindrucksvoll die + Leistungsfähigkeit von Closures. In C müssten wir den Wert zunächst in + eine temporäre Variable einlesen, die entsprechenden Bits ändern und den + Wert anschließend wieder zurückschreiben. Dies birgt ein erhebliches + Fehlerpotenzial: +] else { todo } + +```c +uint32_t temp = pwm0.ctl.read(); +temp |= PWM0_CTL_GLOBALSYNC0; +pwm0.ctl.write(temp); +uint32_t temp2 = pwm0.enable.read(); +temp2 |= PWM0_ENABLE_PWM4EN; +pwm0.enable.write(temp); // Uh oh! Wrong variable! +``` + += #(if lang == "en" [Using a HAL crate] + else if lang == "de" [Verwendung eines HAL-Crates] + else { todo }) + +#if lang == "en" [ + The HAL crate for a chip typically works by implementing a custom Trait + for the raw structures exposed by the PAC. Often this trait will define + a function called `constrain()` for single peripherals or `split()` for + things like GPIO ports with multiple pins. This function will consume + the underlying raw peripheral structure and return a new object with a + higher-level API. This API may also do things like have the Serial port + `new` function require a borrow on some `Clock` structure, which can + only be generated by calling the function which configures the PLLs and + sets up all the clock frequencies. In this way, it is statically + impossible to create a Serial port object without first having + configured the clock rates, or for the Serial port object to misconvert + the baud rate into clock ticks. Some crates even define special traits + for the states each GPIO pin can be in, requiring the user to put a pin + into the correct state (say, by selecting the appropriate Alternate + Function Mode) before passing the pin into Peripheral. All with no + run-time cost! +] else if lang == "de" [ + Ein HAL-Crate für einen Chip funktioniert typischerweise, indem es ein + spezifisches Trait für die vom PAC bereitgestellten Rohstrukturen + implementiert. Oft definiert dieses Trait eine Funktion namens + `constrain()` für einzelne Peripherieeinheiten oder `split()` für + Komponenten wie GPIO-Ports mit mehreren Pins. Diese Funktion übernimmt + die zugrundeliegende Rohstruktur der Peripherie und gibt ein neues + Objekt mit einer höherwertigen API zurück. Diese API kann zudem + sicherstellen, dass beispielsweise die `new`-Funktion für die serielle + Schnittstelle eine Referenz auf eine `Clock`-Struktur verlangt; eine + solche Struktur lässt sich nur durch den Aufruf der Funktion erzeugen, + welche die PLLs konfiguriert und sämtliche Taktfrequenzen einstellt. Auf + diese Weise ist es zur Kompilierzeit ausgeschlossen, ein Objekt für die + serielle Schnittstelle zu erzeugen, ohne zuvor die Taktraten + konfiguriert zu haben, oder dass das Objekt die Baudrate fehlerhaft in + Taktzyklen umrechnet. Manche Crates definieren sogar spezielle Traits + für die Zustände, die ein GPIO-Pin einnehmen kann; der Nutzer muss den + Pin dann in den korrekten Zustand versetzen (etwa durch Auswahl des + passenden Modus für alternative Funktionen), bevor er ihn an die + Peripherieeinheit übergibt. Und das alles ohne Laufzeitkosten! +] else { todo } + +#if lang == "en" [ + Let's see an example: +] else if lang == "de" [ + Schauen wir uns ein Beispiel an: +] else { todo } + +#raw(block: true, lang: "rust", +"#![no_std] +#![no_main] + +use panic_halt as _; // panic handler + +use cortex_m_rt::entry; +use tm4c123x_hal as hal; +use tm4c123x_hal::prelude::*; +use tm4c123x_hal::serial::{NewlineMode, Serial}; +use tm4c123x_hal::sysctl; + +#[entry] +fn main() -> ! { + let p = hal::Peripherals::take().unwrap(); + let cp = hal::CorePeripherals::take().unwrap(); + + // " + if lang == "en" { + "Wrap up the SYSCTL struct into an object with a higher-layer API" + } else if lang == "de" { + "Kapseln Sie die SYSCTL-Struktur in einem Objekt mit einer API auf + // hoeherer Ebene." + } else { todos } + " + let mut sc = p.SYSCTL.constrain(); + // " + if lang == "en" { + "Pick our oscillation settings" + } else if lang == "de" { + "Waehlen Sie unsere Oszillationseinstellungen aus." + } else { todos } + " + sc.clock_setup.oscillator = sysctl::Oscillator::Main( + sysctl::CrystalFrequency::_16mhz, + sysctl::SystemClock::UsePll(sysctl::PllOutputFrequency::_80_00mhz), + ); + // " + if lang == "en" { + "Configure the PLL with those settings" + } else if lang == "de" { + "Konfigurieren Sie die PLL mit diesen Einstellungen." + } else { todos } + " + let clocks = sc.clock_setup.freeze(); + + // " + if lang == "en" { + "Wrap up the GPIO_PORTA struct into an object with a higher-layer API. + // Note it needs to borrow `sc.power_control` so it can power up the GPIO + // peripheral automatically." + } else if lang == "de" { + "Kapseln Sie die `GPIO_PORTA`-Struktur in einem Objekt mit einer API der + // hoeheren Ebene. + // Beachten Sie, dass es `sc.power_control` nutzen muss, um die + // Stromversorgung der GPIO-Peripherie automatisch zu aktivieren." + } else { todos } + " + let mut porta = p.GPIO_PORTA.split(&sc.power_control); + + // " + if lang == "en" { + "Activate the UART." + } else if lang == "de" { + "Aktivieren Sie den UART." + } else { todos } + " + let uart = Serial::uart0( + p.UART0, + // " + if lang == "en" { + "The transmit pin" + } else if lang == "de" { + "Der Sende-Pin" + } else { todos } + " + porta + .pa1 + .into_af_push_pull::(&mut porta.control), + // " + if lang == "en" { + "The receive pin" + } else if lang == "de" { + "Der Empfangs-Pin" + } else { todos } + " + porta + .pa0 + .into_af_push_pull::(&mut porta.control), + // " + if lang == "en" { + "No RTS or CTS required" + } else if lang == "de" { + "Kein RTS oder CTS erforderlich" + } else { todos } + " + (), + (), + // " + if lang == "en" { + "The baud rate" + } else if lang == "de" { + "Die Baudrate" + } else { todos } + " + 115200_u32.bps(), + // " + if lang == "en" { + "Output handling" + } else if lang == "de" { + "Ausgabeverarbeitung" + } else { todos } + " + NewlineMode::SwapLFtoCRLF, + // " + if lang == "en" { + "We need the clock rates to calculate the baud rate divisors" + } else if lang == "de" { + "Wir benoetigen die Taktfrequenzen, um die Baudraten-Teiler zu + // berechnen." + } else { todos } + " + &clocks, + // " + if lang == "en" { + "We need this to power up the UART peripheral" + } else if lang == "de" { + "Wir benoetigen dies, um die UART-Peripherie zu aktivieren." + } else { todos } + " + &sc.power_control, + ); + + loop { + writeln!(uart, \"Hello, World!\\r\\n\").unwrap(); + } +} +") diff --git a/typ/start/semihosting.typ b/typ/start/semihosting.typ new file mode 100644 index 00000000..dcde540e --- /dev/null +++ b/typ/start/semihosting.typ @@ -0,0 +1,227 @@ +#import "../config.typ": * + +#h1(offset: whole, + if lang == "en" [Semihosting] + else if lang == "de" [Semihosting] + else { todo }) +#set heading(offset: whole*2) + +#if lang == "en" [ + Semihosting is a mechanism that lets embedded devices do I/O on the host + and is mainly used to log messages to the host console. Semihosting + requires a debug session and pretty much nothing else (no extra wires!) + so it's super convenient to use. The downside is that it's super slow: + each write operation can take several milliseconds depending on the + hardware debugger (e.g.~ST-Link) you use. +] else if lang == "de" [ + Semihosting ist ein Mechanismus, der es eingebetteten Systemen + ermöglicht, Ein-/Ausgabeoperationen über den eigenen Rechner (Host) + durchzuführen; er wird hauptsächlich dazu genutzt, Meldungen auf der + Host-Konsole zu protokollieren. Da Semihosting lediglich eine + Debug-Sitzung und sonst so gut wie nichts (keine zusätzlichen Kabel!) + erfordert, ist es äußerst komfortabel in der Anwendung. Der Nachteil ist + jedoch die sehr geringe Geschwindigkeit: Je nach verwendetem + Hardware-Debugger (z. B. ST-Link) kann jeder Schreibvorgang mehrere + Millisekunden in Anspruch nehmen. +] else { todo } + +#let ln_sh = link("https://crates.io/crates/cortex-m-semihosting")[`cortex-m-semihosting`] +#if lang == "en" [ + The #ln_sh crate provides an API to do semihosting + operations on Cortex-M devices. + The program below is the semihosting version of "Hello, world!": +] else if lang == "de" [ + Das #ln_sh;-Crate + stellt eine API für Semihosting-Operationen auf Cortex-M-Geräten bereit. + Das folgende Programm ist die Semihosting-Version von „Hello, world!": +] else { todo } + +```rust +#![no_main] +#![no_std] + +use panic_halt as _; + +use cortex_m_rt::entry; +use cortex_m_semihosting::hprintln; + +#[entry] +fn main() -> ! { + hprintln!("Hello, world!").unwrap(); + + loop {} +} +``` + +#if lang == "en" [ + If you run this program on hardware you'll see the "Hello, world!" + message within the OpenOCD logs. +] else if lang == "de" [ + Wenn Sie dieses Programm auf der Hardware ausführen, sehen Sie die + Meldung „Hello, world!" in den OpenOCD-Protokollen. +] else { todo } + +```text +$ openocd +(..) +Hello, world! +(..) +``` + +#if lang == "en" [ + You do need to enable semihosting in OpenOCD from GDB first: +] else if lang == "de" [ + Sie müssen zunächst Semihosting in OpenOCD über GDB aktivieren: +] else { todo } + +```console +(gdb) monitor arm semihosting enable +semihosting is enabled +``` + +#if lang == "en" [ + QEMU understands semihosting operations so the above program will also + work with `qemu-system-arm` without having to start a debug session. + Note that you'll need to pass the `-semihosting-config` flag to QEMU to + enable semihosting support; these flags are already included in the + `.cargo/config.toml` file of the template. +] else if lang == "de" [ + QEMU unterstützt Semihosting-Operationen, sodass das obige Programm auch + mit `qemu-system-arm` funktioniert, ohne dass eine Debug-Sitzung + gestartet werden muss. Beachten Sie, dass Sie QEMU die Option + `-semihosting-config` übergeben müssen, um die Semihosting-Unterstützung + zu aktivieren; diese Optionen sind bereits in der Datei + `.cargo/config.toml` der Vorlage enthalten. +] else { todo } + +#raw(block: true, lang: "text", +"$ # " + if lang == "en" { + "this program will block the terminal" + } else if lang == "de" { + "Dieses Programm wird das Terminal blockieren." + } else { todos } + " +$ cargo run + Running `qemu-system-arm (..) +Hello, world! +") + +#if lang == "en" [ + There's also an `exit` semihosting operation that can be used to + terminate the QEMU process. Important: do *not* use `debug::exit` + on hardware; this function can corrupt your OpenOCD session and you will + not be able to debug more programs until you restart it. +] else if lang == "de" [ + Es gibt auch eine `exit`-Semihosting-Operation, mit der sich der + QEMU-Prozess beenden lässt. Wichtig: Verwenden Sie `debug::exit` + *nicht* auf echter Hardware; diese Funktion kann Ihre + OpenOCD-Sitzung beschädigen, sodass Sie keine weiteren Programme mehr + debuggen können, bis Sie die Sitzung neu starten. +] else { todo } + +```rust +#![no_main] +#![no_std] + +use panic_halt as _; + +use cortex_m_rt::entry; +use cortex_m_semihosting::debug; + +#[entry] +fn main() -> ! { + let roses = "blue"; + + if roses == "red" { + debug::exit(debug::EXIT_SUCCESS); + } else { + debug::exit(debug::EXIT_FAILURE); + } + + loop {} +} +``` + +```text +$ cargo run + Running `qemu-system-arm (..) + +$ echo $? +1 +``` + +#if lang == "en" [ + One last tip: you can set the panicking behavior to + `exit(EXIT_FAILURE)`. This will let you write `no_std` run-pass tests + that you can run on QEMU. +] else if lang == "de" [ + Ein letzter Tipp: Du kannst das Verhalten bei einem Panic auf + `exit(EXIT_FAILURE)` einstellen. Dadurch lassen sich `no_std`-Tests + schreiben, die erfolgreich durchlaufen und unter QEMU ausgeführt werden + können. +] else { todo } + +#if lang == "en" [ + For convenience, the `panic-semihosting` crate has an "exit" feature + that when enabled invokes `exit(EXIT_FAILURE)` after logging the panic + message to the host stderr. +] else if lang == "de" [ + Praktischerweise bietet der `panic-semihosting`-Crate ein „exit"-Feature + an; ist dieses aktiviert, wird `exit(EXIT_FAILURE)` aufgerufen, nachdem + die Panic-Meldung auf dem `stderr` des Hosts ausgegeben wurde. +] else { todo } + +```rust +#![no_main] +#![no_std] + +use panic_semihosting as _; // features = ["exit"] + +use cortex_m_rt::entry; +use cortex_m_semihosting::debug; + +#[entry] +fn main() -> ! { + let roses = "blue"; + + assert_eq!(roses, "red"); + + loop {} +} +``` + +```text +$ cargo run + Running `qemu-system-arm (..) +panicked at 'assertion failed: `(left == right)` + left: `"blue"`, + right: `"red"`', examples/hello.rs:15:5 + +$ echo $? +1 +``` + +#if lang == "en" [ + *NOTE*: To enable this feature on `panic-semihosting`, edit your + `Cargo.toml` dependencies section where `panic-semihosting` is specified + with: +] else if lang == "de" [ + *HINWEIS*: Um diese Funktion für `panic-semihosting` zu + aktivieren, bearbeiten Sie den Abschnitt „dependencies" in Ihrer + `Cargo.toml`, in dem `panic-semihosting` wie folgt angegeben ist: +] else { todo } + +```toml +panic-semihosting = { version = "VERSION", features = ["exit"] } +``` + +#if lang == "en" [ + where `VERSION` is the version desired. For more information on + dependencies features check the + #link("https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html")[`specifying dependencies`] + section of the Cargo book. +] else if lang == "de" [ + wobei `VERSION` für die gewünschte Version steht. Weitere Informationen + zu Abhängigkeiten und Funktionen finden Sie im Abschnitt + "#link("https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html")[`specifying dependencies`]" + des Cargo-Buchs. +] else { todo } diff --git a/typ/static-guarantees/design-contracts.typ b/typ/static-guarantees/design-contracts.typ new file mode 100644 index 00000000..4081dc24 --- /dev/null +++ b/typ/static-guarantees/design-contracts.typ @@ -0,0 +1,416 @@ +#import "../config.typ": * + +#h1(offset: whole, + if lang == "en" [Design Contracts] + else if lang == "de" [Design-Verträge] + else { todo }) +#set heading(offset: whole*2) + +#if lang == "en" [ + In our last chapter, we wrote an interface that _didn't_ enforce + design contracts. Let's take another look at our imaginary GPIO + configuration register: +] else if lang == "de" [ + Im letzten Kapitel haben wir eine Schnittstelle geschrieben, die + _keine_ Design-Verträge (Design Contracts) durchsetzte. Werfen wir + noch einmal einen Blick auf unser fiktives GPIO-Konfigurationsregister: +] else { todo } + +#include "gpio-table.typ" + +#if lang == "en" [ + If we instead checked the state before making use of the underlying + hardware, enforcing our design contracts at runtime, we might write code + that looks like this instead: +] else if lang == "de" [ + Wenn wir stattdessen den Zustand überprüfen würden, bevor wir die + zugrundeliegende Hardware nutzen, und dabei unsere Entwurfsverträge zur + Laufzeit durchsetzen würden, könnten wir stattdessen Code schreiben, der + wie folgt aussieht: +] else { todo } + +#raw(block: true, lang: "rust", +"/// " + if lang == "en" { + "GPIO interface" + } else if lang == "de" { + "GPIO-Schnittstelle" + } else { todos } + " +struct GpioConfig { + /// " + if lang == "en" { + "GPIO Configuration structure generated by svd2rust" + } else if lang == "de" { + "Von svd2rust generierte GPIO-Konfigurationsstruktur" + } else { todos } + " + periph: GPIO_CONFIG, +} + +impl GpioConfig { + pub fn set_enable(&mut self, is_enabled: bool) { + self.periph.modify(|_r, w| { + w.enable().set_bit(is_enabled) + }); + } + + pub fn set_direction(&mut self, is_output: bool) -> Result<(), ()> { + if self.periph.read().enable().bit_is_clear() { + // " + if lang == "en" { + "Must be enabled to set direction" + } else if lang == "de" { + "Muss aktiviert sein, um die Richtung festzulegen." + } else { todos } + " + return Err(()); + } + + self.periph.modify(|r, w| { + w.direction().set_bit(is_output) + }); + + Ok(()) + } + + pub fn set_input_mode(&mut self, variant: InputMode) -> Result<(), ()> { + if self.periph.read().enable().bit_is_clear() { + // " + if lang == "en" { + "Must be enabled to set input mode" + } else if lang == "de" { + "Muss aktiviert sein, um den Eingabemodus festzulegen." + } else { todos } + " + return Err(()); + } + + if self.periph.read().direction().bit_is_set() { + // " + if lang == "en" { + "Direction must be input" + } else if lang == "de" { + "Die Richtung muss input sein." + } else { todos } + " + return Err(()); + } + + self.periph.modify(|_r, w| { + w.input_mode().variant(variant) + }); + + Ok(()) + } + + pub fn set_output_status(&mut self, is_high: bool) -> Result<(), ()> { + if self.periph.read().enable().bit_is_clear() { + // " + if lang == "en" { + "Must be enabled to set output status" + } else if lang == "de" { + "Muss aktiviert sein, um den Ausgangsstatus festzulegen." + } else { todos } + " + return Err(()); + } + + if self.periph.read().direction().bit_is_clear() { + // " + if lang == "en" { + "Direction must be output" + } else if lang == "de" { + "Die Richtung muss output sein." + } else { todos } + " + return Err(()); + } + + self.periph.modify(|_r, w| { + w.output_mode.set_bit(is_high) + }); + + Ok(()) + } + + pub fn get_input_status(&self) -> Result { + if self.periph.read().enable().bit_is_clear() { + // " + if lang == "en" { + "Must be enabled to get status" + } else if lang == "de" { + "Muss aktiviert sein, um den Status abzurufen." + } else { todos } + " + return Err(()); + } + + if self.periph.read().direction().bit_is_set() { + // " + if lang == "en" { + "Direction must be input" + } else if lang == "de" { + "Die Richtung muss input sein." + } else { todos } + " + return Err(()); + } + + Ok(self.periph.read().input_status().bit_is_set()) + } +} +") + +#if lang == "en" [ + Because we need to enforce the restrictions on the hardware, we end up + doing a lot of runtime checking which wastes time and resources, and + this code will be much less pleasant for the developer to use. +] else if lang == "de" [ + Da wir die Hardware-Beschränkungen durchsetzen müssen, führen wir + letztlich zahlreiche Laufzeitüberprüfungen durch -- was Zeit und + Ressourcen verschwendet --, und der Code ist für Entwickler deutlich + weniger angenehm zu handhaben. +] else { todo } + += #(if lang == "en" [Type States] + else if lang == "de" [Typzustände] + else { todo }) + +#if lang == "en" [ + But what if instead, we used Rust's type system to enforce the state + transition rules? Take this example: +] else if lang == "de" [ + Was aber, wenn wir stattdessen das Typsystem von Rust nutzen würden, um + die Regeln für Zustandsübergänge durchzusetzen? Betrachten wir dieses + Beispiel: +] else { todo } + +#raw(block: true, lang: "rust", +"/// " + if lang == "en" { + "GPIO interface" + } else if lang == "de" { + "GPIO-Schnittstelle" + } else { todos } + " +struct GpioConfig { + /// " + if lang == "en" { + "GPIO Configuration structure generated by svd2rust" + } else if lang == "de" { + "Von svd2rust generierte GPIO-Konfigurationsstruktur" + } else { todos } + " + periph: GPIO_CONFIG, + enabled: ENABLED, + direction: DIRECTION, + mode: MODE, +} + +// " + if lang == "en" { + "Type states for MODE in GpioConfig" + } else if lang == "de" { + "Typzustaende fuer MODE in GpioConfig" + } else { todos } + " +struct Disabled; +struct Enabled; +struct Output; +struct Input; +struct PulledLow; +struct PulledHigh; +struct HighZ; +struct DontCare; + +/// " + if lang == "en" { + "These functions may be used on any GPIO Pin" + } else if lang == "de" { + "Diese Funktionen koennen an jedem GPIO-Pin verwendet werden." + } else { todos } + " +impl GpioConfig { + pub fn into_disabled(self) -> GpioConfig { + self.periph.modify(|_r, w| w.enable.disabled()); + GpioConfig { + periph: self.periph, + enabled: Disabled, + direction: DontCare, + mode: DontCare, + } + } + + pub fn into_enabled_input(self) -> GpioConfig { + self.periph.modify(|_r, w| { + w.enable.enabled() + .direction.input() + .input_mode.high_z() + }); + GpioConfig { + periph: self.periph, + enabled: Enabled, + direction: Input, + mode: HighZ, + } + } + + pub fn into_enabled_output(self) -> GpioConfig { + self.periph.modify(|_r, w| { + w.enable.enabled() + .direction.output() + .input_mode.set_high() + }); + GpioConfig { + periph: self.periph, + enabled: Enabled, + direction: Output, + mode: DontCare, + } + } +} + +/// " + if lang == "en" { + "This function may be used on an Output Pin" + } else if lang == "de" { + "Diese Funktion kann fuer einen Ausgangspin verwendet werden." + } else { todos } + " +impl GpioConfig { + pub fn set_bit(&mut self, set_high: bool) { + self.periph.modify(|_r, w| w.output_mode.set_bit(set_high)); + } +} + +/// " + if lang == "en" { + "These methods may be used on any enabled input GPIO" + } else if lang == "de" { + "Diese Methoden koennen auf jedem aktivierten Eingangs-GPIO verwendet werden." + } else { todos } + " +impl GpioConfig { + pub fn bit_is_set(&self) -> bool { + self.periph.read().input_status.bit_is_set() + } + + pub fn into_input_high_z(self) -> GpioConfig { + self.periph.modify(|_r, w| w.input_mode().high_z()); + GpioConfig { + periph: self.periph, + enabled: Enabled, + direction: Input, + mode: HighZ, + } + } + + pub fn into_input_pull_down(self) -> GpioConfig { + self.periph.modify(|_r, w| w.input_mode().pull_low()); + GpioConfig { + periph: self.periph, + enabled: Enabled, + direction: Input, + mode: PulledLow, + } + } + + pub fn into_input_pull_up(self) -> GpioConfig { + self.periph.modify(|_r, w| w.input_mode().pull_high()); + GpioConfig { + periph: self.periph, + enabled: Enabled, + direction: Input, + mode: PulledHigh, + } + } +} +") + +#if lang == "en" [ + Now let's see what the code using this would look like: +] else if lang == "de" [ + Schauen wir uns nun an, wie der Code aussehen würde, der dies verwendet: +] else { todo } + +#raw(block: true, lang: "rust", +"/* + * " + if lang == "en" { + "Example 1: Unconfigured to High-Z input" + } else if lang == "de" { + "Beispiel 1: Nicht konfiguriert als High-Z-Eingang" + } else { todos } + " + */ +let pin: GpioConfig = get_gpio(); + +// " + if lang == "en" { + "Can't do this, pin isn't enabled!" + } else if lang == "de" { + "Das ist nicht moeglich, die PIN ist nicht aktiviert!" + } else { todos } + " +// pin.into_input_pull_down(); + +// " + if lang == "en" { + "Now turn the pin from unconfigured to a high-z input" + } else if lang == "de" { + "Schalten Sie den Pin nun von „unkonfiguriert“ auf einen hochohmigen Eingang + // (High-Z-Eingang) um." + } else { todos } + " +let input_pin = pin.into_enabled_input(); + +// " + if lang == "en" { + "Read from the pin" + } else if lang == "de" { + "Vom Pin lesen" + } else { todos } + " +let pin_state = input_pin.bit_is_set(); + +// " + if lang == "en" { + "Can't do this, input pins don't have this interface!" + } else if lang == "de" { + "Das ist nicht moeglich; Eingangs-Pins verfuegen nicht über diese + // Schnittstelle!" + } else { todos } + " +// input_pin.set_bit(true); + +/* + * " + if lang == "en" { + "Example 2: High-Z input to Pulled Low input" + } else if lang == "de" { + "Beispiel 2: Hochohmiger Eingang (High-Z) an auf Low-Pegel gezogenen Eingang" + } else { todos } + " + */ +let pulled_low = input_pin.into_input_pull_down(); +let pin_state = pulled_low.bit_is_set(); + +/* + * " + if lang == "en" { + "Example 3: Pulled Low input to Output, set high" + } else if lang == "de" { + "Beispiel 3: Eingang auf Low gezogen, Ausgang auf High gesetzt" + } else { todos } + " + */ +let output_pin = pulled_low.into_enabled_output(); +output_pin.set_bit(true); + +// " + if lang == "en" { + "Can't do this, output pins don't have this interface!" + } else if lang == "de" { + "Das geht nicht, Ausgangspins verfuegen nicht über diese Schnittstelle!" + } else { todos } + " +// output_pin.into_input_pull_down(); +") + +#if lang == "en" [ + This is definitely a convenient way to store the state of the pin, but + why do it this way? Why is this better than storing the state as an + `enum` inside of our `GpioConfig` structure? +] else if lang == "de" [ + Dies ist zweifellos eine praktische Möglichkeit, den Zustand des Pins zu + speichern, aber warum sollte man es auf diese Weise tun? Warum ist das + besser, als den Zustand als `enum` innerhalb unserer + `GpioConfig`-Struktur zu speichern? +] else { todo } + += #(if lang == "en" [Compile Time Functional Safety] + else if lang == "de" [Funktionale Sicherheit zur Kompilierzeit] + else { todo }) + +#if lang == "en" [ + Because we are enforcing our design constraints entirely at compile + time, this incurs no runtime cost. It is impossible to set an output + mode when you have a pin in an input mode. Instead, you must walk + through the states by converting it to an output pin, and then setting + the output mode. Because of this, there is no runtime penalty due to + checking the current state before executing a function. +] else if lang == "de" [ + Da wir unsere Designvorgaben vollständig zur Kompilierzeit durchsetzen, + entstehen keinerlei Laufzeitkosten. Es ist unmöglich, einen Ausgabemodus + einzustellen, solange sich ein Pin im Eingabemodus befindet. Stattdessen + muss man die Zustände durchlaufen, indem man den Pin zunächst in einen + Ausgangspin umwandelt und anschließend den Ausgabemodus festlegt. + Dadurch entfällt der Laufzeitaufwand für die Überprüfung des aktuellen + Zustands vor der Ausführung einer Funktion. +] else { todo } + +#if lang == "en" [ + Also, because these states are enforced by the type system, there is no + longer room for errors by consumers of this interface. If they try to + perform an illegal state transition, the code will not compile! +] else if lang == "de" [ + Da diese Zustände zudem durch das Typsystem erzwungen werden, sind + Fehler bei der Verwendung dieser Schnittstelle ausgeschlossen. Versucht + der Nutzer einen unzulässigen Zustandsübergang, lässt sich der Code + nicht kompilieren! +] else { todo } diff --git a/typ/static-guarantees/gpio-table.typ b/typ/static-guarantees/gpio-table.typ new file mode 100644 index 00000000..01311d0c --- /dev/null +++ b/typ/static-guarantees/gpio-table.typ @@ -0,0 +1,89 @@ +#import "../config.typ": * + +#figure( + kind: table, + table( + columns: (auto, auto, auto, auto, 1fr), + align: (x, y) => { + if x == 4 and y > 0 { + left + horizon + } else { + center + horizon + } + }, + table.header( + if lang in ("en", "de") [Name] + else { todo }, + if lang == "en" [Bit Number(s)] + else if lang == "de" [Bit-Nummer] + else { todo }, + if lang == "en" [Value] + else if lang == "de" [Wert] + else { todo }, + if lang == "en" [Meaning] + else if lang == "de" [Bedeutung] + else { todo }, + if lang == "en" [Notes] + else if lang == "de" [Hinweise] + else { todo }, + ), + table.cell(rowspan: 2)[enable], table.cell(rowspan: 2)[0], + [0], [disabled], + if lang == "en" [Disables the GPIO] + else if lang == "de" [Deaktiviert den GPIO] + else { todo }, + + [1], [enabled], + if lang == "en" [Enables the GPIO] + else if lang == "de" [Aktiviert den GPIO] + else { todo }, + + table.cell(rowspan: 2)[direction], table.cell(rowspan: 2)[1], + [0], [input], + if lang == "en" [Sets the direction to Input] + else if lang == "de" [Legt die Richtung auf „Eingang" fest.] + else { todo }, + + [1], [output], + if lang == "en" [Sets the direction to Output] + else if lang == "de" [Legt die Richtung auf „Ausgang" fest.] + else { todo }, + + table.cell(rowspan: 4)[input_mode], table.cell(rowspan: 4)[2..3], + [00], [hi-z], + if lang == "en" [Sets the input as high resistance] + else if lang == "de" [Setzt den Eingang auf hochohmig.] + else { todo }, + + [01], [pull-low], + if lang == "en" [Input pin is pulled low] + else if lang == "de" [Der Eingangspin wird auf Low-Pegel gezogen.] + else { todo }, + + [10], [pull-high], + if lang == "en" [Input pin is pulled high] + else if lang == "de" [Der Eingangspin wird auf High-Pegel gezogen] + else { todo }, + + [11], [n/a], + if lang == "en" [Invalid state. Do not set] + else if lang == "de" [Ungültiger Zustand. Nicht setzen.] + else { todo }, + + table.cell(rowspan: 2)[output_mode], table.cell(rowspan: 2)[4], + [0], [set-low], + if lang == "en" [Output pin is driven low] + else if lang == "de" [Der Ausgangspin wird auf Low-Pegel gesteuert.] + else { todo }, + + [1], [set-high], + if lang == "en" [Output pin is driven high] + else if lang == "de" [Der Ausgangspin wird auf High-Pegel gesteuert.] + else { todo }, + + [input_status], [5], [x], [in-val], + if lang == "en" [0 if input is < 1.5v, 1 if input >= 1.5v] + else if lang == "de" [0, wenn der Eingang < 1,5 V ist; 1, wenn der Eingang ≥ 1,5 V ist.] + else { todo }, + ) +) \ No newline at end of file diff --git a/typ/static-guarantees/index.typ b/typ/static-guarantees/index.typ new file mode 100644 index 00000000..972ce8a6 --- /dev/null +++ b/typ/static-guarantees/index.typ @@ -0,0 +1,65 @@ +#import "../config.typ": * + +#h1(if lang == "en" [Static Guarantees] + else if lang == "de" [Statische Garantien] + else { todo }) +#set heading(offset: whole) + +#let ln_send = link("https://doc.rust-lang.org/core/marker/trait.Send.html")[`Send`] +#let ln_sync = link("https://doc.rust-lang.org/core/marker/trait.Sync.html")[`Sync`] +#if lang == "en" [ + Rust's type system prevents data races at compile time + (see #ln_send and #ln_sync traits). + The type system can also be used to check other properties at + compile time; reducing the need for runtime checks in some cases. +] else if lang == "de" [ + Das Typsystem von Rust verhindert Data Races zur Kompilierzeit + (siehe die Traits #ln_send und #ln_sync). + Zudem lässt sich das Typsystem nutzen, um weitere Eigenschaften bereits + zur Kompilierzeit zu überprüfen, wodurch in einigen Fällen + Laufzeitprüfungen überflüssig werden. +] else { todo } + +#if lang == "en" [ + When applied to embedded programs these _static checks_ can be + used, for example, to enforce that configuration of I/O interfaces is + done properly. For instance, one can design an API where it is only + possible to initialize a serial interface by first configuring the pins + that will be used by the interface. +] else if lang == "de" [ + Bei Embedded-Programmen lassen sich diese _statischen Prüfungen_ + beispielsweise nutzen, um sicherzustellen, dass die Konfiguration von + E/A-Schnittstellen korrekt erfolgt. So lässt sich etwa eine API + entwerfen, bei der die Initialisierung einer seriellen Schnittstelle nur + möglich ist, wenn zuvor die für diese Schnittstelle vorgesehenen Pins + konfiguriert wurden. +] else { todo } + +#if lang == "en" [ + One can also statically check that operations, like setting a pin low, + can only be performed on correctly configured peripherals. For example, + trying to change the output state of a pin configured in floating input + mode would raise a compile error. +] else if lang == "de" [ + Zudem lässt sich statisch überprüfen, ob Operationen -- wie etwa das + Setzen eines Pins auf den Pegel „Low" -- nur an korrekt konfigurierten + Peripheriekomponenten ausgeführt werden. Der Versuch, den + Ausgangszustand eines Pins zu ändern, der als „Floating Input" (Eingang + mit undefiniertem Pegel) konfiguriert ist, würde beispielsweise zu einem + Kompilierfehler führen. +] else { todo } + +#if lang == "en" [ + And, as seen in the previous chapter, the concept of ownership can be + applied to peripherals to ensure that only certain parts of a program + can modify a peripheral. This _access control_ makes software + easier to reason about compared to the alternative of treating + peripherals as global mutable state. +] else if lang == "de" [ + Wie bereits im vorherigen Kapitel erläutert, lässt sich auch das Konzept + des „Ownership" (Besitzverhältnis) auf Peripheriekomponenten anwenden, + um sicherzustellen, dass nur bestimmte Programmteile diese modifizieren + können. Diese _Zugriffskontrolle_ erleichtert das Verständnis und + die Analyse der Software im Vergleich zu dem alternativen Ansatz, + Peripheriekomponenten als globalen, veränderbaren Zustand zu behandeln. +] else { todo } diff --git a/typ/static-guarantees/state-machines.typ b/typ/static-guarantees/state-machines.typ new file mode 100644 index 00000000..2eae0956 --- /dev/null +++ b/typ/static-guarantees/state-machines.typ @@ -0,0 +1,184 @@ +#import "../config.typ": * + +#h1(offset: whole, + if lang == "en" [Peripherals as State Machines] + else if lang == "de" [Peripheriegeräte als Zustandsautomaten] + else { todo }) +#set heading(offset: whole*2) + +#let url_pin = "https://en.wikipedia.org/wiki/General-purpose_input/output" +#if lang == "en" [ + The peripherals of a microcontroller can be thought of as set of state + machines. For example, the configuration of a simplified #link(url_pin)[GPIO pin] + could be represented as the following tree of states: + - Disabled + - Enabled + - Configured as Output + - Output: High + - Output: Low + - Configured as Input + - Input: High Resistance + - Input: Pulled Low + - Input: Pulled High +] else if lang == "de" [ + Die Peripherie eines Mikrocontrollers lässt sich als eine Menge von + Zustandsautomaten auffassen. So ließe sich beispielsweise die + Konfiguration eines vereinfachten + #link(url_pin)[GPIO-Pins] + als der folgende Zustandsbaum darstellen: + - Deaktiviert + - Aktiviert + - Als Ausgang konfiguriert + - Ausgang: High + - Ausgang: Low + - Als Eingang konfiguriert + - Eingang: Hoher Widerstand + - Eingang: Auf Low-Pegel gezogen + - Eingang: Auf High-Pegel gezogen +] else { todo } + +#if lang == "en" [ + If the peripheral starts in the `Disabled` mode, to move to the + `Input: High Resistance` mode, we must perform the following steps: + + Disabled + + Enabled + + Configured as Input + + Input: High Resistance +] else if lang == "de" [ + Wenn die Peripherieeinheit im Modus `Deaktiviert` startet, müssen wir + die folgenden Schritte ausführen, um in den Modus + `Eingang: Hoher Widerstand` zu ​​wechseln: + + Deaktiviert + + Aktiviert + + Als Eingang konfiguriert + + Eingang: Hoher Widerstand +] else { todo } + +#if lang == "en" [ + If we wanted to move from `Input: High Resistance` to + `Input: Pulled Low`, we must perform the following steps: + + Input: High Resistance + + Input: Pulled Low +] else if lang == "de" [ + Wenn wir von „Eingang: Hoher Widerstand" zu „Eingang: Auf Low-Pegel + gezogen" wechseln möchten, müssen wir die folgenden Schritte ausführen: + + Eingang: Hoher Widerstand 2. Eingang: Auf Low-Pegel gezogen +] else { todo } + +#if lang == "en" [ + Similarly, if we want to move a GPIO pin from configured as + `Input: Pulled Low` to `Output: High`, we must perform the following + steps: + + Input: Pulled Low + + Configured as Input + + Configured as Output + + Output: High +] else if lang == "de" [ + Wenn wir einen GPIO-Pin von der Konfiguration „Eingang: Auf Low-Pegel + gezogen" auf „Ausgang: High" umstellen wollen, müssen wir ebenfalls die + folgenden Schritte ausführen: + + Eingang: Auf Low-Pegel gezogen + + Als Eingang konfiguriert + + Als Ausgang konfiguriert + + Ausgang: High +] else { todo } + += #(if lang == "en" [Hardware Representation] + else if lang == "de" [Hardware-Darstellung] + else { todo }) + +#if lang == "en" [ + Typically the states listed above are set by writing values to given + registers mapped to a GPIO peripheral. Let's define an imaginary GPIO + Configuration Register to illustrate this: +] else if lang == "de" [ + Typischerweise werden die oben aufgeführten Zustände eingestellt, indem + Werte in bestimmte Register geschrieben werden, die einer + GPIO-Peripherieeinheit zugeordnet sind. Definieren wir zur + Veranschaulichung ein fiktives GPIO-Konfigurationsregister: +] else { todo } + +#include "gpio-table.typ" + +#if lang == "en" [ + We _could_ expose the following structure in Rust to control this GPIO: +] else if lang == "de" [ + Wir _könnten_ die folgende Struktur in Rust bereitstellen, um diesen GPIO zu steuern: +] else { todo } + +#raw(block: true, lang: "rust", +"/// " + if lang in ("en", "de") { + "GPIO interface" + } else { todos } + " +struct GpioConfig { + /// " + if lang == "en" { + "GPIO Configuration structure generated by svd2rust" + } else if lang == "de" { + "Von svd2rust generierte GPIO-Konfigurationsstruktur" + } else { todos } + " + periph: GPIO_CONFIG, +} + +impl GpioConfig { + pub fn set_enable(&mut self, is_enabled: bool) { + self.periph.modify(|_r, w| { + w.enable().set_bit(is_enabled) + }); + } + + pub fn set_direction(&mut self, is_output: bool) { + self.periph.modify(|_r, w| { + w.direction().set_bit(is_output) + }); + } + + pub fn set_input_mode(&mut self, variant: InputMode) { + self.periph.modify(|_r, w| { + w.input_mode().variant(variant) + }); + } + + pub fn set_output_mode(&mut self, is_high: bool) { + self.periph.modify(|_r, w| { + w.output_mode.set_bit(is_high) + }); + } + + pub fn get_input_status(&self) -> bool { + self.periph.read().input_status().bit_is_set() + } +} +") + +#if lang == "en" [ + However, this would allow us to modify certain registers that do not + make sense. For example, what happens if we set the `output_mode` field + when our GPIO is configured as an input? +] else if lang == "de" [ + Dies würde uns jedoch erlauben, bestimmte Register zu verändern, was + keinen Sinn ergibt. Was passiert beispielsweise, wenn wir das Feld + `output_mode` setzen, während unser GPIO als Eingang konfiguriert ist? +] else { todo } + +#if lang == "en" [ + In general, use of this structure would allow us to reach states not + defined by our state machine above: e.g.~an output that is pulled low, + or an input that is set high. For some hardware, this may not matter. On + other hardware, it could cause unexpected or undefined behavior! +] else if lang == "de" [ + Im Allgemeinen würde die Verwendung dieser Struktur es uns ermöglichen, + Zustände zu erreichen, die nicht durch unsere obige Zustandsmaschine + definiert sind: z. B. einen Ausgang, der auf LOW gesetzt wird, oder + einen Eingang, der auf HIGH gesetzt wird. Bei mancher Hardware mag dies + keine Rolle spielen. Bei anderer Hardware könnte es jedoch zu + unerwartetem oder undefiniertem Verhalten führen! +] else { todo } + +#if lang == "en" [ + Although this interface is convenient to write, it doesn't enforce the + design contracts set out by our hardware implementation. +] else if lang == "de" [ + Obwohl diese Schnittstelle bequem zu implementieren ist, erzwingt sie + nicht die in unserer Hardware-Implementierung festgelegten + Designvorgaben. +] else { todo } diff --git a/typ/static-guarantees/typestate-programming.typ b/typ/static-guarantees/typestate-programming.typ new file mode 100644 index 00000000..deaab3ad --- /dev/null +++ b/typ/static-guarantees/typestate-programming.typ @@ -0,0 +1,132 @@ +#import "../config.typ": * + +#h1(offset: whole, + if lang == "en" [Typestate Programming] + else if lang == "de" [Typgestützte Programmierung] + else { todo }) +#set heading(offset: whole*2) + +#let url_ts = "https://en.wikipedia.org/wiki/Typestate_analysis" +#let url_builders = "https://doc.rust-lang.org/1.0.0/style/ownership/builders.html" +#if lang == "en" [ + The concept of + #link(url_ts)[typestates] + describes the encoding of information about the current state of an + object into the type of that object. Although this can sound a little + arcane, if you have used the #link(url_builders)[Builder Pattern] + in Rust, you have already started using Typestate Programming! +] else if lang == "de" [ + Das Konzept der #link(url_ts)[typestates] + beschreibt die Kodierung von Informationen über den aktuellen Zustand + eines Objekts direkt in dessen Typ. Auch wenn dies zunächst etwas + abstrakt oder kompliziert klingen mag: Wenn Sie in Rust bereits das + #link(url_builders)[Builder-Muster] + verwendet haben, sind Sie schon mit der Typestate-Programmierung in + Berührung gekommen! +] else { todo } + + +```rust +pub mod foo_module { + #[derive(Debug)] + pub struct Foo { + inner: u32, + } + + pub struct FooBuilder { + a: u32, + b: u32, + } + + impl FooBuilder { + pub fn new(starter: u32) -> Self { + Self { + a: starter, + b: starter, + } + } + + pub fn double_a(self) -> Self { + Self { + a: self.a * 2, + b: self.b, + } + } + + pub fn into_foo(self) -> Foo { + Foo { + inner: self.a + self.b, + } + } + } +} + +fn main() { + let x = foo_module::FooBuilder::new(10) + .double_a() + .into_foo(); + + println!("{:#?}", x); +} +``` + +#if lang == "en" [ + In this example, there is no direct way to create a `Foo` object. We + must create a `FooBuilder`, and properly initialize it before we can + obtain the `Foo` object we want. +] else if lang == "de" [ + In diesem Beispiel gibt es keine direkte Möglichkeit, ein `Foo`-Objekt + zu erstellen. Wir müssen einen `FooBuilder` erstellen und ihn korrekt + initialisieren, bevor wir das gewünschte `Foo`-Objekt erhalten können. +] else { todo } + +#if lang == "en" [ + This minimal example encodes two states: + - `FooBuilder`, which represents an "unconfigured", or "configuration in + process" state + - `Foo`, which represents a "configured", or "ready to use" state. +] else if lang == "de" [ + Dieses Minimalbeispiel kodiert zwei Zustände: + - `FooBuilder`, das einen „unkonfigurierten" Zustand oder einen Zustand + der „laufenden Konfiguration" repräsentiert + - `Foo`, das einen „konfigurierten" oder „einsatzbereiten" Zustand + repräsentiert. +] else { todo } + += #(if lang == "en" [Strong Types] + else if lang == "de" [Starke Typen] + else { todo }) + +#let url_strong = "https://en.wikipedia.org/wiki/Strong_and_weak_typing" +#if lang == "en" [ + Because Rust has a #link(url_strong)[Strong Type System], + there is no easy way to magically create an instance of `Foo`, or to + turn a `FooBuilder` into a `Foo` without calling the `into_foo()` + method. Additionally, calling the `into_foo()` method consumes the + original `FooBuilder` structure, meaning it can not be reused without + the creation of a new instance. +] else if lang == "de" [ + Da Rust über ein + #link(url_strong)[starkes Typsystem] + verfügt, gibt es keine einfache Möglichkeit, auf magische Weise eine + Instanz von `Foo` zu erzeugen oder einen `FooBuilder` in ein `Foo` + umzuwandeln, ohne die Methode `into_foo()` aufzurufen. Zudem verbraucht + der Aufruf der Methode `into_foo()` die ursprüngliche + `FooBuilder`-Struktur; das bedeutet, dass sie nicht ohne die Erstellung + einer neuen Instanz wiederverwendet werden kann. +] else { todo } + +#if lang == "en" [ + This allows us to represent the states of our system as types, and to + include the necessary actions for state transitions into the methods + that exchange one type for another. By creating a `FooBuilder`, and + exchanging it for a `Foo` object, we have walked through the steps of a + basic state machine. +] else if lang == "de" [ + Dies ermöglicht es uns, die Zustände unseres Systems als Typen + darzustellen und die für Zustandsübergänge erforderlichen Aktionen in + jene Methoden zu integrieren, die einen Typ gegen einen anderen + austauschen. Indem wir einen `FooBuilder` erstellen und diesen gegen ein + `Foo`-Objekt austauschen, haben wir die Schritte einer einfachen + Zustandsmaschine durchlaufen. +] else { todo } diff --git a/typ/static-guarantees/zero-cost-abstractions.typ b/typ/static-guarantees/zero-cost-abstractions.typ new file mode 100644 index 00000000..c9f6c674 --- /dev/null +++ b/typ/static-guarantees/zero-cost-abstractions.typ @@ -0,0 +1,117 @@ +#import "../config.typ": * + +#h1(offset: whole, + if lang == "en" [Zero Cost Abstractions] + else if lang == "de" [Abstraktionen ohne Kosten] + else { todo }) +#set heading(offset: whole*2) + +#if lang == "en" [ + Type states are also an excellent example of Zero Cost Abstractions - + the ability to move certain behaviors to compile time execution or + analysis. These type states contain no actual data, and are instead used + as markers. Since they contain no data, they have no actual + representation in memory at runtime: +] else if lang == "de" [ + Typzustände (Type States) sind zudem ein hervorragendes Beispiel für + „Zero-Cost Abstractions" -- also die Möglichkeit, bestimmte + Verhaltensweisen in die Kompilierzeit zu verlagern (sei es zur + Ausführung oder zur Analyse). Diese Typzustände enthalten keine + eigentlichen Daten, sondern dienen als Markierungen. Da sie keine Daten + enthalten, besitzen sie zur Laufzeit keine tatsächliche Repräsentation + im Speicher: +] else { todo } + +```rust +use core::mem::size_of; + +let _ = size_of::(); // == 0 +let _ = size_of::(); // == 0 +let _ = size_of::(); // == 0 +let _ = size_of::>(); // == 0 +``` + += #(if lang == "en" [Zero Sized Types] + else if lang == "de" [Typen der Größe Null] + else { todo }) + +```rust +struct Enabled; +``` + +#if lang == "en" [ + Structures defined like this are called Zero Sized Types, as they + contain no actual data. Although these types act "real" + at compile time - you can copy them, move them, + take references to them, etc., however + the optimizer will completely strip them away. +] else if lang == "de" [ + Strukturen dieser Art werden als Null-Typen bezeichnet, da sie keine + tatsächlichen Daten enthalten. Obwohl sich diese Typen zur Kompilierzeit + „real" verhalten -- man kann sie kopieren, verschieben, Referenzen + darauf erstellen usw. --, werden sie vom Optimierer vollständig + entfernt. +] else { todo } + +#if lang == "en" [ + In this snippet of code: +] else if lang == "de" [ + In diesem Code-Ausschnitt: +] else { todo } + +```rust +pub fn into_input_high_z(self) -> GpioConfig { + self.periph.modify(|_r, w| w.input_mode().high_z()); + GpioConfig { + periph: self.periph, + enabled: Enabled, + direction: Input, + mode: HighZ, + } +} +``` + +#if lang == "en" [ + The GpioConfig we return never exists at runtime. Calling this function + will generally boil down to a single assembly instruction - storing a + constant register value to a register location. This means that the type + state interface we've developed is a zero cost abstraction - it uses no + more CPU, RAM, or code space tracking the state of `GpioConfig`, and + renders to the same machine code as a direct register access. +] else if lang == "de" [ + Das von uns zurückgegebene `GpioConfig`-Objekt existiert zur Laufzeit + nicht. Der Aufruf dieser Funktion läuft im Wesentlichen auf einen + einzigen Assembler-Befehl hinaus: das Speichern eines konstanten + Registerwerts an einer bestimmten Registeradresse. Das bedeutet, dass + die von uns entwickelte „Type-State"-Schnittstelle eine Abstraktion ohne + Laufzeitkosten (Zero-Cost-Abstraction) darstellt -- sie verbraucht weder + zusätzliche CPU- oder RAM-Ressourcen noch zusätzlichen Programmspeicher + für die Zustandsverwaltung von `GpioConfig` und führt zu demselben + Maschinencode wie ein direkter Registerzugriff. +] else { todo } + += #(if lang == "en" [Nesting] + else if lang == "de" [Verschachtelung] + else { todo }) + +#if lang == "en" [ + In general, these abstractions may be nested as deeply as you would + like. As long as all components used are zero sized types, the whole + structure will not exist at runtime. +] else if lang == "de" [ + Im Allgemeinen lassen sich diese Abstraktionen beliebig tief + verschachteln. Solange es sich bei allen verwendeten Komponenten um + Typen ohne Größe (Zero-Sized Types) handelt, existiert die gesamte + Struktur zur Laufzeit nicht. +] else { todo } + +#if lang == "en" [ + For complex or deeply nested structures, it may be tedious to define all + possible combinations of state. In these cases, macros may be used to + generate all implementations. +] else if lang == "de" [ + Bei komplexen oder tief verschachtelten Strukturen kann es mühsam sein, + alle möglichen Zustandskombinationen zu definieren. In solchen Fällen + lassen sich Makros verwenden, um sämtliche Implementierungen zu + generieren. +] else { todo } diff --git a/typ/unsorted/index.typ b/typ/unsorted/index.typ new file mode 100644 index 00000000..020ed1e0 --- /dev/null +++ b/typ/unsorted/index.typ @@ -0,0 +1,7 @@ +#import "../config.typ": * + +#h1(if lang == "en" [Unsorted topics] + else if lang == "de" [Unsortierte Themen] + else { todo }) +#set heading(offset: whole) + diff --git a/typ/unsorted/math.typ b/typ/unsorted/math.typ new file mode 100644 index 00000000..99900b25 --- /dev/null +++ b/typ/unsorted/math.typ @@ -0,0 +1,113 @@ +#import "../config.typ": * + +#h1(offset: whole, + if lang == "en" [Performing math functionality with `#[no_std]`] + else if lang == "de" [Mathematische Funktionen mit `#[no_std]` nutzen] + else { todo }) +#set heading(offset: whole*2) + +#if lang == "en" [ + If you want to perform math related functionality like calculating the + squareroot or the exponential of a number and you have the full standard + library available, your code might look like this: +] else if lang == "de" [ + Wenn Sie mathematische Operationen durchführen möchten -- wie etwa die + Berechnung der Quadratwurzel aus der Exponentialfunktion einer Zahl -- + und Ihnen die vollständige Standardbibliothek zur Verfügung steht, + könnte Ihr Code folgendermaßen aussehen: +] else { todo } + +#raw(block: true, lang: "rust", +"//! " + if lang == "en" { + "Some mathematical functions with standard support available" + } else if lang == "de" { + "Einige mathematische Funktionen mit Standardunterstützung verfügbar" + } else { todos } + " + +fn main() { + let float: f32 = 4.82832; + let floored_float = float.floor(); + + let sqrt_of_four = floored_float.sqrt(); + + let sinus_of_four = floored_float.sin(); + + let exponential_of_four = floored_float.exp(); + println!(\"Floored test float {} to {}\", float, floored_float); + println!(\"The square root of {} is {}\", floored_float, sqrt_of_four); + println!(\"The sinus of four is {}\", sinus_of_four); + println!( + \"The exponential of four to the base e is {}\", + exponential_of_four + ) +} +") + +#let ln_libm = link("https://crates.io/crates/libm")[`libm`] +#if lang == "en" [ + Without standard library support, these functions are not available. An + external crate like #ln_libm can + be used instead. The example code would then look like this: +] else if lang == "de" [ + Ohne Unterstützung durch die Standardbibliothek stehen diese Funktionen + nicht zur Verfügung. Stattdessen kann ein externes Crate wie + #link("https://crates.io/crates/libm")[`libm`] verwendet werden. Der + Beispielcode sähe dann folgendermaßen aus: +] else { todo } + +#raw(block: true, lang: "rust", +"#![no_main] +#![no_std] + +use panic_halt as _; + +use cortex_m_rt::entry; +use cortex_m_semihosting::{debug, hprintln}; +use libm::{exp, floorf, sin, sqrtf}; + +#[entry] +fn main() -> ! { + let float = 4.82832; + let floored_float = floorf(float); + + let sqrt_of_four = sqrtf(floored_float); + + let sinus_of_four = sin(floored_float.into()); + + let exponential_of_four = exp(floored_float.into()); + hprintln!(\"Floored test float {} to {}\", float, floored_float).unwrap(); + hprintln!(\"The square root of {} is {}\", floored_float, sqrt_of_four).unwrap(); + hprintln!(\"The sinus of four is {}\", sinus_of_four).unwrap(); + hprintln!( + \"The exponential of four to the base e is {}\", + exponential_of_four + ) + .unwrap(); + // " + if lang == "en" { + "exit QEMU + // NOTE do not run this on hardware; it can corrupt OpenOCD state" + } else if lang == "de" { + "exit QEMU + // HINWEIS: Fuehren Sie dies nicht auf der Hardware aus; es kann den + // OpenOCD-Zustand beschaedigen." + } else { todos } + " + // debug::exit(debug::EXIT_SUCCESS); + + loop {} +} +") + +#if lang == "en" [ + If you need to perform more complex operations like DSP signal + processing or advanced linear algebra on your MCU, the following crates + might help you +] else if lang == "de" [ + Wenn Sie komplexere Operationen wie DSP-Signalverarbeitung oder + fortgeschrittene lineare Algebra auf Ihrem Mikrocontroller durchführen + müssen, könnten Ihnen die folgenden Crates weiterhelfen. +] else { todo } +- #link("https://github.com/jacobrosenthal/cmsis-dsp-sys")[CMSIS DSP library binding] +- #link("https://crates.io/crates/constgebra")[`constgebra`] +- #link("https://github.com/tarcieri/micromath")[`micromath`] +- #link("https://crates.io/crates/microfft")[`microfft`] +- #link("https://github.com/dimforge/nalgebra")[`nalgebra`] diff --git a/typ/unsorted/speed-vs-size.typ b/typ/unsorted/speed-vs-size.typ new file mode 100644 index 00000000..a0fc7801 --- /dev/null +++ b/typ/unsorted/speed-vs-size.typ @@ -0,0 +1,381 @@ +#import "../config.typ": * + +#h1(offset: whole, + if lang == "en" [Optimizations: the speed size tradeoff] + else if lang == "de" [Optimierungen: Der Kompromiss zwischen Geschwindigkeit und Größe] + else { todo }) +#set heading(offset: whole*2) + +#if lang == "en" [ + Everyone wants their program to be super fast and super small but it's + usually not possible to have both characteristics. This section + discusses the different optimization levels that `rustc` provides and + how they affect the execution time and binary size of a program. +] else if lang == "de" [ + Jeder möchte, dass sein Programm extrem schnell und extrem klein ist, + doch meist lassen sich nicht beide Eigenschaften zugleich verwirklichen. + Dieser Abschnitt behandelt die verschiedenen Optimierungsstufen von + `rustc` und deren Auswirkungen auf die Ausführungszeit sowie die Größe + der Binärdatei eines Programms. +] else { todo } + += #(if lang == "en" [No optimizations] + else if lang == "de" [Keine Optimierungen] + else { todo }) + +#if lang == "en" [ + This is the default. When you call `cargo build` you use the development + (AKA `dev`) profile. This profile is optimized for debugging so it + enables debug information and does _not_ enable any optimizations, + i.e.~it uses `-C opt-level = 0`. +] else if lang == "de" [ + Dies ist die Standardeinstellung. Wenn Sie `cargo build` aufrufen, wird + das Entwicklungs-Profil (auch bekannt als `dev`-Profil) verwendet. Da + dieses Profil für das Debugging optimiert ist, werden + Debug-Informationen aktiviert, jedoch _keinerlei_ Optimierungen + vorgenommen; es wird also `-C opt-level=0` verwendet. +] else { todo } + +#if lang == "en" [ + At least for bare metal development, debuginfo is zero cost in the sense + that it won't occupy space in Flash / ROM so we actually recommend that + you enable debuginfo in the release profile -- it is disabled by + default. That will let you use breakpoints when debugging release builds. +] else if lang == "de" [ + Zumindest bei der Bare-Metal-Entwicklung verursachen Debug-Informationen + keine Kosten im Sinne von Speicherplatzbedarf im Flash- oder + ROM-Speicher. Daher empfehlen wir, Debug-Informationen auch im + Release-Profil zu aktivieren -- standardmäßig sind sie dort deaktiviert. + Dies ermöglicht Ihnen die Verwendung von Breakpoints beim Debuggen von + Release-Builds. +] else { todo } + +#raw(block: true, lang: "toml", +"[profile.release] +# " + if lang == "en" { + "symbols are nice and they don't increase the size on Flash" + } else if lang == "de" { + "Symbole sind praktisch und vergroessern die Dateigroesse im Flash nicht" + } else { todos } + " +debug = true +") + +#if lang == "en" [ + No optimizations is great for debugging because stepping through the + code feels like you are executing the program statement by statement, + plus you can `print` stack variables and function arguments in GDB. When + the code is optimized, trying to print variables results in + `$0 = ` being printed. +] else if lang == "de" [ + Der Verzicht auf Optimierungen ist ideal für das Debugging, da sich das + schrittweise Durchlaufen des Codes anfühlt, als würde man das Programm + Anweisung für Anweisung ausführen; zudem lassen sich Stack-Variablen und + Funktionsargumente in GDB per `print` ausgeben. Bei optimiertem Code + führt der Versuch, Variablen auszugeben, hingegen zur Meldung + `$0 = `. +] else { todo } + +#if lang == "en" [ + The biggest downside of the `dev` profile is that the resulting binary + will be huge and slow. The size is usually more of a problem because + unoptimized binaries can occupy dozens of KiB of Flash, which your + target device may not have -- the result: your unoptimized binary + doesn't fit in your device! +] else if lang == "de" [ + Der größte Nachteil des `dev`-Profils besteht darin, dass die + resultierende Binärdatei sehr groß und langsam ist. Meist wiegt vor + allem die Größe schwer, da nicht optimierte Binärdateien Dutzende KiB an + Flash-Speicher belegen können -- Speicherplatz, über den das Zielgerät + womöglich gar nicht verfügt. Die Folge: Die nicht optimierte Binärdatei + passt nicht auf das Gerät! +] else { todo } + +#if lang == "en" [ + Can we have smaller, debugger friendly binaries? Yes, there's a trick. +] else if lang == "de" [ + Gibt es eine Möglichkeit, kleinere und dennoch für das Debugging + geeignete Binärdateien zu erhalten? Ja, es gibt einen Trick. +] else { todo } + +== #(if lang == "en" [Optimizing dependencies] + else if lang == "de" [Optimierung der Abhängigkeiten] + else { todo }) + +#let url_overrides = "https://doc.rust-lang.org/cargo/reference/profiles.html#overrides" +#if lang == "en" [ + There's a Cargo feature named #link(url_overrides)[`profile-overrides`] + that lets you override the optimization level of dependencies. You can + use that feature to optimize all dependencies for size while keeping the + top crate unoptimized and debugger friendly. +] else if lang == "de" [ + Cargo bietet mit #link(url_overrides)[`Profil-Überschreibung`] + eine Funktion, mit der Sie den Optimierungsgrad von Abhängigkeiten + überschreiben können. So lassen sich alle Abhängigkeiten hinsichtlich + ihrer Größe optimieren, während die oberste Crate unoptimiert und + debuggfreundlich bleibt. +] else { todo } + +#if lang == "en" [ + Beware that generic code can sometimes be optimized alongside the crate + where it is instantiated, rather than the crate where it is defined. If + you create an instance of a generic struct in your application and find + that it pulls in code with a large footprint, it may be that increasing + the optimisation level of the relevant dependencies has no effect. +] else if lang == "de" [ + Beachten Sie, dass generischer Code manchmal zusammen mit der Crate + optimiert werden kann, in der er instanziiert wird, anstatt mit der + Crate, in der er definiert ist. Wenn Sie in Ihrer Anwendung eine Instanz + einer generischen Struktur erstellen und feststellen, dass diese Code + mit großem Speicherbedarf einbindet, kann es sein, dass eine Erhöhung + des Optimierungsgrads der relevanten Abhängigkeiten keine Wirkung zeigt. +] else { todo } + +#if lang == "en" [ + Here's an example: +] else if lang == "de" [ + Hier ist ein Beispiel: +] else { todo } + +```toml +# Cargo.toml +[package] +name = "app" +# .. + +[profile.dev.package."*"] # + +opt-level = "z" # + +``` + +#if lang == "en" [ + Without the override: +] else if lang == "de" [ + Ohne die Überschreibung: +] else { todo } + +```text +$ cargo size --bin app -- -A +app : +section size addr +.vector_table 1024 0x8000000 +.text 9060 0x8000400 +.rodata 1708 0x8002780 +.data 0 0x20000000 +.bss 4 0x20000000 +``` + +#if lang == "en" [ + With the override: +] else if lang == "de" [ + Mit der Überschreibung +] else { todo } + +```text +$ cargo size --bin app -- -A +app : +section size addr +.vector_table 1024 0x8000000 +.text 3490 0x8000400 +.rodata 1100 0x80011c0 +.data 0 0x20000000 +.bss 4 0x20000000 +``` + +#if lang == "en" [ + That's a 6 KiB reduction in Flash usage without any loss in the + debuggability of the top crate. If you step into a dependency then + you'll start seeing those `` messages again but + it's usually the case that you want to debug the top crate and not the + dependencies. And if you _do_ need to debug a dependency then you + can use the `profile-overrides` feature to exclude a particular + dependency from being optimized. See example below: +] else if lang == "de" [ + Das bedeutet eine Verringerung des Flash-Speicherverbrauchs um 6 KiB, + ohne die Debug-Fähigkeit des Haupt-Crates zu beeinträchtigen. Wenn man + in eine Abhängigkeit hineinspringt, erscheinen zwar wieder die Meldungen + ``, doch in der Regel möchte man das Haupt-Crate + debuggen und nicht die Abhängigkeiten. Sollte es dennoch erforderlich + sein, eine Abhängigkeit zu debuggen, lässt sich die Funktion + `profile-overrides` nutzen, um diese spezifische Abhängigkeit von der + Optimierung auszunehmen. Siehe dazu das folgende Beispiel: +] else { todo } + +#raw(block: true, lang: "toml", +"# .. + +# " + if lang == "en" { + "don't optimize the `cortex-m-rt` crate" + } else if lang == "de" { + "Optimiere das `cortex-m-rt`-Crate nicht" + } else { todos } + " +[profile.dev.package.cortex-m-rt] # + +opt-level = 0 # + + +# " + if lang == "en" { + "but do optimize all the other dependencies" + } else if lang == "de" { + "optimieren Sie jedoch alle anderen Abhaengigkeiten" + } else { todos } + " +[profile.dev.package.\"*\"] +codegen-units = 1 # " + if lang in ("en", "de") { + "better optimizations" + } else { todos } + " +opt-level = \"z\" +") + +#if lang == "en" [ + Now the top crate and `cortex-m-rt` are debugger friendly! +] else if lang == "de" [ + Jetzt sind das Top-Level-Crate und `cortex-m-rt` debuggerfreundlich! +] else { todo } + += #(if lang == "en" [Optimize for speed] + else if lang == "de" [Auf Geschwindigkeit optimieren] + else { todo }) + +#if lang == "en" [ + As of 2018-09-18 `rustc` supports three "optimize for speed" levels: + `opt-level = 1`, `2` and `3`. When you run `cargo build --release` you + are using the release profile which defaults to `opt-level = 3`. +] else if lang == "de" [ + Seit dem 18.09.2018 unterstützt `rustc` drei Stufen der + Geschwindigkeitsoptimierung: `opt-level = 1`, `2` und `3`. Beim + Ausführen von `cargo build --release` wird das Release-Profil verwendet, + das standardmäßig auf `opt-level = 3` eingestellt ist. +] else { todo } + +#if lang == "en" [ + Both `opt-level = 2` and `3` optimize for speed at the expense of binary + size, but level `3` does more vectorization and inlining than level `2`. + In particular, you'll see that at `opt-level` equal to or greater than + `2` LLVM will unroll loops. Loop unrolling has a rather high cost in + terms of Flash / ROM (e.g.~from 26 bytes to 194 for a zero this array + loop) but can also halve the execution time given the right conditions + (e.g.~number of iterations is big enough). +] else if lang == "de" [ + Sowohl `opt-level = 2` als auch `3` optimieren auf Geschwindigkeit + zulasten der Binärgröße; allerdings führt Stufe `3` mehr Vektorisierung + und Inlining durch als Stufe `2`. Insbesondere ist zu beobachten, dass + LLVM bei einem `opt-level` von 2 oder höher Schleifen entrollt („Loop + Unrolling"). Das Entrollen von Schleifen ist recht kostspielig im + Hinblick auf den Flash-/ROM-Speicherbedarf (z. B. Anstieg von 26 auf 194 + Bytes für eine Schleife zum Nullsetzen eines Arrays), kann aber unter + geeigneten Bedingungen (etwa bei einer ausreichend hohen Anzahl von + Iterationen) die Ausführungszeit halbieren. +] else { todo } + +#if lang == "en" [ + Currently there's no way to disable loop unrolling in `opt-level = 2` + and `3` so if you can't afford its cost you should optimize your program + for size. +] else if lang == "de" [ + Derzeit gibt es keine Möglichkeit, das Entrollen von Schleifen bei + `opt-level = 2` und `3` zu deaktivieren; wenn Sie sich diesen + Speicheraufwand also nicht leisten können, sollten Sie Ihr Programm + stattdessen auf eine geringe Größe hin optimieren. +] else { todo } + += #(if lang == "en" [Optimize for size] + else { todo }) + +#if lang == "en" [ + As of 2018-09-18 `rustc` supports two "optimize for size" levels: + `opt-level = "s"` and `"z"`. These names were inherited from clang / + LLVM and are not too descriptive but `"z"` is meant to give the idea + that it produces smaller binaries than `"s"`. +] else if lang == "de" [ + Seit dem 18.09.2018 unterstützt `rustc` zwei Stufen zur + Größenoptimierung: `opt-level = "s"` und `"z"`. Diese Bezeichnungen + wurden von Clang/LLVM übernommen und sind nicht besonders + aussagekräftig; allerdings soll das `"z"` signalisieren, dass damit + kleinere Binärdateien erzeugt werden als mit `"s"`. +] else { todo } + +#if lang == "en" [ + If you want your release binaries to be optimized for size then change + the `profile.release.opt-level` setting in `Cargo.toml` as shown below. +] else if lang == "de" [ + Wenn Ihre Release-Binärdateien hinsichtlich der Größe optimiert werden + sollen, ändern Sie die Einstellung `profile.release.opt-level` in der + Datei `Cargo.toml` wie unten dargestellt. +] else { todo } + +#raw(block: true, lang: "toml", +"[profile.release] +# " + if lang == "en" { "or \"z\"" } + else if lang == "de" { "oder \"z\"" } + else { todos } + " +opt-level = \"s\" +") + +#if lang == "en" [ + These two optimization levels greatly reduce LLVM's inline threshold, a + metric used to decide whether to inline a function or not. One of Rust + principles are zero cost abstractions; these abstractions tend to use a + lot of newtypes and small functions to hold invariants (e.g.~functions + that borrow an inner value like `deref`, `as_ref`) so a low inline + threshold can make LLVM miss optimization opportunities (e.g.~eliminate + dead branches, inline calls to closures). +] else if lang == "de" [ + Diese beiden Optimierungsstufen senken den Inline-Schwellenwert von LLVM + erheblich -- einen Kennwert, der darüber entscheidet, ob eine Funktion + „geinlined" (direkt an der Aufrufstelle eingebettet) wird oder nicht. + Eines der Prinzipien von Rust sind „Zero-Cost-Abstractions" + (Abstraktionen ohne Laufzeitkosten); diese nutzen häufig sogenannte + „Newtypes" und kleine Funktionen zur Wahrung von Invarianten (z. B. + Funktionen wie `deref` oder `as_ref`, die einen inneren Wert ausleihen). + Ein niedriger Inline-Schwellenwert kann daher dazu führen, dass LLVM + Optimierungsmöglichkeiten verpasst (etwa das Entfernen von nicht + erreichbaren Programmzweigen oder das Inlinen von Closure-Aufrufen). +] else { todo } + +#if lang == "en" [ +When optimizing for size you may want to try increasing the inline +threshold to see if that has any effect on the binary size. The +recommended way to change the inline threshold is to append the +`-C inline-threshold` flag to the other rustflags in +`.cargo/config.toml`. +] else if lang == "de" [ + Bei der Optimierung auf eine geringe Binärgröße kann es sinnvoll sein, + den Inline-Schwellenwert zu erhöhen und zu prüfen, ob sich dies auf die + Größe der Binärdatei auswirkt. Die empfohlene Methode zur Änderung + dieses Schwellenwerts besteht darin, das Flag `-C inline-threshold` zu + den übrigen `rustflags` in der Datei `.cargo/config.toml` hinzuzufügen. +] else { todo } + +#raw(block: true, lang: "toml", +"# .cargo/config.toml +# " + if lang == "en" { + "this assumes that you are using the cortex-m-quickstart template" + } else if lang == "de" { + "Dies setzt voraus, dass Sie die cortex-m-quickstart-Vorlage verwenden" + } else { todos } + " +[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))'] +rustflags = [ + # .. + \"-C\", \"inline-threshold=123\", # + +] +") + +#let url_opt_lvls = "https://github.com/rust-lang/rust/blob/1.29.0/src/librustc_codegen_llvm/back/write.rs#L2105-L2122" +#if lang == "en" [ + What value to use? + #link(url_opt_lvls)[As of 1.29.0 these are the inline thresholds that the different optimization levels use]: + - `opt-level = 3` uses 275 + - `opt-level = 2` uses 225 + - `opt-level = "s"` uses 75 + - `opt-level = "z"` uses 25 +] else if lang == "de" [ + Welchen Wert verwenden? + #link(url_opt_lvls)[Ab Version 1.29.0 gelten für die verschiedenen Optimierungsstufen folgende Inline-Schwellenwerte]: + - `opt-level = 3` verwendet 275 + - `opt-level = 2` verwendet 225 + - `opt-level = "s"` verwendet 75 + - `opt-level = "z"` verwendet 25 +] else { todo } + +#if lang == "en" [ + You should try `225` and `275` when optimizing for size. +] else if lang == "de" [ + Du solltest `225` und `275` ausprobieren, wenn du auf die Größe + optimierst. +] else { todo } From a31a73512ab8d2d66267a644e81f32ff221864fd Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Fri, 7 Aug 2026 08:48:02 +0300 Subject: [PATCH 02/11] chinese --- typ/book.typ | 88 ++++++++++++++++++------------------- typ/intro/install.typ | 46 ++++++++++++++++--- typ/intro/install/linux.typ | 37 +++++++++++++++- 3 files changed, 120 insertions(+), 51 deletions(-) diff --git a/typ/book.typ b/typ/book.typ index d1f08d16..cba464d0 100644 --- a/typ/book.typ +++ b/typ/book.typ @@ -3,7 +3,7 @@ #let sources = ( "intro/index": ( - content: (include "intro/index.typ"), + content: include "intro/index.typ", title: if lang == "en" [Introduction] else if lang == "de" [Einleitung] else if lang == "uk" [Вступ] @@ -11,18 +11,18 @@ else { todo }, sub: ( "intro/hardware": ( - content: (include "intro/hardware.typ"), + content: include "intro/hardware.typ", title: if lang in ("en", "de") [Hardware] else if lang == "uk" [Залізо] else if lang == "zh" [硬件] else { todo } ), "intro/no-std": ( - content: (include "intro/no-std.typ"), + content: include "intro/no-std.typ", title: `no_std` ), "intro/tooling": ( - content: (include "intro/tooling.typ"), + content: include "intro/tooling.typ", title: if lang == "en" [Tooling] else if lang == "de" [Werkzeuge] else if lang == "uk" [Інструменти] @@ -30,26 +30,26 @@ else { todo } ), "intro/install": ( - content: (include "intro/install.typ"), + content: include "intro/install.typ", title: if lang in ("en", "de") [Installation] else if lang == "uk" [Встановлення] else if lang == "zh" [安装] else { todo }, sub: ( "intro/install/linux": ( - content: (include "intro/install/linux.typ"), + content: include "intro/install/linux.typ", title: [Linux] ), "intro/install/macos": ( - content: (include "intro/install/macos.typ"), + content: include "intro/install/macos.typ", title: [MacOS] ), "intro/install/windows": ( - content: (include "intro/install/windows.typ"), + content: include "intro/install/windows.typ", title: [Windows] ), "intro/install/verify": ( - content: (include "intro/install/verify.typ"), + content: include "intro/install/verify.typ", title: if lang == "en" [Verify Installation] else if lang == "de" [Die Installation überprüfen] else if lang == "uk" [Перевірка встановлення] @@ -62,7 +62,7 @@ ), "start/index": ( - content: (include "start/index.typ"), + content: include "start/index.typ", title: if lang == "en" [Getting started] else if lang == "de" [Erste Schritte] else if lang == "uk" [Початок роботи] @@ -70,18 +70,18 @@ else { todo }, sub: ( "start/qemu": ( - content: (include "start/qemu.typ"), + content: include "start/qemu.typ", title: [QEMU] ), "start/hardware": ( - content: (include "start/hardware.typ"), + content: include "start/hardware.typ", title: if lang in ("en", "de") [Hardware] else if lang == "uk" [Залізо] else if lang == "zh" [硬件] else { todo } ), "start/registers": ( - content: (include "start/registers.typ"), + content: include "start/registers.typ", title: if lang == "en" [Memory-mapped Registers] else if lang == "de" [Im Speicher abgebildete Register] else if lang == "uk" [Відображені в пам'яті регістри] @@ -89,13 +89,13 @@ else { todo } ), "start/semihosting": ( - content: (include "start/semihosting.typ"), + content: include "start/semihosting.typ", title: if lang in ("en", "de", "uk") [Semihosting] else if lang == "zh" [半主机模式] else { todo } ), "start/panicking": ( - content: (include "start/panicking.typ"), + content: include "start/panicking.typ", title: if lang == "en" [Panicking] else if lang == "de" [In Panik geraten] else if lang == "uk" [Паніка] @@ -103,14 +103,14 @@ else { todo } ), "start/exceptions": ( - content: (include "start/exceptions.typ"), + content: include "start/exceptions.typ", title: if lang in ("en", "de") [Exceptions] else if lang == "uk" [Виключення] else if lang == "zh" [异常] else { todo } ), "start/interrupts": ( - content: (include "start/interrupts.typ"), + content: include "start/interrupts.typ", title: if lang in ("en", "de") [Interrupts] else if lang == "uk" [Переривання] else if lang == "zh" [中断] @@ -119,7 +119,7 @@ ) ), "peripherals/index": ( - content: (include "peripherals/index.typ"), + content: include "peripherals/index.typ", title: if lang == "en" [Peripherals] else if lang == "de" [Peripheriegeräte] else if lang == "uk" [Периферійні пристрої] @@ -127,7 +127,7 @@ else { todo }, sub: ( "peripherals/a-first-attempt": ( - content: (include "peripherals/a-first-attempt.typ"), + content: include "peripherals/a-first-attempt.typ", title: if lang == "en" [A first attempt in Rust] else if lang == "de" [Ein erster Versuch in Rust] else if lang == "uk" [Перша спроба на Rust] @@ -135,7 +135,7 @@ else { todo } ), "peripherals/borrowck": ( - content: (include "peripherals/borrowck.typ"), + content: include "peripherals/borrowck.typ", title: if lang == "en" [The Borrow Checker] else if lang == "de" [Der Borrow-Prüfer] else if lang == "uk" [Перевірка запозичень] @@ -143,7 +143,7 @@ else { todo } ), "peripherals/singletons": ( - content: (include "peripherals/singletons.typ"), + content: include "peripherals/singletons.typ", title: if lang in ("en", "de") [Singletons] else if lang == "uk" [Одинаки] else if lang == "zh" [单例] @@ -152,7 +152,7 @@ ) ), "static-guarantees/index": ( - content: (include "static-guarantees/index.typ"), + content: include "static-guarantees/index.typ", title: if lang == "en" [Static Guarantees] else if lang == "de" [Statische Garantien] else if lang == "uk" [Статичні гарантії] @@ -160,7 +160,7 @@ else { todo }, sub: ( "static-guarantees/typestate-programming": ( - content: (include "static-guarantees/typestate-programming.typ"), + content: include "static-guarantees/typestate-programming.typ", title: if lang == "en" [Typestate Programming] else if lang == "de" [Typgestützte Programmierung] else if lang == "uk" [Програмування типів-станів] @@ -168,7 +168,7 @@ else { todo } ), "static-guarantees/state-machines": ( - content: (include "static-guarantees/state-machines.typ"), + content: include "static-guarantees/state-machines.typ", title: if lang == "en" [Peripherals as State Machines] else if lang == "de" [Peripheriegeräte als Zustandsmaschinen] else if lang == "uk" [Периферія як кінцеві автомати] @@ -176,7 +176,7 @@ else { todo } ), "static-guarantees/design-contracts": ( - content: (include "static-guarantees/design-contracts.typ"), + content: include "static-guarantees/design-contracts.typ", title: if lang == "en" [Design Contracts] else if lang == "de" [Designverträge] else if lang == "uk" [Угоди щодо дизайну] @@ -184,7 +184,7 @@ else { todo } ), "static-guarantees/zero-cost-abstractions": ( - content: (include "static-guarantees/zero-cost-abstractions.typ"), + content: include "static-guarantees/zero-cost-abstractions.typ", title: if lang == "en" [Zero Cost Abstractions] else if lang == "de" [Abstraktionen ohne Kosten] else if lang == "uk" [Безкоштовні абстракції] @@ -194,7 +194,7 @@ ) ), "portability/index": ( - content: (include "portability/index.typ"), + content: include "portability/index.typ", title: if lang == "en" [Portability] else if lang == "de" [Portabilität] else if lang == "uk" [Переносимість] @@ -202,7 +202,7 @@ else { todo } ), "concurrency/index": ( - content: (include "concurrency/index.typ"), + content: include "concurrency/index.typ", title: if lang == "en" [Concurrency] else if lang == "de" [Nebenläufigkeit] else if lang == "uk" [Паралелізм] @@ -210,7 +210,7 @@ else { todo } ), "collections/index": ( - content: (include "collections/index.typ"), + content: include "collections/index.typ", title: if lang == "en" [Collections] else if lang == "de" [Sammlungen] else if lang == "uk" [Колекції] @@ -218,7 +218,7 @@ else { todo } ), "design-patterns/index": ( - content: (include "design-patterns/index.typ"), + content: include "design-patterns/index.typ", title: if lang == "en" [Design Patterns] else if lang == "de" [Desginmuster] else if lang == "uk" [Шаблони проектування] @@ -226,13 +226,13 @@ else { todo }, sub: ( "design-patterns/hal/index": ( - content: (include "design-patterns/hal/index.typ"), + content: include "design-patterns/hal/index.typ", title: if lang in ("en", "zh", "de") [HALs] else if lang == "uk" [HALи] else { todo }, sub: ( "design-patterns/hal/checklist": ( - content: (include "design-patterns/hal/checklist.typ"), + content: include "design-patterns/hal/checklist.typ", title: if lang == "en" [Checklist] else if lang == "de" [Checkliste] else if lang == "uk" [Контрольний список] @@ -240,7 +240,7 @@ else { todo } ), "design-patterns/hal/naming": ( - content: (include "design-patterns/hal/naming.typ"), + content: include "design-patterns/hal/naming.typ", title: if lang == "en" [Naming] else if lang == "de" [Benennung] else if lang == "uk" [Найменування] @@ -248,7 +248,7 @@ else { todo } ), "design-patterns/hal/interoperability": ( - content: (include "design-patterns/hal/interoperability.typ"), + content: include "design-patterns/hal/interoperability.typ", title: if lang == "en" [Interoperability] else if lang == "de" [Interoperabilität] else if lang == "uk" [Сумісність] @@ -256,7 +256,7 @@ else { todo } ), "design-patterns/hal/predictability": ( - content: (include "design-patterns/hal/predictability.typ"), + content: include "design-patterns/hal/predictability.typ", title: if lang == "en" [Predictability] else if lang == "de" [Vorhersehbarkeit] else if lang == "uk" [Передбачуваність] @@ -264,7 +264,7 @@ else { todo } ), "design-patterns/hal/gpio": ( - content: (include "design-patterns/hal/gpio.typ"), + content: include "design-patterns/hal/gpio.typ", title: [GPIO] ), ) @@ -272,7 +272,7 @@ ) ), "c-tips/index": ( - content: (include "c-tips/index.typ"), + content: include "c-tips/index.typ", title: if lang == "en" [Tips for embedded C developers] else if lang == "de" [Tipps für Entwickler im Bereich Embedded-C] else if lang == "uk" [Поради для розробників мовою C] @@ -280,7 +280,7 @@ else { todo } ), "interoperability/index": ( - content: (include "interoperability/index.typ"), + content: include "interoperability/index.typ", title: if lang == "en" [Interoperability] else if lang == "de" [Interoperabilität] else if lang == "uk" [Сумісність] @@ -288,7 +288,7 @@ else { todo }, sub: ( "interoperability/c-with-rust": ( - content: (include "interoperability/c-with-rust.typ"), + content: include "interoperability/c-with-rust.typ", title: if lang == "en" [A little C with your Rust] else if lang == "de" [Ein bisschen C zu Ihrem Rust] else if lang == "uk" [Трошки C в вашому Rust] @@ -296,7 +296,7 @@ else { todo } ), "interoperability/rust-with-c": ( - content: (include "interoperability/rust-with-c.typ"), + content: include "interoperability/rust-with-c.typ", title: if lang == "en" [A little Rust with your C] else if lang == "de" [Ein bisschen Rust zu Ihrem C] else if lang == "uk" [Трошки Rust в вашому C] @@ -306,7 +306,7 @@ ) ), "unsorted/index": ( - content: (include "unsorted/index.typ"), + content: include "unsorted/index.typ", title: if lang == "en" [Unsorted topics] else if lang == "de" [Unsortierte Themen] else if lang == "uk" [Невідсортовані теми] @@ -314,7 +314,7 @@ else { todo }, sub: ( "unsorted/speed-vs-size": ( - content: (include "unsorted/speed-vs-size.typ"), + content: include "unsorted/speed-vs-size.typ", title: if lang == "en" [Optimizations: The speed size tradeoff] else if lang == "de" [Optimierungen: Der Kompromiss zwischen Geschwindigkeit und Größe] else if lang == "uk" [Оптимізація: компроміс між швидкістю та розміром] @@ -322,7 +322,7 @@ else { todo } ), "unsorted/math": ( - content: (include "unsorted/math.typ"), + content: include "unsorted/math.typ", title: if lang == "en" [Performing Math Functionality] else if lang == "de" [Ausführung mathematischer Funktionen] else if lang == "uk" [Виконання математики] @@ -332,7 +332,7 @@ ) ), "appendix/glossary": ( - content: (include "appendix/glossary.typ"), + content: include "appendix/glossary.typ", title: if lang == "en" [Appendix A: Glossary] else if lang == "de" [Anhang A: Glossar] else if lang == "uk" [Додаток А: Глосарій] diff --git a/typ/intro/install.typ b/typ/intro/install.typ index 6a922065..13ae4097 100644 --- a/typ/intro/install.typ +++ b/typ/intro/install.typ @@ -2,15 +2,20 @@ #h1(offset: whole, if lang == "en" [Installing the tools] + else if lang == "zh" [安装工具] else { todo }) #set heading(offset: whole*2) #if lang == "en" [ This page contains OS-agnostic installation instructions for a few of the tools: +] else if lang == "zh" [ + 这一页包含的工具安装指令与操作系统无关: ] else { todo } -== Rust Toolchain +== #(if lang == "en" [Rust Toolchain] + else if lang == "zh" [Rust 工具链] + else { todo }) #let ln_rustup = link("https://rustup.rs") #if lang == "en" [ @@ -20,6 +25,9 @@ #if lang == "en" [ *NOTE* Make sure you have a compiler version equal to or newer than `1.31`. `rustc -V` should return a date newer than the one shown below. +] else if lang == "zh" [ + *注意* + 确保你的编译器版本等于或者大于`1.31`版本。`rustc -V`应该返回一个比下列日期更新的日期。 ] else { todo } ```text @@ -35,10 +43,16 @@ rustc 1.31.1 (b6c32da9b 2018-12-18) targets. For the STM32F3DISCOVERY board used for the examples in this book, use the `thumbv7em-none-eabihf` target. #link(url_find)[Find the best Cortex-M for you.] +] else if lang == "zh" [ + #todoupd("zh") + 考虑到带宽和磁盘的使用量,默认的安装只支持主机环境的编译。为了添加对ARM + Cortex-M架构交叉编译的支持,从下列编译目标中选择一个。对于这本书里使用的STM32F3DISCOVERY板子,使用`thumbv7em-none-eabihf`作为目标。 ] else { todo } #if lang == "en" [ Cortex-M0, M0+, and M1 (ARMv6-M architecture): +] else if lang == "zh" [ + Cortex-M0, M0+, 和 M1 (ARMv6-M 架构): ] else { todo } ```console @@ -47,6 +61,8 @@ rustup target add thumbv6m-none-eabi #if lang == "en" [ Cortex-M3 (ARMv7-M architecture): +] else if lang == "zh" [ + Cortex-M3 (ARMv7-M 架构): ] else { todo } ```console @@ -55,6 +71,8 @@ rustup target add thumbv7m-none-eabi #if lang == "en" [ Cortex-M4 and M7 without hardware floating point (ARMv7E-M architecture): +] else if lang == "zh" [ + 没有硬件浮点单元的Cortex-M4和M7 (ARMv7E-M架构) ] else { todo } ```console @@ -63,6 +81,8 @@ rustup target add thumbv7em-none-eabi #if lang == "en" [ Cortex-M4F and M7F with hardware floating point (ARMv7E-M architecture): +] else if lang == "zh" [ + 具有硬件浮点单元的Cortex-M4F和M7F (ARMv7E-M架构) ] else { todo } ```console @@ -71,6 +91,8 @@ rustup target add thumbv7em-none-eabihf #if lang == "en" [ Cortex-M23 (ARMv8-M architecture): +] else if lang == "zh" [ + Cortex-M23 (ARMv8-M架构): ] else { todo } ```console @@ -79,6 +101,8 @@ rustup target add thumbv8m.base-none-eabi #if lang == "en" [ Cortex-M33 and M35P (ARMv8-M architecture): +] else if lang == "zh" [ + Cortex-M33和M35P (ARMv8-M架构): ] else { todo } ```console @@ -86,7 +110,9 @@ rustup target add thumbv8m.main-none-eabi ``` #if lang == "en" [ -Cortex-M33F and M35PF with hardware floating point (ARMv8-M architecture): + Cortex-M33F and M35PF with hardware floating point (ARMv8-M architecture): +] else if lang == "zh" [ + 具有硬件浮点单元的Cortex-M33F和M35PF (ARMv8-M架构): ] else { todo } ```console @@ -103,14 +129,17 @@ rustup component add llvm-tools #let ln_vs = link("https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools&rel=16") #if lang == "en" [ - WINDOWS: prerequisite C++ Build Tools for Visual Studio 2019 is - installed. #ln_vs + WINDOWS: prerequisite C++ Build Tools for Visual Studio 2019 is installed. #ln_vs +] else if lang == "zh" [ + WINDOWS: 需要预先安装 C++ Build Tools for Visual Studio 2019。#ln_vs ] else { todo } == `cargo-generate` #if lang == "en" [ We'll use this later to generate a project from a template. +] else if lang == "zh" [ + 我们随后将使用这个来从模板生成一个项目。 ] else { todo } ```console @@ -119,15 +148,20 @@ cargo install cargo-generate #if lang == "en" [ Note: on some Linux distros (e.g.~Ubuntu) you may need to install the - packages `libssl-dev` and `pkg-config` prior to installing - cargo-generate. + packages `libssl-dev` and `pkg-config` prior to installing cargo-generate. +] else if lang == "zh" [ + 注意:在某些Linux发行版上(e.g.~Ubuntu) + 在安装cargo-generate之前,你可能需要安装`libssl-dev`和`pkg-config` ] else { todo } == #(if lang == "en" [OS-Specific Instructions] + else if lang == "zh" [特定于操作系统的指令] else { todo }) #if lang == "en" [ Now follow the instructions specific to the OS you are using: +] else if lang == "zh" [ + 现在根据你使用的操作系统,来执行对应的指令: ] else { todo } - #link("install/linux.html")[Linux] - #link("install/windows.html")[Windows] diff --git a/typ/intro/install/linux.typ b/typ/intro/install/linux.typ index 9e3e77fa..0a03bd7a 100644 --- a/typ/intro/install/linux.typ +++ b/typ/intro/install/linux.typ @@ -8,16 +8,21 @@ Here are the installation commands for a few Linux distributions. ] else if lang == "de" [ Hier sind die Installationsbefehle für einige Linux-Distributionen. +] else if lang == "zh" [ + 这部分是在某些Linux发行版下的安装指令。 ] else { todo } = #(if lang == "en" [Packages] else if lang == "de" [Pakete] + else if lang == "zh" [依赖包] else { todo }) #if lang == "en" [ - Ubuntu 18.04 or newer / Debian stretch or newer ] else if lang == "de" [ - Ubuntu 18.04 oder neuer / Debian Stretch oder neuer +] else if lang == "zh" [ + - Ubuntu 18.04 或者更新的版本 / Debian stretch 或者更新的版本 ] else { todo } #quote(block: true)[ @@ -27,6 +32,8 @@ ] else if lang == "de" [ *HINWEIS* `gdb-multiarch` ist der GDB-Befehl, den Sie zum Debuggen Ihrer ARM-Cortex-M-Programme verwenden werden. +] else if lang == "zh" [ + * 注意* `gdb-multiarch` 是你将用来调试你的ARM ] else { todo } ] @@ -34,7 +41,7 @@ sudo apt install gdb-multiarch openocd qemu-system-arm ``` -#if lang == "en" [ +#if lang in ("en", "zh") [ - Ubuntu 14.04 and 16.04 ] else if lang == "de" [ - Ubuntu 14.04 und 16.04 @@ -47,6 +54,9 @@ sudo apt install gdb-multiarch openocd qemu-system-arm ] else if lang == "de" [ *HINWEIS* `arm-none-eabi-gdb` ist der GDB-Befehl, den Sie zum Debuggen Ihrer ARM-Cortex-M-Programme verwenden werden. +] else if lang == "zh" [ + *注意* `arm-none-eabi-gdb` 是你将用来调试你的ARM + Cortex-M程序的GDB命令 ] else { todo } ] @@ -58,6 +68,8 @@ sudo apt install gdb-arm-none-eabi openocd qemu-system-arm - Fedora 27 or newer ] else if lang == "de" [ - Fedora 27 oder neuer +] else if lang == "zh" [ + - Fedora 27 或者更新的版本 ] else { todo } ```console @@ -73,6 +85,8 @@ sudo dnf install gdb openocd qemu-system-arm ] else if lang == "de" [ *HINWEIS* `arm-none-eabi-gdb` ist der GDB-Befehl, den Sie zum Debuggen von ARM-Cortex-M-Programmen verwenden. +] else if lang == "zh" [ + *注意* `arm-none-eabi-gdb` 是你将用来调试你的ARM Cortex-M程序的GDB命令 ] else { todo } ] @@ -82,12 +96,15 @@ sudo pacman -S arm-none-eabi-gdb qemu-system-arm openocd = #(if lang == "en" [udev rules] else if lang == "de" [udev-Regeln] + else if lang == "zh" [udev 规则] else { todo }) #if lang == "en" [ This rule lets you use OpenOCD with the Discovery board without root privilege. ] else if lang == "de" [ Diese Regel ermöglicht die Nutzung von OpenOCD mit dem Discovery Board ohne Root-Rechte. +] else if lang == "zh" [ + 这个规则可以让你在不使用超级用户权限的情况下,使用OpenOCD和Discovery开发板。 ] else { todo } #{ @@ -96,6 +113,8 @@ if lang == "en" [ Create the file #f with the contents shown below. ] else if lang == "de" [ Erstellen Sie die Datei #f mit dem unten stehenden Inhalt. +] else if lang == "zh" [ + 生成包含下列内容的 `/etc/udev/rules.d/70-st-link.rules` 文件 ] else { todo } } @@ -111,6 +130,8 @@ ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374b", TAG+="uaccess" Then reload all the udev rules with: ] else if lang == "de" [ Laden Sie anschließend alle udev-Regeln neu mit: +] else if lang == "zh" [ + 然后重新加载所有的udev规则 ] else { todo } ```console @@ -122,12 +143,16 @@ sudo udevadm control --reload-rules ] else if lang == "de" [ Wenn Sie die Platine an Ihren Laptop angeschlossen hatten, ziehen Sie den Stecker heraus und schließen Sie ihn dann erneut an. +] else if lang == "zh" [ + 如果你已经把开发板插入到笔记本中了,请拔下它然后再插上它。 ] else { todo } #if lang == "en" [ You can check the permissions by running this command: ] else if lang == "de" [ Sie können die Berechtigungen überprüfen, indem Sie diesen Befehl ausführen: +] else if lang == "zh" [ + 你可以通过运行这个命令检查权限: ] else { todo } ```console @@ -138,6 +163,8 @@ lsusb Which should show something like ] else if lang == "de" [ Was so etwas wie +] else if lang == "zh" [ + 终端可能有如下显示 ] else { todo } ```text @@ -156,6 +183,9 @@ Bus 001 Device 018: ID 0483:374b STMicroelectronics ST-LINK/V2.1 Notieren Sie sich die Bus- und Gerätenummern. Verwenden Sie diese Nummern, um einen Pfad wie `/dev/bus/usb//` zu bilden. Verwenden Sie diesen Pfad anschließend wie folgt: +] else if lang == "zh" [ + 记住bus和device号,使用这些数字组合成一个像是 + `/dev/bus/usb//` 这样的路径。然后像这样使用这个路径: ] else { todo } ```console @@ -183,10 +213,15 @@ user:you:rw- Das angehängte „+" bei den Berechtigungen weist auf eine erweiterte Berechtigung hin. Der Befehl „getfacl" teilt dem Benutzer mit, dass er dieses Gerät verwenden darf. +] else if lang == "zh" [ + 权限后的 `+` 指出存在一个扩展权限。`getfacl` + 命令显示,`user`也就是`你`,可以使用这个设备。 ] else { todo } #if lang == "en" [ Now, go to the #link()[next section]. ] else if lang == "de" [ Fahren Sie nun mit dem #link()[nächsten Abschnitt] fort. +] else if lang == "zh" [ + 现在,去往#link()[下个章节]. ] else { todo } From 14bd9b730abb5844fd2ad8b6a2d2a63a117e9e30 Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Fri, 7 Aug 2026 09:04:55 +0300 Subject: [PATCH 03/11] automatic set heading(offset) --- typ/appendix/glossary.typ | 1 - typ/c-tips/index.typ | 1 - typ/collections/index.typ | 1 - typ/concurrency/index.typ | 1 - typ/design-patterns/hal/checklist.typ | 1 - typ/design-patterns/hal/gpio.typ | 1 - typ/design-patterns/hal/index.typ | 1 - typ/design-patterns/hal/interoperability.typ | 1 - typ/design-patterns/hal/naming.typ | 1 - typ/design-patterns/hal/predictability.typ | 1 - typ/design-patterns/index.typ | 1 - typ/interoperability/c-with-rust.typ | 1 - typ/interoperability/index.typ | 1 - typ/interoperability/rust-with-c.typ | 1 - typ/intro/hardware.typ | 1 - typ/intro/install.typ | 1 - typ/intro/install/linux.typ | 1 - typ/intro/install/macos.typ | 1 - typ/intro/install/verify.typ | 1 - typ/intro/install/windows.typ | 1 - typ/intro/no-std.typ | 1 - typ/intro/tooling.typ | 1 - typ/mdbook/lib.typ | 7 ++++++- typ/peripherals/a-first-attempt.typ | 1 - typ/peripherals/borrowck.typ | 1 - typ/peripherals/index.typ | 1 - typ/peripherals/singletons.typ | 1 - typ/portability/index.typ | 1 - typ/start/exceptions.typ | 1 - typ/start/hardware.typ | 1 - typ/start/index.typ | 1 - typ/start/interrupts.typ | 1 - typ/start/panicking.typ | 1 - typ/start/qemu.typ | 1 - typ/start/registers.typ | 1 - typ/start/semihosting.typ | 1 - typ/static-guarantees/design-contracts.typ | 1 - typ/static-guarantees/index.typ | 1 - typ/static-guarantees/state-machines.typ | 1 - typ/static-guarantees/typestate-programming.typ | 1 - typ/static-guarantees/zero-cost-abstractions.typ | 1 - typ/unsorted/index.typ | 1 - typ/unsorted/math.typ | 1 - typ/unsorted/speed-vs-size.typ | 1 - 44 files changed, 6 insertions(+), 44 deletions(-) diff --git a/typ/appendix/glossary.typ b/typ/appendix/glossary.typ index 23af5d34..88ab88ec 100644 --- a/typ/appendix/glossary.typ +++ b/typ/appendix/glossary.typ @@ -3,7 +3,6 @@ #h1(if lang == "en" [Appendix A: Glossary] else if lang == "de" [Anhang A: Glossar] else { todo }) -#set heading(offset: whole) #if lang == "en" [ The embedded ecosystem is full of different protocols, hardware diff --git a/typ/c-tips/index.typ b/typ/c-tips/index.typ index 9856f74b..04588982 100644 --- a/typ/c-tips/index.typ +++ b/typ/c-tips/index.typ @@ -3,7 +3,6 @@ #h1(if lang == "en" [Tips for embedded C developers] else if lang == "de" [Tipps für Embedded-C-Entwickler] else { todo }) -#set heading(offset: whole) #if lang == "en" [ This chapter collects a variety of tips that might be useful to diff --git a/typ/collections/index.typ b/typ/collections/index.typ index acdaada7..65f419df 100644 --- a/typ/collections/index.typ +++ b/typ/collections/index.typ @@ -3,7 +3,6 @@ #h1(if lang == "en" [Collections] else if lang == "de" [Sammlungen (Collections)] else { todo }) -#set heading(offset: whole) #let ln_vec = link("https://doc.rust-lang.org/std/vec/struct.Vec.html")[`Vec`] #let ln_string = link("https://doc.rust-lang.org/std/string/struct.String.html")[`String`] diff --git a/typ/concurrency/index.typ b/typ/concurrency/index.typ index d62795d9..8e2f909d 100644 --- a/typ/concurrency/index.typ +++ b/typ/concurrency/index.typ @@ -4,7 +4,6 @@ else if lang == "de" [Nebenläufigkeit] else { todo }) -#set heading(offset: whole) #if lang == "en" [ Concurrency happens whenever different parts of your program might diff --git a/typ/design-patterns/hal/checklist.typ b/typ/design-patterns/hal/checklist.typ index 0a620f90..c2c6860e 100644 --- a/typ/design-patterns/hal/checklist.typ +++ b/typ/design-patterns/hal/checklist.typ @@ -5,7 +5,6 @@ else if lang == "de" [Checkliste HAL-Design-Muster] else { todo }) -#set heading(offset: whole*3) #if lang == "en" [ *Naming* (_crate aligns with Rust naming conventions_) diff --git a/typ/design-patterns/hal/gpio.typ b/typ/design-patterns/hal/gpio.typ index c5cc691b..cce8fc8f 100644 --- a/typ/design-patterns/hal/gpio.typ +++ b/typ/design-patterns/hal/gpio.typ @@ -5,7 +5,6 @@ else if lang == "de" [Empfehlungen für GPIO-Schnittstellen] else { todo }) -#set heading(offset: whole*3) = #(if lang == "en" [Pin types are zero-sized by default] else if lang == "de" [Pin-Typen haben standardmäßig die Größe Null] diff --git a/typ/design-patterns/hal/index.typ b/typ/design-patterns/hal/index.typ index d5408bba..b4731358 100644 --- a/typ/design-patterns/hal/index.typ +++ b/typ/design-patterns/hal/index.typ @@ -4,7 +4,6 @@ if lang == "en" [HAL Design Patterns] else if lang == "de" [HAL-Design-Muster] else { todo }) -#set heading(offset: whole*2) #let url_quide = "https://rust-lang.github.io/api-guidelines/" #if lang == "en" [ diff --git a/typ/design-patterns/hal/interoperability.typ b/typ/design-patterns/hal/interoperability.typ index 680adde0..e51b6e9c 100644 --- a/typ/design-patterns/hal/interoperability.typ +++ b/typ/design-patterns/hal/interoperability.typ @@ -5,7 +5,6 @@ else if lang == "de" [Interoperabilität] else { todo }) -#set heading(offset: whole*3) = #(if lang == "en" [Wrapper types provide a destructor method] else if lang == "de" [Wrapper-Typen stellen eine Destruktor-Methode bereit] diff --git a/typ/design-patterns/hal/naming.typ b/typ/design-patterns/hal/naming.typ index 12fb6ef9..17dafcca 100644 --- a/typ/design-patterns/hal/naming.typ +++ b/typ/design-patterns/hal/naming.typ @@ -5,7 +5,6 @@ else if lang == "de" [Benennung] else { todo }) -#set heading(offset: whole*3) = #(if lang == "en" [The crate is named appropriately] else if lang == "de" [Das Crate trägt einen passenden Namen] diff --git a/typ/design-patterns/hal/predictability.typ b/typ/design-patterns/hal/predictability.typ index e8be020c..15489f41 100644 --- a/typ/design-patterns/hal/predictability.typ +++ b/typ/design-patterns/hal/predictability.typ @@ -5,7 +5,6 @@ else if lang == "de" [Vorhersehbarkeit] else { todo }) -#set heading(offset: whole*3) = #(if lang == "en" [Constructors are used instead of extension traits] else if lang == "de" [Anstelle von Extension Traits werden Konstruktoren verwendet] diff --git a/typ/design-patterns/index.typ b/typ/design-patterns/index.typ index aa788c45..06add5d2 100644 --- a/typ/design-patterns/index.typ +++ b/typ/design-patterns/index.typ @@ -3,7 +3,6 @@ #h1(if lang == "en" [Design Patterns] else if lang == "de" [Design-Muster] else { todo }) -#set heading(offset: whole) #if lang == "en" [ This chapter aims to collect various useful design patterns for embedded Rust. diff --git a/typ/interoperability/c-with-rust.typ b/typ/interoperability/c-with-rust.typ index 5414c188..f45aaa60 100644 --- a/typ/interoperability/c-with-rust.typ +++ b/typ/interoperability/c-with-rust.typ @@ -5,7 +5,6 @@ else if lang == "de" [Ein bisschen C zu Ihrem Rust] else { todo }) -#set heading(offset: whole*2) #if lang == "en" [ Using C or C++ inside of a Rust project consists of two major parts: diff --git a/typ/interoperability/index.typ b/typ/interoperability/index.typ index ce400715..a42c2830 100644 --- a/typ/interoperability/index.typ +++ b/typ/interoperability/index.typ @@ -3,7 +3,6 @@ #h1(if lang == "en" [Interoperability] else if lang == "de" [Interoperabilität] else { todo }) -#set heading(offset: whole) #let ln_ffi = link("https://doc.rust-lang.org/std/ffi/index.html")[`std::ffi`] #if lang == "en" [ diff --git a/typ/interoperability/rust-with-c.typ b/typ/interoperability/rust-with-c.typ index 034f3a67..87ca223a 100644 --- a/typ/interoperability/rust-with-c.typ +++ b/typ/interoperability/rust-with-c.typ @@ -5,7 +5,6 @@ else if lang == "de" [Ein bisschen Rust zu Ihrem C] else { todo }) -#set heading(offset: whole*2) #if lang == "en" [ Using Rust code inside a C or C++ project mostly consists of two parts. diff --git a/typ/intro/hardware.typ b/typ/intro/hardware.typ index 5ed8e268..53ea1a42 100644 --- a/typ/intro/hardware.typ +++ b/typ/intro/hardware.typ @@ -6,7 +6,6 @@ else if lang == "uk" [Зустрічайте залізо] else if lang == "zh" [熟悉你的硬件] else { todo }) -#set heading(offset: whole*2) #if lang == "en" [ Let's get familiar with the hardware we'll be working with. diff --git a/typ/intro/install.typ b/typ/intro/install.typ index 13ae4097..4f10415d 100644 --- a/typ/intro/install.typ +++ b/typ/intro/install.typ @@ -4,7 +4,6 @@ if lang == "en" [Installing the tools] else if lang == "zh" [安装工具] else { todo }) -#set heading(offset: whole*2) #if lang == "en" [ This page contains OS-agnostic installation instructions for a few of diff --git a/typ/intro/install/linux.typ b/typ/intro/install/linux.typ index 0a03bd7a..cbf4e1a7 100644 --- a/typ/intro/install/linux.typ +++ b/typ/intro/install/linux.typ @@ -2,7 +2,6 @@ #h1(offset: whole*2, [Linux]) -#set heading(offset: whole*3) #if lang == "en" [ Here are the installation commands for a few Linux distributions. diff --git a/typ/intro/install/macos.typ b/typ/intro/install/macos.typ index c1b6f9d5..233a54dc 100644 --- a/typ/intro/install/macos.typ +++ b/typ/intro/install/macos.typ @@ -2,7 +2,6 @@ #h1(offset: whole*2, [macOS]) -#set heading(offset: whole*3) #let brew_ln = link("http://brew.sh/")[Homebrew] #let ports_ln = link("https://www.macports.org/")[MacPorts] diff --git a/typ/intro/install/verify.typ b/typ/intro/install/verify.typ index cc42604e..a89a0ddc 100644 --- a/typ/intro/install/verify.typ +++ b/typ/intro/install/verify.typ @@ -5,7 +5,6 @@ else if lang == "de" [Die Installation überprüfen] else { todo }) -#set heading(offset: whole*3) #if lang == "en" [ In this section we check that some of the required tools / drivers have diff --git a/typ/intro/install/windows.typ b/typ/intro/install/windows.typ index bf8c02dd..54ba4daf 100644 --- a/typ/intro/install/windows.typ +++ b/typ/intro/install/windows.typ @@ -2,7 +2,6 @@ #h1(offset: whole*2, [Windows]) -#set heading(offset: whole*3) = `arm-none-eabi-gdb` diff --git a/typ/intro/no-std.typ b/typ/intro/no-std.typ index 25232a52..d25f1981 100644 --- a/typ/intro/no-std.typ +++ b/typ/intro/no-std.typ @@ -5,7 +5,6 @@ else if lang == "de" [Eine `no_std`-Rust-Umgebung] else if lang == "zh" [一个 `no_std` Rust环境] else { todo }) -#set heading(offset: whole*2) #let ln_st72325 = link("https://www.st.com/resource/en/datasheet/st72325j6.pdf")[ST72325xx] #let ln_rpi = link("https://en.wikipedia.org/wiki/Raspberry_Pi#Specifications")[Modell B 3+] diff --git a/typ/intro/tooling.typ b/typ/intro/tooling.typ index 906dd862..ddc2ac60 100644 --- a/typ/intro/tooling.typ +++ b/typ/intro/tooling.typ @@ -5,7 +5,6 @@ else if lang == "de" [Werkzeuge] else if lang == "zh" [工具] else { todo }) -#set heading(offset: whole*2) #if lang == "en" [ Dealing with microcontrollers involves using several different tools as diff --git a/typ/mdbook/lib.typ b/typ/mdbook/lib.typ index ce5994df..6f5715af 100644 --- a/typ/mdbook/lib.typ +++ b/typ/mdbook/lib.typ @@ -378,12 +378,17 @@ #outline(depth: 3) #let part = "main" #for (path, source, _) in sa { + let offset = path.split("/").len() - 1 + if path.ends-with("/index") { + offset = offset - 1 + } if part == "main" and path.starts-with("appendix/") { path = "appendix" counter(heading).update(0) - set heading(numbering: "A.1.") + set heading(numbering: "A.1.", offset: offset+1) source } else { + set heading(offset: offset+1) source } } diff --git a/typ/peripherals/a-first-attempt.typ b/typ/peripherals/a-first-attempt.typ index 85b446a8..7f4a6906 100644 --- a/typ/peripherals/a-first-attempt.typ +++ b/typ/peripherals/a-first-attempt.typ @@ -4,7 +4,6 @@ if lang == "en" [A First Attempt] else if lang == "de" [Ein erster Versuch in Rust] else { todo }) -#set heading(offset: whole*2) = #(if lang == "en" [The Registers] else if lang == "de" [Die Register] diff --git a/typ/peripherals/borrowck.typ b/typ/peripherals/borrowck.typ index 7815530d..15756718 100644 --- a/typ/peripherals/borrowck.typ +++ b/typ/peripherals/borrowck.typ @@ -4,7 +4,6 @@ if lang == "en" [Mutable Global State] else if lang == "de" [Veränderbarer globaler Zustand] else { todo }) -#set heading(offset: whole*2) #if lang == "en" [ Unfortunately, hardware is basically nothing but mutable global state, diff --git a/typ/peripherals/index.typ b/typ/peripherals/index.typ index 66e7f214..4057f9ed 100644 --- a/typ/peripherals/index.typ +++ b/typ/peripherals/index.typ @@ -4,7 +4,6 @@ else if lang == "de" [Peripheriegeräte] else { todo }) -#set heading(offset: whole) = #(if lang == "en" [What are Peripherals?] else if lang == "de" [Was sind Peripheriegeräte?] diff --git a/typ/peripherals/singletons.typ b/typ/peripherals/singletons.typ index d15f2404..e4ec57ed 100644 --- a/typ/peripherals/singletons.typ +++ b/typ/peripherals/singletons.typ @@ -3,7 +3,6 @@ #h1(offset: whole, if lang in ("en", "de") [Singletons] else { todo }) -#set heading(offset: whole*2) #quote(block: true)[ #if lang == "en" [ diff --git a/typ/portability/index.typ b/typ/portability/index.typ index 852cbf4d..11cd66d6 100644 --- a/typ/portability/index.typ +++ b/typ/portability/index.typ @@ -4,7 +4,6 @@ else if lang == "de" [Portabilität] else { todo }) -#set heading(offset: whole) #if lang == "en" [ In embedded environments portability is a very important topic: Every diff --git a/typ/start/exceptions.typ b/typ/start/exceptions.typ index 89b8a472..2b8c3a46 100644 --- a/typ/start/exceptions.typ +++ b/typ/start/exceptions.typ @@ -5,7 +5,6 @@ else if lang == "de" [Ausnahmen (Exceptions)] else { todo }) -#set heading(offset: whole*2) #if lang == "en" [ Exceptions, and interrupts, are a hardware mechanism by which the diff --git a/typ/start/hardware.typ b/typ/start/hardware.typ index 14b0c483..2547cd92 100644 --- a/typ/start/hardware.typ +++ b/typ/start/hardware.typ @@ -4,7 +4,6 @@ if lang in ("en", "de") [Hardware] else { todo }) -#set heading(offset: whole*2) #if lang == "en" [ By now you should be somewhat familiar with the tooling and the diff --git a/typ/start/index.typ b/typ/start/index.typ index bb577d32..cfd813fc 100644 --- a/typ/start/index.typ +++ b/typ/start/index.typ @@ -4,7 +4,6 @@ else if lang == "de" [Erste Schritte] else { todo }) -#set heading(offset: whole) #let ln_f3 = link("http://www.st.com/en/evaluation-tools/stm32f3discovery.html")[STM32F3DISCOVERY] #if lang == "en" [ diff --git a/typ/start/interrupts.typ b/typ/start/interrupts.typ index a5c2dcff..9b1b5923 100644 --- a/typ/start/interrupts.typ +++ b/typ/start/interrupts.typ @@ -3,7 +3,6 @@ #h1(offset: whole, if lang in ("en", "de") [Interrupts] else { todo }) -#set heading(offset: whole*2) #if lang == "en" [ Interrupts differ from exceptions in a variety of ways but their diff --git a/typ/start/panicking.typ b/typ/start/panicking.typ index af11279e..641045f1 100644 --- a/typ/start/panicking.typ +++ b/typ/start/panicking.typ @@ -5,7 +5,6 @@ else if lang == "de" [In Panik geraten] else { todo }) -#set heading(offset: whole*2) #if lang == "en" [ Panicking is a core part of the Rust language. Built-in operations like diff --git a/typ/start/qemu.typ b/typ/start/qemu.typ index 024635d2..7ec8d9d8 100644 --- a/typ/start/qemu.typ +++ b/typ/start/qemu.typ @@ -3,7 +3,6 @@ #h1(offset: whole, [QEMU]) -#set heading(offset: whole*2) // TODO: check #if lang == "de" [ diff --git a/typ/start/registers.typ b/typ/start/registers.typ index 4101e526..376c3bf1 100644 --- a/typ/start/registers.typ +++ b/typ/start/registers.typ @@ -5,7 +5,6 @@ else if lang == "de" [Im Speicher abgebildete Register] else { todo }) -#set heading(offset: whole*2) #if lang == "en" [ Embedded systems can only get so far by executing normal Rust code and diff --git a/typ/start/semihosting.typ b/typ/start/semihosting.typ index dcde540e..6b5a7eae 100644 --- a/typ/start/semihosting.typ +++ b/typ/start/semihosting.typ @@ -4,7 +4,6 @@ if lang == "en" [Semihosting] else if lang == "de" [Semihosting] else { todo }) -#set heading(offset: whole*2) #if lang == "en" [ Semihosting is a mechanism that lets embedded devices do I/O on the host diff --git a/typ/static-guarantees/design-contracts.typ b/typ/static-guarantees/design-contracts.typ index 4081dc24..398edd85 100644 --- a/typ/static-guarantees/design-contracts.typ +++ b/typ/static-guarantees/design-contracts.typ @@ -4,7 +4,6 @@ if lang == "en" [Design Contracts] else if lang == "de" [Design-Verträge] else { todo }) -#set heading(offset: whole*2) #if lang == "en" [ In our last chapter, we wrote an interface that _didn't_ enforce diff --git a/typ/static-guarantees/index.typ b/typ/static-guarantees/index.typ index 972ce8a6..9e4a76d7 100644 --- a/typ/static-guarantees/index.typ +++ b/typ/static-guarantees/index.typ @@ -3,7 +3,6 @@ #h1(if lang == "en" [Static Guarantees] else if lang == "de" [Statische Garantien] else { todo }) -#set heading(offset: whole) #let ln_send = link("https://doc.rust-lang.org/core/marker/trait.Send.html")[`Send`] #let ln_sync = link("https://doc.rust-lang.org/core/marker/trait.Sync.html")[`Sync`] diff --git a/typ/static-guarantees/state-machines.typ b/typ/static-guarantees/state-machines.typ index 2eae0956..bf098935 100644 --- a/typ/static-guarantees/state-machines.typ +++ b/typ/static-guarantees/state-machines.typ @@ -4,7 +4,6 @@ if lang == "en" [Peripherals as State Machines] else if lang == "de" [Peripheriegeräte als Zustandsautomaten] else { todo }) -#set heading(offset: whole*2) #let url_pin = "https://en.wikipedia.org/wiki/General-purpose_input/output" #if lang == "en" [ diff --git a/typ/static-guarantees/typestate-programming.typ b/typ/static-guarantees/typestate-programming.typ index deaab3ad..9351f480 100644 --- a/typ/static-guarantees/typestate-programming.typ +++ b/typ/static-guarantees/typestate-programming.typ @@ -4,7 +4,6 @@ if lang == "en" [Typestate Programming] else if lang == "de" [Typgestützte Programmierung] else { todo }) -#set heading(offset: whole*2) #let url_ts = "https://en.wikipedia.org/wiki/Typestate_analysis" #let url_builders = "https://doc.rust-lang.org/1.0.0/style/ownership/builders.html" diff --git a/typ/static-guarantees/zero-cost-abstractions.typ b/typ/static-guarantees/zero-cost-abstractions.typ index c9f6c674..ae7218a0 100644 --- a/typ/static-guarantees/zero-cost-abstractions.typ +++ b/typ/static-guarantees/zero-cost-abstractions.typ @@ -4,7 +4,6 @@ if lang == "en" [Zero Cost Abstractions] else if lang == "de" [Abstraktionen ohne Kosten] else { todo }) -#set heading(offset: whole*2) #if lang == "en" [ Type states are also an excellent example of Zero Cost Abstractions - diff --git a/typ/unsorted/index.typ b/typ/unsorted/index.typ index 020ed1e0..3cd6e935 100644 --- a/typ/unsorted/index.typ +++ b/typ/unsorted/index.typ @@ -3,5 +3,4 @@ #h1(if lang == "en" [Unsorted topics] else if lang == "de" [Unsortierte Themen] else { todo }) -#set heading(offset: whole) diff --git a/typ/unsorted/math.typ b/typ/unsorted/math.typ index 99900b25..c585f680 100644 --- a/typ/unsorted/math.typ +++ b/typ/unsorted/math.typ @@ -4,7 +4,6 @@ if lang == "en" [Performing math functionality with `#[no_std]`] else if lang == "de" [Mathematische Funktionen mit `#[no_std]` nutzen] else { todo }) -#set heading(offset: whole*2) #if lang == "en" [ If you want to perform math related functionality like calculating the diff --git a/typ/unsorted/speed-vs-size.typ b/typ/unsorted/speed-vs-size.typ index a0fc7801..d0b65a20 100644 --- a/typ/unsorted/speed-vs-size.typ +++ b/typ/unsorted/speed-vs-size.typ @@ -4,7 +4,6 @@ if lang == "en" [Optimizations: the speed size tradeoff] else if lang == "de" [Optimierungen: Der Kompromiss zwischen Geschwindigkeit und Größe] else { todo }) -#set heading(offset: whole*2) #if lang == "en" [ Everyone wants their program to be super fast and super small but it's From ab7884a13fcf69128bdf29f6b0e0db7358d11b59 Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Fri, 7 Aug 2026 12:22:36 +0300 Subject: [PATCH 04/11] fix toc.js --- typ/design-patterns/hal/gpio.typ | 4 +- typ/intro/install/macos.typ | 8 ++ typ/intro/install/verify.typ | 28 ++++- typ/intro/install/windows.typ | 17 +++ typ/mdbook/assets/toc-7ac66f26.js_part1 | 11 ++ ...{toc-7ac66f26.js => toc-7ac66f26.js_part2} | 12 +- typ/mdbook/lib.typ | 100 +++++++++------- typ/mdbook/typst.toml | 7 ++ typ/start/exceptions.typ | 89 ++++++++++++++ typ/start/hardware.typ | 113 ++++++++++++++++-- typ/start/index.typ | 3 + 11 files changed, 328 insertions(+), 64 deletions(-) create mode 100644 typ/mdbook/assets/toc-7ac66f26.js_part1 rename typ/mdbook/assets/{toc-7ac66f26.js => toc-7ac66f26.js_part2} (68%) diff --git a/typ/design-patterns/hal/gpio.typ b/typ/design-patterns/hal/gpio.typ index cce8fc8f..778dd9d4 100644 --- a/typ/design-patterns/hal/gpio.typ +++ b/typ/design-patterns/hal/gpio.typ @@ -177,14 +177,14 @@ enum Port { ] else { todo } - `pub fn into_input(self, input: N) -> Pin` - `pub fn into_output(self, output: N) -> Pin` -- ```ignore +- ```rust pub fn with_input_state( &mut self, input: N, f: impl FnOnce(&mut PA1) -> R, ) -> R ``` -- ```ignore +- ```rust pub fn with_output_state( &mut self, output: N, diff --git a/typ/intro/install/macos.typ b/typ/intro/install/macos.typ index 233a54dc..895370e6 100644 --- a/typ/intro/install/macos.typ +++ b/typ/intro/install/macos.typ @@ -10,10 +10,13 @@ All the tools can be installed using #brew_ln or #ports_ln: ] else if lang == "de" [ Alle Werkzeuge können mit #brew_ln oder #ports_ln installiert werden: +] else if lang == "zh" [ + 所有的工具都可以使用#brew_ln;或者#ports_ln;来安装: ] else { todo } = #(if lang == "en" [Install tools with #brew_ln] else if lang == "de" [Werkzeuge mit #brew_ln installieren] + else if lang == "zh" [使用#brew_ln;安装工具] else { todo }) ```text @@ -34,6 +37,8 @@ $ brew install qemu ] else if lang == "de" [ *HINWEIS* Falls OpenOCD abstürzt, müssen Sie möglicherweise die neueste Version installieren; verwenden Sie dazu: +] else if lang == "zh" [ + *注意* 如果OpenOCD崩溃了,你可能需要用以下方法安装最新版本: ] else { todo } ] @@ -43,6 +48,7 @@ $ brew install --HEAD openocd = #(if lang == "en" [Install tools with #ports_ln] else if lang == "de" [Werkzeuge mit #ports_ln installieren] + else if lang == "zh" [使用#ports_ln;安装工具] else { todo }) ```text @@ -60,4 +66,6 @@ $ sudo port install qemu That's all! Go to the #link()[next section]. ] else if lang == "de" [ Das war's! Gehen Sie zum #link()[nächsten Abschnitt]. +] else if lang == "zh" [ + 这是全部内容,请转入#link()[下个章节]. ] else { todo } diff --git a/typ/intro/install/verify.typ b/typ/intro/install/verify.typ index a89a0ddc..9fd3f0dd 100644 --- a/typ/intro/install/verify.typ +++ b/typ/intro/install/verify.typ @@ -3,6 +3,7 @@ #h1(offset: whole*2, if lang == "en" [Verify Installation] else if lang == "de" [Die Installation überprüfen] + else if lang == "zh" [安装验证] else { todo }) @@ -12,6 +13,8 @@ ] else if lang == "de" [ In diesem Abschnitt überprüfen wir, ob einige der erforderlichen Werkzeuge bzw. Treiber korrekt installiert und konfiguriert wurden. +] else if lang == "zh" [ + 在这个章节中我们将检查工具和驱动是否已经被正确地安装和配置了。 ] else { todo } #if lang == "en" [ @@ -23,6 +26,8 @@ Discovery-Board. Das Discovery-Board verfügt über zwei USB-Anschlüsse; verwenden Sie den mit „USB ST-LINK" beschrifteten Anschluss, der sich mittig an der Platinenkante befindet. +] else if lang == "zh" [ + 使用一个micro USB线缆将你的笔记本/个人电脑连接到discovery开发板上。discovery开发板有两个USB连接器;使用标记着"USB ST-LINK"的那个,它位于开发板边缘的中间位置。 ] else { todo } #if lang == "en" [ @@ -30,14 +35,17 @@ the ST-LINK header is highlighted. ] else if lang == "de" [ Überprüfen Sie außerdem, ob die ST-LINK-Stiftleiste bestückt ist. Siehe - dazu die Abbildung unten; die ST-LINK-Stiftleiste ist dort - hervorgehoben. + dazu die Abbildung unten; die ST-LINK-Stiftleiste ist dort hervorgehoben. +] else if lang == "zh" [ + 也要检查下ST-LINK的短路帽是否被安装了。看下面的图;ST-LINK短路帽用红色圈起来了。 ] else { todo } #if lang == "en" [ Now run the following command: ] else if lang == "de" [ Führen Sie nun den folgenden Befehl aus: +] else if lang == "zh" [ + 现在运行下面的命令: ] else { todo } ```console @@ -56,6 +64,10 @@ openocd -f interface/stlink.cfg -f target/stm32f3x.cfg bevorzugende) Datei `interface/stlink.cfg`; stattdessen müssen Sie möglicherweise `interface/stlink-v2.cfg` oder `interface/stlink-v2-1.cfg` verwenden. +] else if lang == "zh" [ + *注意*: 旧版的openocd, 包括从2017发布的0.10.0, + 不包含新的(且更适合的)`interface/stlink.cfg`文件; + 你需要使用`interface/stlink-v2.cfg` 或者 `interface/stlink-v2-1.cfg`。 ] else { todo } ] @@ -65,6 +77,8 @@ openocd -f interface/stlink.cfg -f target/stm32f3x.cfg ] else if lang == "de" [ Sie sollten die folgende Ausgabe erhalten und das Programm sollte die Konsole blockieren: +] else if lang == "zh" [ + 你应该看到了下面的输出,且程序应该阻塞住了控制台: ] else { todo } ```text @@ -95,6 +109,8 @@ Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints Zeile bezüglich Breakpoints und Watchpoints sollten Sie sehen. Wenn dies der Fall ist, beenden Sie den OpenOCD-Prozess und fahren Sie mit dem #link()[nächsten Abschnitt] fort. +] else if lang == "zh" [ + 内容可能并不是一模一样,但是在最后一行,你应该看到了breakpoints和watchpoints,如果你看到了,那就终止OpenOCD进程然后进入#link()[下个章节] ] else { todo } #if lang == "en" [ @@ -103,6 +119,8 @@ Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints ] else if lang == "de" [ Falls du die Zeile „breakpoints" nicht erhalten hast, versuche einen der folgenden Befehle. +] else if lang == "zh" [ + 如果你没看到"breakpoints"这行,尝试下下列命令中的某一个命令。 ] else { todo } ```console @@ -124,6 +142,8 @@ openocd -f interface/stlink-v2-1.cfg -f target/stm32f3x.cfg Problem dar, aber merken Sie sich diesen Umstand, da Sie die Konfiguration später etwas anders vornehmen müssen. Sie können zum #link()[nächsten Abschnitt] übergehen. +] else if lang == "zh" [ + 如果这些命令的某条起作用了,那意味着你使用的discovery开发板是一个旧的版本。那也不成问题,但是你要记住这件事,因为随后你的配置可能有点不同。你可以移到#link()[下个章节]了。 ] else { todo } #if lang == "en" [ @@ -136,6 +156,8 @@ openocd -f interface/stlink-v2-1.cfg -f target/stm32f3x.cfg Sie, sie mit Root-Rechten auszuführen (z. B. `sudo openocd ..`). Sollten die Befehle mit Root-Rechten funktionieren, überprüfen Sie, ob die #link()[udev-Regeln] korrekt eingerichtet wurden. +] else if lang == "zh" [ + 如果这些命令在普通用户模式下都没用,尝试下使用root模式运行它们(e.g.~`sudo openocd ..`)。如果命令在root模式下起作用,需要检查下#link()[udev rules]是否被正确地设置了。 ] else { todo } #let url_issues = "https://github.com/rust-embedded/book/issues" @@ -148,4 +170,6 @@ openocd -f interface/stlink-v2-1.cfg -f target/stm32f3x.cfg eröffnen Sie bitte #link(url_issues)[eine Problemmeldung], und wir helfen Ihnen weiter! +] else if lang == "zh" [ + 如果这些都试了,OpenOCD还不工作,请打开一个#link(url_issues)[issue],我们将帮助你! ] else { todo } diff --git a/typ/intro/install/windows.typ b/typ/intro/install/windows.typ index 54ba4daf..be191a4c 100644 --- a/typ/intro/install/windows.typ +++ b/typ/intro/install/windows.typ @@ -18,6 +18,10 @@ Abschluss des Installationsvorgangs die Option „Add path to environment variable". Überprüfen Sie anschließend, ob die Werkzeuge in Ihrem `%PATH%` enthalten sind: +] else if lang == "zh" [ + ARM提供了用于Windows的`.exe`安装程序。从#link(url_arm_gnu)[这里]获取, + 然后按照说明操作。 在完成安装之前,勾选/选择"Add path to environment + variable"选项。 然后验证环境变量是否添加到 `%PATH%`中: ] else { todo } ```text @@ -46,12 +50,17 @@ GNU gdb (GNU Tools for Arm Embedded Processors 7-2018-q2-update) 8.1.0.20180315- Ihre Umgebungsvariable `%PATH%`, indem Sie den Pfad hinzufügen, unter dem die Binärdateien installiert wurden (z. B. #bin_path, falls Sie die einfache Installation verwendet haben). +] else if lang == "zh" [ + OpenOCD 官方没有提供Windows的二进制版本, + 若你没有心情去折腾编译,#link(url_openocd)[这里]有xPack提供的一个二进制发布.。按照说明进行安装。然后更新你的`%PATH%` 环境变量,将安装目录包括进去。 (#bin_path, 如果使用简易安装) ] else { todo } #if lang == "en" [ Verify that OpenOCD is in your `%PATH%` with: ] else if lang == "de" [ Überprüfen Sie mit folgendem Befehl, ob OpenOCD in Ihrem `%PATH%` enthalten ist: +] else if lang == "zh" [ + 使用以下命令验证OpenOCD是否在你的`%PATH%`环境变量中 : ] else { todo } ```text @@ -67,6 +76,8 @@ Open On-Chip Debugger 0.10.0 Grab QEMU from #link(url_qemu)[the official website]. ] else if lang == "de" [ Lade QEMU von #link(url_qemu)[der offiziellen Website] herunter. +] else if lang == "zh" [ + 从#link(url_qemu)[官网]获取QEMU。 ] else { todo } = ST-LINK USB driver @@ -81,10 +92,16 @@ Open On-Chip Debugger 0.10.0 installieren, da OpenOCD sonst nicht funktioniert. Befolgen Sie die Anweisungen des Installationsprogramms und stellen Sie sicher, dass Sie die richtige Version des Treibers (32-Bit oder 64-Bit) installieren. +] else if lang == "zh" [ + 你还需要安装这个 + #link(url_stlink)[USB驱动] + 否则OpenOCD将无法工作。按照安装程序的说明,确保你安装了正确版本(32位或64位)的驱动程序。 ] else { todo } #if lang == "en" [ That's all! Go to the #link()[next section]. ] else if lang == "de" [ Das war's! Gehen Sie zum #link()[nächsten Abschnitt]. +] else if lang == "zh" [ + 以上是全部内容!转到 #link()[下个章节]。 ] else { todo } diff --git a/typ/mdbook/assets/toc-7ac66f26.js_part1 b/typ/mdbook/assets/toc-7ac66f26.js_part1 new file mode 100644 index 00000000..6184feb7 --- /dev/null +++ b/typ/mdbook/assets/toc-7ac66f26.js_part1 @@ -0,0 +1,11 @@ +// Populate the sidebar +// +// This is a script, and not included directly in the page, to control the total size of the book. +// The TOC contains an entry for each page, so if each page includes a copy of the TOC, +// the total size of the page becomes O(n**2). +class MDBookSidebarScrollbox extends HTMLElement { + constructor() { + super(); + } + connectedCallback() { + this.innerHTML = ' \ No newline at end of file diff --git a/typ/mdbook/assets/toc-7ac66f26.js b/typ/mdbook/assets/toc-7ac66f26.js_part2 similarity index 68% rename from typ/mdbook/assets/toc-7ac66f26.js rename to typ/mdbook/assets/toc-7ac66f26.js_part2 index d93cfd8d..1ead2f71 100644 --- a/typ/mdbook/assets/toc-7ac66f26.js +++ b/typ/mdbook/assets/toc-7ac66f26.js_part2 @@ -1,14 +1,4 @@ -// Populate the sidebar -// -// This is a script, and not included directly in the page, to control the total size of the book. -// The TOC contains an entry for each page, so if each page includes a copy of the TOC, -// the total size of the page becomes O(n**2). -class MDBookSidebarScrollbox extends HTMLElement { - constructor() { - super(); - } - connectedCallback() { - this.innerHTML = '
    1. Introduction
      1. Hardware
      2. no_std
      3. Tooling
      4. Installation
        1. Linux
        2. MacOS
        3. Windows
        4. Verify Installation
    2. Getting started
      1. QEMU
      2. Hardware
      3. Memory-mapped Registers
      4. Semihosting
      5. Panicking
      6. Exceptions
      7. Interrupts
    3. Peripherals
      1. A first attempt in Rust
      2. The Borrow Checker
      3. Singletons
    4. Static Guarantees
      1. Typestate Programming
      2. Peripherals as State Machines
      3. Design Contracts
      4. Zero Cost Abstractions
    5. Portability
    6. Concurrency
    7. Collections
    8. Design Patterns
      1. HALs
        1. Checklist
        2. Naming
        3. Interoperability
        4. Predictability
        5. GPIO
    9. Tips for embedded C developers
    10. Interoperability
      1. A little C with your Rust
      2. A little Rust with your C
    11. Unsorted topics
      1. Optimizations: The speed size tradeoff
      2. Performing Math Functionality
    12. Appendix A: Glossary
    '; +'; // Set the current, active page, and reveal it if it's hidden let current_page = document.location.href.toString().split('#')[0].split('?')[0]; if (current_page.endsWith('/')) { diff --git a/typ/mdbook/lib.typ b/typ/mdbook/lib.typ index 6f5715af..31ae7400 100644 --- a/typ/mdbook/lib.typ +++ b/typ/mdbook/lib.typ @@ -15,6 +15,24 @@ } } +#let elem-to-string(content) = { + if content == none { + "" + } else if content.has("children") { + content.children.map(elem-to-string).join("") + } else if content.has("tag") { + let attrs = "" + if content.has("attrs") { + for (k, v) in content.attrs { + attrs = attrs+" "+k+"=\""+v+"\"" + } + } + "<"+content.tag+attrs+">"+elem-to-string(content.body)+"" + } else { + to-string(content) + } +} + #let html_head_common(pth) = [ #html.link(rel: "stylesheet", href: pth+"css/variables-8adf115d.css") #html.link(rel: "stylesheet", href: pth+"css/general-e96d0476.css") @@ -46,9 +64,6 @@ // Provide site root and default themes to javascript #builtin-script("provide-themes", root_pth: root_pth) - - // Start loading toc.js asap - #html.script(src: root_pth+"toc-7ac66f26.js") ] #let help_container() = [ @@ -183,58 +198,59 @@ #html.div(class: "sidebar-resize-indicator") ] ] -#let toc(sources, out, lang, default_lang) = document(out)[ - #html.html(lang: lang, class: "light", dir: ltr)[ - #html.head[ - #html.meta(charset: "utf-8") - #html.meta(name:"robots", content: "noindex") - #html.meta(name: "viewport", content: "width=device-width, initial-scale=1") - #html.meta(name: "theme-color", content: "#ffffff") - #html_head_common(if lang == default_lang { - "" - } else { - "../" - }) - #html.title() - ] - #html.body(class: "sidebar-iframe-inner")[ - #html.ol(class: "chapter")[ - #let num = (0,) - #for (path, src) in sources.pairs() { - let d = 0 +#let toc_tree(sources, prefix: "") = html.ol(class: "chapter")[ + #let num = (0,) + #for (path, src) in sources.pairs() { + let d = 0 + num = num.slice(0, d+1) + num.at(d) = num.at(d) + 1 + html.li(class: ("chapter-item", "expanded"))[#html.span(class: "chapter-link-wrapper")[ + #html.a(href: prefix+path+".html", target: "_parent", [#html.strong(aria-hidden: true)[#num.map(i => [#i.]).join[]] #src.at("title")]) + ]] + if "sub" in src { + html.ol(class: "section")[ + #num.push(0) + #for (path, src) in src.at("sub").pairs() { + let d = 1 num = num.slice(0, d+1) num.at(d) = num.at(d) + 1 html.li(class: ("chapter-item", "expanded"))[#html.span(class: "chapter-link-wrapper")[ - #html.a(href: path+".html", target: "_parent", [#html.strong(aria-hidden: true)[#num.map(i => [#i.]).join[]] #src.at("title")]) + #html.a(href: prefix+path+".html", target: "_parent", [#html.strong(aria-hidden: true)[#num.map(i => [#i.]).join[]] #src.at("title")]) ]] if "sub" in src { html.ol(class: "section")[ #num.push(0) #for (path, src) in src.at("sub").pairs() { - let d = 1 + let d = 2 num = num.slice(0, d+1) num.at(d) = num.at(d) + 1 html.li(class: ("chapter-item", "expanded"))[#html.span(class: "chapter-link-wrapper")[ - #html.a(href: path+".html", target: "_parent", [#html.strong(aria-hidden: true)[#num.map(i => [#i.]).join[]] #src.at("title")]) + #html.a(href: prefix+path+".html", target: "_parent", [#html.strong(aria-hidden: true)[#num.map(i => [#i.]).join[]] #src.at("title")]) ]] - if "sub" in src { - html.ol(class: "section")[ - #num.push(0) - #for (path, src) in src.at("sub").pairs() { - let d = 2 - num = num.slice(0, d+1) - num.at(d) = num.at(d) + 1 - html.li(class: ("chapter-item", "expanded"))[#html.span(class: "chapter-link-wrapper")[ - #html.a(href: path+".html", target: "_parent", [#html.strong(aria-hidden: true)[#num.map(i => [#i.]).join[]] #src.at("title")]) - ]] - } - ] - } } ] } } ] + } + } +] +#let toc(tree, out, lang, default_lang) = document(out)[ + #html.html(lang: lang, class: "light", dir: ltr)[ + #html.head[ + #html.meta(charset: "utf-8") + #html.meta(name:"robots", content: "noindex") + #html.meta(name: "viewport", content: "width=device-width, initial-scale=1") + #html.meta(name: "theme-color", content: "#ffffff") + #html_head_common(if lang == default_lang { + "" + } else { + "../" + }) + #html.title() + ] + #html.body(class: "sidebar-iframe-inner")[ + #tree ] ] ] @@ -270,6 +286,8 @@ "../" }+pth #html_head(title, root_pth) + // Start loading toc.js asap + #html.script(src: pth+"toc-7ac66f26_"+lang+".js") #html.body[ #help_container() #html.div(id: "mdbook-body-container")[ @@ -324,7 +342,6 @@ "mark-09e88c2c.min.js", "searcher-09f2665d.js", "searchindex-8ec871e3.js", - "toc-7ac66f26.js", "tomorrow-night-4c0ae647.css", ) @@ -342,7 +359,10 @@ } } let prefix = if lang != default_lang { lang+"/" } else { "" } - toc(sources, prefix+"toc.html", lang, default_lang) + + asset(prefix+"toc-7ac66f26_"+lang+".js", read("assets/"+"toc-7ac66f26.js_part1")+elem-to-string(toc_tree(sources, prefix: prefix))+read("assets/"+"toc-7ac66f26.js_part2")) + + toc(toc_tree(sources), prefix+"toc.html", lang, default_lang) for (i, (path, source, title)) in sa.enumerate() { let (prev_path, _, _) = if i > 0 { sa.at(i - 1) } else { (none, none, none) } let (next_path, _, _) = sa.at(i + 1, default: (none, none, none)) diff --git a/typ/mdbook/typst.toml b/typ/mdbook/typst.toml index e26a01f0..9e79e85c 100644 --- a/typ/mdbook/typst.toml +++ b/typ/mdbook/typst.toml @@ -2,3 +2,10 @@ name = "typbook" version = "0.1.0" entrypoint = "lib.typ" +authors = ["Andrey Zgarbul "] +license = "MIT" +description = "Generate `mdBook` like books and documentation" +repository = "https://github.com/burrbull/typbook" +keywords = ["html", "web", "mdbook", "documentation"] +categories = ["components", "model", "utility"] +compiler = "0.15.0" diff --git a/typ/start/exceptions.typ b/typ/start/exceptions.typ index 2b8c3a46..56ce9cee 100644 --- a/typ/start/exceptions.typ +++ b/typ/start/exceptions.typ @@ -3,6 +3,7 @@ #h1(offset: whole, if lang == "en" [Exceptions] else if lang == "de" [Ausnahmen (Exceptions)] + else if lang == "zh" [异常] else { todo }) @@ -18,6 +19,8 @@ Ausführung einer ungültigen Anweisung) behandelt. Ausnahmen implizieren Präemption und erfordern Ausnahmebehandlungsroutinen, die als Reaktion auf das auslösende Signal ausgeführt werden. +] else if lang == "zh" [ + 异常和中断,是处理器用来处理异步事件和致命错误(e.g.~执行一个无效的指令)的一种硬件机制。异常意味着抢占并涉及到异常处理程序,即响应触发事件的信号的子程序。 ] else { todo } #let ln_ex = link("https://docs.rs/cortex-m-rt-macros/latest/cortex_m_rt_macros/attr.exception.html")[`exception`] @@ -25,6 +28,8 @@ The `cortex-m-rt` crate provides an #ln_ex attribute to declare exception handlers. ] else if lang == "de" [ Die `cortex-m-rt`-Crate bietet das Attribut #ln_ex zur Deklaration von Ausnahmebehandlungsroutinen. +] else if lang == "zh" [ + `cortex-m-rt` crate提供了一个 #ln_ex 属性去声明异常处理程序。 ] else { todo } #raw(block: true, lang: "rust", @@ -32,6 +37,8 @@ "Exception handler for the SysTick (System Timer) exception" } else if lang == "de" { "Ausnahmebehandlungsroutine fuer die SysTick-Ausnahme (System-Timer)" + } else if lang == "zh" { + "SysTick (System计时器)异常的异常处理函数" } else { todos } + " #[exception] fn SysTick() { @@ -50,6 +57,11 @@ fn SysTick() { Unterschied: `exception`-Handler können _nicht_ per Software aufgerufen werden. Bezogen auf das vorherige Beispiel würde die Anweisung `SysTick();` zu einem Kompilierfehler führen. +] else if lang == "zh" [ + 除了 `exception` + 属性,异常处理函数看起来和普通函数一样,但是有一个很大的不同: + `exception` 处理函数 _不能_ 被软件调用。在先前的例子中,语句 + `SysTick();` 将会导致一个编译错误。 ] else { todo } #if lang == "en" [ @@ -61,6 +73,9 @@ fn SysTick() { bestimmte Eigenschaft zu gewährleisten: `static mut`-Variablen, die _innerhalb_ von `exception`-Handlern deklariert werden, sind _sicher_ in der Verwendung. +] else if lang == "zh" [ + 这么做是有目的的,因为异常处理函数必须具有一个特性: + 在异常处理函数中被声明为`static mut`的变量能被安全(safe)地使用。 ] else { todo } #raw(block: true, lang: "rust", @@ -72,6 +87,8 @@ fn SysTick() { } else if lang == "de" { "`COUNT` wurde in den Typ `&mut u32` umgewandelt und ist sicher in der // Verwendung." + } else if lang == "zh" { + "`COUNT` 被转换到了 `&mut u32` 类型且它用起来是安全的" } else { todos } + " *COUNT += 1; ") @@ -91,6 +108,8 @@ fn SysTick() { wiedereintrittsfähige Funktion direkt oder indirekt aus mehr als einem Ausnahme- oder Interrupt-Handler oder aus `main` und einem oder mehreren Ausnahme- oder Interrupt-Handlern heraus aufzurufen. +] else if lang == "zh" [ + 就像你可能已经知道的那样,在一个函数里使用`static mut`变量,会让函数变成#link(url_re)[_非可重入函数(non-reentrancy)_]。从多个异常/中断处理函数,或者从`main`函数和多个异常/中断处理函数中,直接或者间接地调用一个非可重入(non-reentrancy)函数是未定义的行为。 ] else { todo } #if lang == "en" [ @@ -110,6 +129,11 @@ fn SysTick() { (Reentrancy) stattfinden kann. Diese Handler werden von der Hardware selbst aufgerufen, bei der davon ausgegangen wird, dass sie physisch keine Nebenläufigkeit aufweist. +] else if lang == "zh" [ + #todoupd("zh") + 安全的Rust不能导致未定义的行为出现,所以非可重入函数必须被标记为 + `unsafe`。然而,我刚说了`exception`处理函数能安全地使用`static mut`变量。这怎么可能?因为`exception`处理函数 + _不_ 能被软件调用因此重入(reentrancy)不会发生,所以这才变得可能。 ] else { todo } #if lang == "en" [ @@ -166,11 +190,16 @@ fn SysTick() { wir die Referenz mittels `*` dereferenzieren, um auf die Werte der Variablen zuzugreifen, ohne sie selbst in einen `unsafe`-Block einschließen zu müssen. +] else if lang == "zh" [ + 注意,`exception`属性,通过将静态变量封装进`unsafe`块中并为我们提供了名字相同的,类型为 + `&mut` 的,合适的新变量,转换了函数中静态变量的定义。因此我们可以通过 + `*` 解引用访问变量的值而不需要将它们打包进一个 `unsafe` 块中。 ] else { todo } ] = #(if lang == "en" [A complete example] else if lang == "de" [Ein vollständiges Beispiel] + else if lang == "zh" [一个完整的例子] else { todo }) #if lang == "en" [ @@ -185,6 +214,11 @@ fn SysTick() { `SysTick`-Exception-Handler protokolliert in der Variablen `COUNT`, wie oft er aufgerufen wurde, und gibt anschließend den Wert von `COUNT` mittels Semihosting auf der Host-Konsole aus. +] else if lang == "zh" [ + 这里有个例子,使用系统计时器大概每秒抛出一个 `SysTick` + 异常。异常处理函数使用 `COUNT` + 变量追踪它自己被调用了多少次,然后使用半主机模式(semihosting)打印 + `COUNT` 的值到主机控制台上。 ] else { todo } #quote(block: true)[ @@ -194,6 +228,8 @@ fn SysTick() { ] else if lang == "de" [ *HINWEIS*: Sie können dieses Beispiel auf jedem Cortex-M-Gerät ausführen; es lässt sich auch unter QEMU ausführen. +] else if lang == "zh" [ + *注意*: 你能在任何Cortex-M设备上运行这个例子;你也能在QEMU运行它。 ] else { todo } ] @@ -223,6 +259,8 @@ fn main() -> ! { } else if lang == "de" { "konfiguriert den System-Timer so, dass er jede Sekunde eine SysTick- // Exception ausloest" + } else if lang == "zh" { + "配置系统的计时器每秒去触发一个SysTick异常" } else { todos } + " syst.set_clock_source(SystClkSource::Core); // " + if lang == "en" { @@ -230,6 +268,8 @@ fn main() -> ! { } else if lang == "de" { "Dies ist für den LM3S6965 konfiguriert, der einen Standard-CPU-Takt von // 12 MHz hat" + } else if lang == "zh" { + "这是关于LM3S6965的配置,其有一个12MHz的默认CPU时钟" } else { todos } + " syst.set_reload(12_000_000); syst.clear_current(); @@ -250,6 +290,8 @@ fn SysTick() { "Lazy initialization" } else if lang == "de" { "Verzoegerte Initialisierung" + } else if lang == "zh" { + "惰性初始化(Lazy initialization)" } else { todos } + " if STDOUT.is_none() { *STDOUT = hio::hstdout().ok(); @@ -266,12 +308,17 @@ fn SysTick() { "WICHTIG: Lassen Sie diesen `if`-Block weg, wenn Sie das Programm auf // echter Hardware ausfuehren, da sich Ihr Debugger sonst in // einem inkonsistenten Zustand befinden wird." + } else if lang == "zh" { + "重要信息 如果运行在真正的硬件上,去掉这个 `if` 块, + // 否则你的调试器将会以一种不一致的状态结束" } else { todos } + " if *COUNT == 9 { // " + if lang == "en" { "This will terminate the QEMU process" } else if lang == "de" { "Dies beendet den QEMU-Prozess." + } else if lang == "zh" { + "这将终结QEMU进程" } else { todos } + " debug::exit(debug::EXIT_SUCCESS); } @@ -304,10 +351,13 @@ $ cargo run --release Wenn Sie dies auf dem Discovery-Board ausführen, sehen Sie die Ausgabe in der OpenOCD-Konsole. Außerdem hält das Programm _nicht_ an, wenn der Zählerstand 9 erreicht. +] else if lang == "zh" [ + 如果你在Discovery开发板上运行这个例子,你将会在OpenOCD控制台上看到输出。还有,当计数到达9的时候,程序将 _会_ 停止。 ] else { todo } = #(if lang == "en" [The default exception handler] else if lang == "de" [Der Standard-Exception-Handler] + else if lang == "zh" [默认异常处理函数] else { todo }) #if lang == "en" [ @@ -321,6 +371,10 @@ $ cargo run --release _überschrieben_ wird. Wenn Sie den Handler für eine bestimmte Exception nicht überschreiben, wird sie von der Funktion `DefaultHandler` behandelt, die standardmäßig Folgendes tut: +] else if lang == "zh" [ + `exception` 属性真正做的是,_覆盖_ + 了一个特定异常的默认异常处理函数。如果你不覆盖一个特定异常的处理函数,它将会被 + `DefaultHandler` 函数处理,其默认的是: ] else { todo } ```rust @@ -338,6 +392,9 @@ fn DefaultHandler() { `#[no_mangle]` markiert, sodass Sie einen Breakpoint auf „DefaultHandler" setzen und _unbehandelte_ Exceptions abfangen können. +] else if lang == "zh" [ + 这个函数是 `cortex-m-rt` crate提供的,且被标记为 `#[no_mangle]` + 因此你能在 "DefaultHandler" 上放置一个断点并捕获 _unhandled_ 异常。 ] else { todo } #if lang == "en" [ @@ -346,6 +403,8 @@ fn DefaultHandler() { ] else if lang == "de" [ Es ist möglich, diesen `DefaultHandler` mithilfe des `exception`-Attributs zu überschreiben: +] else if lang == "zh" [ + 可以使用 `exception` 属性覆盖这个 `DefaultHandler`: ] else { todo } #raw(block: true, lang: "rust", @@ -355,6 +414,8 @@ fn DefaultHandler(irqn: i16) { "custom default handler" } else if lang == "de" { "benutzerdefinierter Standard-Handler" + } else if lang == "zh" { + "自定义默认处理函数" } else { todos } + " } ") @@ -370,10 +431,13 @@ fn DefaultHandler(irqn: i16) { wird, während ein Wert von null oder ein positiver Wert darauf hinweist, dass eine gerätespezifische Exception -- auch als Interrupt bezeichnet -- bearbeitet wird. +] else if lang == "zh" [ + `irqn` 参数指出了被服务的是哪个异常。一个负数值指出了被服务的是一个Cortex-M异常;0或者一个正数值指出了被服务的是一个设备特定的异常,也就是中断。 ] else { todo } = #(if lang == "en" [The hard fault handler] else if lang == "de" [Der "Schwere Fehler"-Handler] + else if lang == "zh" [硬错误(Hard Fault)处理函数] else { todo }) #if lang == "en" [ @@ -389,6 +453,11 @@ fn DefaultHandler(irqn: i16) { führen könnte. Zudem führt die Runtime-Crate einige Vorarbeiten durch, bevor der benutzerdefinierte `HardFault`-Handler aufgerufen wird, um die Fehlersuche (Debugging) zu erleichtern. +] else if lang == "zh" [ + `HardFault`异常有点特别。当程序进入一个无法工作的状态时,这个异常被触发,因此它的处理函数 + _不能_ 返回,因为这么做可能导致一个未定义的行为。在用户定义的 + `HardFault` + 处理函数被调用之前,运行时crate还做了一些工作以改进调试功能。 ] else { todo } #if lang == "en" [ @@ -404,6 +473,9 @@ fn DefaultHandler(irqn: i16) { Stack gesichert wurden. Diese Register stellen eine Momentaufnahme des Prozessorzustands zum Zeitpunkt der Auslösung der Exception dar und sind für die Diagnose eines Hard Faults hilfreich. +] else if lang == "zh" [ + 结果是,`HardFault`处理函数必须有下列的签名: `fn(&ExceptionFrame) -> !` + 。处理函数的参数是一个指针,它指向被异常推入栈中的寄存器。这些寄存器是异常被触发那刻,处理器状态的一个记录,能被用来分析一个硬错误。 ] else { todo } #if lang == "en" [ @@ -412,6 +484,8 @@ fn DefaultHandler(irqn: i16) { ] else if lang == "de" [ Hier ist ein Beispiel für eine unzulässige Operation: der Lesezugriff auf eine nicht existierende Speicheradresse. +] else if lang == "zh" [ + 这里有个执行不合法操作的案例: 读取一个不存在的存储位置。 ] else { todo } #quote(block: true)[ @@ -424,6 +498,10 @@ fn DefaultHandler(irqn: i16) { (d.~h. es stürzt nicht ab), da `qemu-system-arm -machine lm3s6965evb` keine Überprüfung von Speicherzugriffen durchführt und beim Lesen von ungültigem Speicher problemlos den Wert `0` zurückgibt. +] else if lang == "zh" [ + *注意*: 这个程序在QEMU上不能起作用,i.e.~它不会崩溃,因为 + `qemu-system-arm -machine lm3s6965evb` + 不对读取存储的操作进行检查,且读取无效存储时将会开心地返回 `0`。 ] else { todo } ] @@ -446,6 +524,8 @@ fn main() -> ! { "read a nonexistent memory location" } else if lang == "de" { "liest einen nicht existierenden Speicherbereich" + } else if lang == "zh" { + "读取一个无效的存储位置" } else { todos } + " unsafe { ptr::read_volatile(0x3FFF_0000 as *const u32); @@ -470,6 +550,8 @@ fn HardFault(ef: &ExceptionFrame) -> ! { ] else if lang == "de" [ Der `HardFault`-Handler gibt den Wert des `ExceptionFrame` aus. Wenn Sie dies ausführen, sehen Sie auf der OpenOCD-Konsole eine Ausgabe wie diese. +] else if lang == "zh" [ + `HardFault`处理函数打印了`ExceptionFrame`值。如果你运行这个,你将会看到下面的东西打印到OpenOCD控制台上。 ] else { todo } ```text @@ -493,12 +575,16 @@ ExceptionFrame { ] else if lang == "de" [ Der Wert `pc` ist der Wert des Programmzählers zum Zeitpunkt der Ausnahme und verweist auf die Anweisung, die die Ausnahme ausgelöst hat. +] else if lang == "zh" [ + `pc`值是异常时程序计数器(Program Counter)的值,它指向触发了异常的指令。 ] else { todo } #if lang == "en" [ If you look at the disassembly of the program: ] else if lang == "de" [ Wenn Sie sich den Disassembler des Programms ansehen: +] else if lang == "zh" [ + 如果你看向程序的反汇编: ] else { todo } ```text @@ -525,4 +611,7 @@ ResetTrampoline: Das Feld `r0` von `ExceptionFrame` gibt an, dass der Wert des Registers `r0` zu diesem Zeitpunkt `0x3fff_fffe` war. +] else if lang == "zh" [ + 你可以在反汇编中搜索程序计数器`0x0800094a`的值。你将会看到一个读取操作(`ldr r0, [r0]`)导致了异常。`ExceptionFrame`的`r0`字段将告诉你,那时寄存器`r0`的值是`0x3fff_fffe` + 。 ] else { todo } diff --git a/typ/start/hardware.typ b/typ/start/hardware.typ index 2547cd92..2971a339 100644 --- a/typ/start/hardware.typ +++ b/typ/start/hardware.typ @@ -2,6 +2,7 @@ #h1(offset: whole, if lang in ("en", "de") [Hardware] + else if lang == "zh" [硬件] else { todo }) @@ -12,12 +13,14 @@ ] else if lang == "de" [ Mittlerweile sollten Sie mit den Werkzeugen und dem Entwicklungsprozess einigermaßen vertraut sein. In diesem Abschnitt wechseln wir zur echten - Hardware; der Prozess bleibt im Großen und Ganzen gleich. Tauchen wir - ein. + Hardware; der Prozess bleibt im Großen und Ganzen gleich. Tauchen wir ein. +] else if lang == "zh" [ + 现在你应该有点熟悉工具和开发过程了。在这部分我们将切换到真正的硬件上;步骤非常相似。让我们深入下去。 ] else { todo } = #(if lang == "en" [Know your hardware] else if lang == "de" [Kennen Sie Ihre Hardware] + else if lang == "zh" [认识你的硬件] else { todo }) #if lang == "en" [ @@ -40,6 +43,12 @@ Flash und 32 KiB RAM. - Wo werden Flash-Speicher und RAM im Adressraum abgebildet? zB RAM ist befindet sich üblicherweise an der Adresse `0x2000_0000`. +] else if lang == "zh" [ + 在我们开始之前,你需要了解下你的目标设备的一些特性,因为你将用它们来配置项目: + - ARM 内核。比如 Cortex-M3 。 + - ARM 内核包括一个FPU吗?Cortex-M4#[*F*]和Cortex-M7#[*F*]有。 + - 目标设备有多少Flash和RAM?比如 256KiB的Flash和32KiB的RAM。 + - Flash和RAM映射在地址空间的什么位置?比如 RAM通常位于 `0x2000_0000` 地址处。 ] else { todo } #if lang == "en" [ @@ -48,6 +57,8 @@ ] else if lang == "de" [ Diese Informationen finden Sie im Datenblatt oder im Referenzhandbuch Ihres/Ihrer Microcontrollers/Microcontrollerplatine. +] else if lang == "zh" [ + 你可以在你的设备的数据手册和参考手册上找到这些信息。 ] else { todo } #if lang == "en" [ @@ -66,20 +77,26 @@ - 256 KiB Flash befinden sich an der Adresse 0x0800_0000. - 440 KiB RAM an der Adresse 0x2000_0000. (Es gibt noch eine weitere RAM-Region, aber der Einfachheit halber ignorieren wir das). +] else if lang == "zh" [ + 这部分,要使用我们的参考硬件,STM32F3DISCOVERY。这个板子包含一个STM32F303VCT6微控制器。这个微控制器拥有: + - 一个Cortex-M4F核心,它包含一个单精度FPU。 + - 位于 0x0800_0000 地址的256KiB的Flash。 + - 位于 0x2000_0000 地址的40KiB的RAM。(这里还有其它的RAM区域,但是为了方便起见,我们将忽略它)。 ] else { todo } = #(if lang == "en" [Configuring] else if lang == "de" [Konfigurieren] + else if lang == "zh" [配置] else { todo }) #if lang == "en" [ We'll start from scratch with a fresh template instance. Refer to the - #link( )[previous section on QEMU] for a refresher on how to do - this without `cargo-generate`. + #link( )[previous section on QEMU] for a refresher on how to do this without `cargo-generate`. ] else if lang == "de" [ Wir beginnen bei Null mit einer neuen Vorlageninstanz. Zur Auffrischung - siehe #link()[vorheriger Abschnitt zu QEMU], wie man das ohne - `cargo-generate` macht. + siehe #link()[vorheriger Abschnitt zu QEMU], wie man das ohne `cargo-generate` macht. +] else if lang == "zh" [ + 我们将使用一个新的模板实例从零开始。对于新手,请参考#link()[先前的QEMU]章节,了解如何在没有`cargo-generate`的情况下完成配置。 ] else { todo } ```text @@ -96,13 +113,15 @@ $ cd app ] else if lang == "de" [ Schritt Nummer eins besteht darin, ein Standardkompilierungsziel in `.cargo/config.toml` festzulegen. +] else if lang == "zh" [ + 第一步是在`.cargo/config.toml`中设置一个默认编译目标。 ] else { todo } ```console tail -n5 .cargo/config.toml ``` -#if lang == "en" [ +#if lang in ("en", "zh") [ ```toml # Pick ONE of these compilation targets # target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ @@ -124,6 +143,8 @@ tail -n5 .cargo/config.toml We'll use `thumbv7em-none-eabihf` as that covers the Cortex-M4F core. ] else if lang == "de" [ Wir verwenden `thumbv7em-none-eabihf`, da es den Cortex-M4F-Kern abdeckt. +] else if lang == "zh" [ + 我们将使用 `thumbv7em-none-eabihf`,因为它包括了Cortex-M4F内核. ] else { todo } #quote(block: true)[ @@ -137,6 +158,10 @@ tail -n5 .cargo/config.toml Kapitel erinnern, müssen wir alle Ziele installieren, dies ist ein neues Ziel. Vergessen Sie also nicht, die Installationsprozess `rustup target add thumbv7em-none-eabihf` für dieses Ziel auszuführen. +] else if lang == "zh" [ + *注意*:你可能还记得先前的章节,我们必须要安装所有的目标平台,这个平台是一个新的. + 所以,不要忘了为这个平台运行安装步骤 + `rustup target add thumbv7em-none-eabihf` . ] else { todo } ] @@ -144,6 +169,8 @@ tail -n5 .cargo/config.toml The second step is to enter the memory region information into the `memory.x` file. ] else if lang == "de" [ Der zweite Schritt besteht darin, die Speicherbereichsinformationen in die Datei `memory.x` einzugeben +] else if lang == "zh" [ + 第二步是将存储区域信息(memory region information)输入`memory.x`。 ] else { todo } ```text @@ -168,6 +195,8 @@ MEMORY geändert haben, nachdem Sie den ersten Build für ein bestimmtes Build-Ziel erstellt haben, dann führen Sie `cargo clean` vor `cargo build` aus, da `cargo build` keine Änderungen von `memory.x` verfolgt. +] else if lang == "zh" [ + *注意*:如果你因为某些理由,在对某个编译目标首次编译后,改变了`memory.x`文件,需要在`cargo build`之前执行`cargo clean`。因为`cargo build`可能不会跟踪`memory.x`的更新。 ] else { todo } ] @@ -177,6 +206,8 @@ MEMORY ] else if lang == "de" [ Wir beginnen noch einmal mit dem Hallo-Beispiel, aber zuerst müssen wir eine kleine Änderung vornehmen. +] else if lang == "zh" [ + 我们将再次使用hello示例作为开始,但是首先我们必须做一个小改变。 ] else { todo } #if lang == "en" [ @@ -185,6 +216,8 @@ MEMORY ] else if lang == "de" [ Stellen Sie in `examples/hello.rs`sicher, daß `debug::exit()` auskommentiert oder entfernt wurde. Es wird nur zum Ausführen in QEMU verwendet. +] else if lang == "zh" [ + 在`examples/hello.rs`中,确保`debug::exit()`调用被注释掉了或者移除掉了。它只能用于在QEMU中运行的情况。 ] else { todo } #raw(block: true, lang: "rust", @@ -201,6 +234,9 @@ fn main() -> ! { // HINWEIS: Fuehren Sie dies nicht auf der Hardware aus; es kann den // OpenOCD-Zustand beschaedigen. // debug::exit(debug::EXIT_SUCCESS);" + } else if lang == "zh" { + "退出 QEMU + // 注意 不要在硬件上运行这个;它会打破OpenOCD的状态" } else { todos } + " loop {} @@ -218,6 +254,9 @@ fn main() -> ! { `cortex-m-rt`-Crate übernimmt die gesamte für den Betrieb des Chips erforderliche „Magie", da erfreulicherweise so gut wie alle Cortex-M-CPUs auf die gleiche Weise booten. +] else if lang == "zh" [ + 你可以像你之前做的一样,使用`cargo build`检查编译程序,使用`cargo-binutils`观察二进制项。`cortex-m-rt`库可以处理所有让芯片运行起来所需的魔法,几乎所有的Cortex-M + CPUs都按同样的方式启动。 ] else { todo } ```console @@ -226,6 +265,7 @@ cargo build --example hello = #(if lang == "en" [Debugging] else if lang == "de" [Fehlerbehebung (Debugging)] + else if lang == "zh" [调试] else { todo }) #let url_dn = "https://github.com/rust-embedded/debugonomicon" @@ -242,6 +282,8 @@ cargo build --example hello STM32F3DISCOVERY laufenden Programms erforderlich sind. Dies soll als Referenz dienen; für gerätespezifische Informationen zum Debugging konsultieren Sie bitte #link(url_dn)[das Debugonomicon]. +] else if lang == "zh" [ + 调试会看起来有点不一样。事实上,取决于不同的目标设备,第一步可能看起来不一样。在这个章节里,我们将展示,调试一个在STM32F3DISCOVERY上运行的程序,所需要的步骤。这作为一个参考。关于调试有关的设备特定的信息,可以看#link(url_dn)[the Debugonomicon]。 ] else { todo } #if lang == "en" [ @@ -250,6 +292,8 @@ cargo build --example hello ] else if lang == "de" [ Wie zuvor führen wir Remote-Debugging durch, wobei der Client ein GDB-Prozess ist. Diesmal ist der Server jedoch OpenOCD. +] else if lang == "zh" [ + 像之前一样,我们将进行远程调试,客户端将是一个GDB进程。不同的是,OpenOCD将是服务器。 ] else { todo } #if lang == "en" [ @@ -261,6 +305,8 @@ cargo build --example hello "#link()[Die Installation überprüfen]" beschrieben, das Discovery Board mit Ihrem Laptop/PC und prüfen Sie, ob der ST-LINK-Header ausgefüllt ist. +] else if lang == "zh" [ + 像是在#link()[安装验证]中做的那样,把你的笔记本/个人电脑和discovery开发板连接起来,检查ST-LINK的短路帽是否被安装了。 ] else { todo } #if lang == "en" [ @@ -274,13 +320,17 @@ cargo build --example hello im Stammverzeichnis der Vorlage aus; `openocd` greift dabei auf die Datei `openocd.cfg` zu, in der festgelegt ist, welche Schnittstellen- und Zieldateien verwendet werden sollen. +] else if lang == "zh" [ + 在一个终端上运行 `openocd` 连接到你的开发板上的 ST-LINK + 。从模板的根目录运行这个命令;`openocd` 将会选择 `openocd.cfg` + 文件,它指出了所使用的接口文件(interface file)和目标文件(target file)。 ] else { todo } ```console cat openocd.cfg ``` -#if lang == "en" [ +#if lang in ("en", "zh") [ ```text # Sample OpenOCD configuration for the STM32F3DISCOVERY development board @@ -325,6 +375,11 @@ cat openocd.cfg feststellen, dass Sie eine ältere Revision des Discovery-Boards besitzen, sollten Sie an dieser Stelle die Datei `openocd.cfg` so anpassen, dass `interface/stlink-v2.cfg` verwendet wird. +] else if lang == "zh" [ + *注意* + 如果你在#link()[安装验证]章节中,发现你的discovery开发板是一个更旧的版本,那么你应该修改你的 + `openocd.cfg` 文件,注释掉 `interface/stlink.cfg`,让它去使用 + `interface/stlink-v2.cfg` 。 ] else { todo } ] @@ -368,12 +423,17 @@ gdb-multiarch -q target/thumbv7em-none-eabihf/debug/examples/hello anstelle von `gdb-multiarch` eine andere Version von gdb, je nachdem, welche Version Sie im Installationskapitel installiert haben. Dies könnte beispielsweise `arm-none-eabi-gdb` oder einfach `gdb` sein. +] else if lang == "zh" [ + *注意*: + 像之前一样,你可能需要另一个版本的gdb而不是`gdb-multiarch`,取决于你在之前的章节安装了什么工具。这也可能使用的是`arm-none-eabi-gdb`或者只是`gdb` ] else { todo } #if lang == "en" [ Next connect GDB to OpenOCD, which is waiting for a TCP connection on port 3333. ] else if lang == "de" [ Verbinden Sie nun GDB mit OpenOCD, das auf eine TCP-Verbindung an Port 3333 wartet. +] else if lang == "zh" [ + 接下来把GDB连接到OpenOCD,它正在等待一个在端口3333上的TCP链接。 ] else { todo } ```console @@ -388,6 +448,8 @@ Remote debugging using :3333 ] else if lang == "de" [ Fahren Sie nun damit fort, das Programm mithilfe des Befehls `load` auf den Mikrocontroller zu _flashen_ (zu laden). +] else if lang == "zh" [ + 接下来使用`load`命令,继续 _flash_(加载) 程序到微控制器上。 ] else { todo } ```console @@ -408,6 +470,9 @@ Transfer rate: 13 KB/sec, 2489 bytes/write. müssen wir OpenOCD anweisen, Semihosting zu aktivieren, bevor wir einen entsprechenden Aufruf tätigen. Befehle können mithilfe des Befehls `monitor` an OpenOCD gesendet werden. +] else if lang == "zh" [ + 程序现在被加载了。这个程序使用半主机模式,因此在我们调用半主机模式之前,我们必须告诉OpenOCD使能半主机。你可以使用 + `monitor` 命令,发送命令给OpenOCD 。 ] else { todo } ```console @@ -421,6 +486,8 @@ semihosting is enabled ] else if lang == "de" [ Sie können sich alle OpenOCD-Befehle anzeigen lassen, indem Sie den Befehl `monitor help` aufrufen. +] else if lang == "zh" [ + 通过调用 `monitor help` 命令,你能看到所有的OpenOCD命令。 ] else { todo } ] @@ -430,6 +497,9 @@ semihosting is enabled ] else if lang == "de" [ Wie zuvor können wir mithilfe eines Breakpoints und des Befehls `continue` direkt zu `main` springen. +] else if lang == "zh" [ + 像我们之前一样,使用一个断点和 `continue` 命令我们可以跳过所有的步骤到 + `main` 。 ] else { todo } ```console @@ -456,6 +526,10 @@ Breakpoint 1, hello::__cortex_m_rt_main_trampoline () at examples/hello.rs:11 `continue` eingegeben haben, sollten Sie überprüfen, ob die Angaben zum Speicherbereich in der Datei `memory.x` für Ihr Gerät korrekt konfiguriert sind (sowohl die Startadressen _als auch_ die Längen). +] else if lang == "zh" [ + *注意* + 如果在你使用了上面的`continue`命令后,GDB阻塞住了终端而不是停在了断点处,你可能需要检查下`memory.x`文件中的存储分区的信息,对于你的设备来说是否被正确的设置了起始位置*和*大小 +。 ] else { todo } ] @@ -463,6 +537,8 @@ Breakpoint 1, hello::__cortex_m_rt_main_trampoline () at examples/hello.rs:11 Step into the main function with `step`. ] else if lang == "de" [ Springen Sie mit `step` in die main-Funktion. +] else if lang == "zh" [ + 使用`step`步进main函数里。 ] else { todo } ```console @@ -478,6 +554,9 @@ hello::__cortex_m_rt_main () at examples/hello.rs:13 ] else if lang == "de" [ Nachdem Sie das Programm mit `next` weitergeführt haben, sollten Sie unter anderem „Hello, world!" auf der OpenOCD-Konsole ausgegeben sehen. +] else if lang == "zh" [ + 在使用了`next`让函数继续执行之后,你应该看到 "Hello, world!" + 被打印到了OpenOCD控制台上。 ] else { todo } ```console @@ -499,12 +578,16 @@ Info : halted: PC: 0x080004bc ] else if lang == "de" [ Die Meldung wird nur einmal angezeigt, kurz bevor das Programm in die in Zeile 19 definierte Endlosschleife eintritt: `loop {}` +] else if lang == "zh" [ + 消息只打印一次,然后进入定义在19行的无限循环中: `loop {}` ] else { todo } #if lang == "en" [ You can now exit GDB using the `quit` command. ] else if lang == "de" [ Sie können GDB nun mit dem Befehl `quit` beenden. +] else if lang == "zh" [ + 使用 `quit` 命令,你现在可以退出 GDB 了。 ] else { todo } ```console @@ -526,6 +609,10 @@ Quit anyway? (y or n) all diese Schritte in einem einzigen GDB-Skript namens `openocd.gdb` zusammengefasst. Die Datei wurde während des Schritts `cargo generate` erstellt und sollte ohne Änderungen funktionieren. Werfen wir einen Blick darauf: +] else if lang == "zh" [ + 现在调试比之前多了点步骤,因此我们要把所有步骤打包进一个名为 + `openocd.gdb` 的GDB脚本中。这个文件在 `cargo generate` + 步骤中被生成,因此不需要任何修改了。让我们看一下: ] else { todo } ```console @@ -560,6 +647,10 @@ stepi ` -x openocd.gdb target/thumbv7em-none-eabihf/debug/examples/hello` ausführen, verbindet sich GDB sofort mit OpenOCD, aktiviert Semihosting, lädt das Programm und startet den Prozess. +] else if lang == "zh" [ + 现在运行 + ` -x openocd.gdb target/thumbv7em-none-eabihf/debug/examples/hello` + 将会立即把GDB和OpenOCD连接起来,使能半主机,加载程序和启动进程。 ] else { todo } #if lang == "en" [ @@ -571,13 +662,17 @@ stepi Runner einrichten, sodass `cargo run` das Programm baut _und_ eine GDB-Sitzung startet. Dieser Runner ist bereits in der Datei `.cargo/config.toml` enthalten, jedoch auskommentiert. +] else if lang == "zh" [ + 另外,你能将 ` -x openocd.gdb` 放进一个自定义的 runner 中,使 + `cargo run` 能编译程序并启动一个GDB会话。这个 runner 在 + `.cargo/config.toml` 中,但是它被注释掉了。 ] else { todo } ```console head -n10 .cargo/config.toml ``` -#if lang == "en" [ +#if lang in ("en", "zh") [ ```toml [target.thumbv7m-none-eabi] # uncomment this to make `cargo run` execute programs on QEMU diff --git a/typ/start/index.typ b/typ/start/index.typ index cfd813fc..314b37ea 100644 --- a/typ/start/index.typ +++ b/typ/start/index.typ @@ -2,6 +2,7 @@ #h1(if lang == "en" [Getting Started] else if lang == "de" [Erste Schritte] + else if lang == "zh" [开始] else { todo }) @@ -23,4 +24,6 @@ Abschnitt, in dem Hardware erforderlich ist, ist natürlich der Abschnitt #link()[Hardware], wo wir OpenOCD verwenden, um ein #ln_f3 zu programmieren. +] else if lang == "zh" [ + 在这部分里,你将会经历编写,编译,烧录和调试嵌入式程序。大多数的例子都不需要特定的硬件就可以试试,因为我们将要向你展示一个开源硬件仿真器,QEMU的基本使用。唯一需要硬件的部分,那就是,#link()[硬件]那一章,我们会使用OpenOCD去编程一个#ln_f3。 ] else { todo } \ No newline at end of file From bba1ac2b4548e015f1c0be9964673c008010af19 Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Fri, 7 Aug 2026 14:07:48 +0300 Subject: [PATCH 05/11] @braunharald German fixes --- typ/c-tips/index.typ | 4 +- typ/interoperability/index.typ | 1 + typ/intro/hardware.typ | 3 +- typ/intro/install.typ | 45 +++++++++++++++++++++ typ/start/interrupts.typ | 74 ++++++++++++++++++++++++---------- typ/unsorted/speed-vs-size.typ | 1 + 6 files changed, 104 insertions(+), 24 deletions(-) diff --git a/typ/c-tips/index.typ b/typ/c-tips/index.typ index 04588982..8c215606 100644 --- a/typ/c-tips/index.typ +++ b/typ/c-tips/index.typ @@ -841,7 +841,9 @@ fn main() { der Rust-Referenz. ] else { todo } -== Other Resources +== #(if lang == "en" [Other Resources] + else if lang == "de" [Weitere Ressourcen] + else { todo }) #let url_faq = "https://docs.rust-embedded.org/faq.html" #let url_for_c = "http://blahg.josefsipek.net/?p=580" diff --git a/typ/interoperability/index.typ b/typ/interoperability/index.typ index a42c2830..7b2861c2 100644 --- a/typ/interoperability/index.typ +++ b/typ/interoperability/index.typ @@ -114,6 +114,7 @@ fn foo(num: u32) { ] else { todo } == #(if lang == "en" [Interoperability with RTOSs] + else if lang == "de" [Interoperabilität mit RTOS] else { todo }) #if lang == "en" [ diff --git a/typ/intro/hardware.typ b/typ/intro/hardware.typ index 53ea1a42..8ee8201a 100644 --- a/typ/intro/hardware.typ +++ b/typ/intro/hardware.typ @@ -76,7 +76,8 @@ - GPIO-Pins (General Purpose Input/Output) und andere Pin-Typen, die über die beiden Stiftleistenreihen an den Seiten der Platine zugänglich sind. - - Eine Mini-USB-Schnittstelle, die über den mit „USB USER" + - Einen #link(wiki_accelerometer)[Beschleunigungsmesser] als Teil des #link(url_lsm303)[LSM303DLHC]-Chips. + - Ein #link(wiki_magnetometer)[Magnetfeldstärkenmessgerät] als Teil des #link(url_lsm303)[LSM303DLHC]-Chips. gekennzeichneten USB-Anschluss zugänglich ist. - Ein #link(wiki_accelerometer)[Beschleunigungssensor] als Teil des #link(url_lsm303)[LSM303DLHC]-Chips. #todoupd("de") - Ein #link(wiki_gyroscope)[Gyroskop] als Teil des diff --git a/typ/intro/install.typ b/typ/intro/install.typ index 4f10415d..b83f4836 100644 --- a/typ/intro/install.typ +++ b/typ/intro/install.typ @@ -2,28 +2,38 @@ #h1(offset: whole, if lang == "en" [Installing the tools] + else if lang == "de" [Werkzeuge installieren] else if lang == "zh" [安装工具] else { todo }) #if lang == "en" [ This page contains OS-agnostic installation instructions for a few of the tools: +] else if lang == "de" [ + Diese Seite enthält betriebssystemunabhängige Installationsanleitungen für + einige der Werkzeuge: ] else if lang == "zh" [ 这一页包含的工具安装指令与操作系统无关: ] else { todo } == #(if lang == "en" [Rust Toolchain] + else if lang == "de" [Rust-Werkzeuge] else if lang == "zh" [Rust 工具链] else { todo }) #let ln_rustup = link("https://rustup.rs") #if lang == "en" [ Install rustup by following the instructions at #ln_rustup. +] else if lang == "de" [ + Installieren Sie rustup, indem Sie die Anweisungen unter #ln_rustup. ] else { todo } #if lang == "en" [ *NOTE* Make sure you have a compiler version equal to or newer than `1.31`. `rustc -V` should return a date newer than the one shown below. +] else if lang == "de" [ + *HINWEIS* Stellen Sie sicher, dass Sie eine Compiler-Version verwenden, die mindestens `1.31` entspricht. Der Befehl `rustc -V` + sollte ein Datum ausgeben, das neuer ist als das unten angegebene. ] else if lang == "zh" [ *注意* 确保你的编译器版本等于或者大于`1.31`版本。`rustc -V`应该返回一个比下列日期更新的日期。 @@ -42,6 +52,14 @@ rustc 1.31.1 (b6c32da9b 2018-12-18) targets. For the STM32F3DISCOVERY board used for the examples in this book, use the `thumbv7em-none-eabihf` target. #link(url_find)[Find the best Cortex-M for you.] +] else if lang == "de" [ + Aus Gründen der Bandbreite und der Festplattenauslastung + unterstützt die Standardinstallation nur die native Kompilierung. Um die + Unterstützung für die Cross-Kompilierung für die ARM-Cortex-M-Architekturen + hinzuzufügen, wählen Sie eines der folgenden Kompilierungsziele aus. Für das + STM32F3DISCOVERY-Board, das für die Beispiele in diesem Buch verwendet wird, + verwenden Sie das Ziel `thumbv7em-none-eabihf`. + #link(url_find)[Finden Sie den für Sie am besten geeigneten Cortex-M.] ] else if lang == "zh" [ #todoupd("zh") 考虑到带宽和磁盘的使用量,默认的安装只支持主机环境的编译。为了添加对ARM @@ -50,6 +68,8 @@ rustc 1.31.1 (b6c32da9b 2018-12-18) #if lang == "en" [ Cortex-M0, M0+, and M1 (ARMv6-M architecture): +] else if lang == "de" [ + Cortex-M0, M0+, and M1 (ARMv6-M-Architektur): ] else if lang == "zh" [ Cortex-M0, M0+, 和 M1 (ARMv6-M 架构): ] else { todo } @@ -60,6 +80,8 @@ rustup target add thumbv6m-none-eabi #if lang == "en" [ Cortex-M3 (ARMv7-M architecture): +] else if lang == "de" [ + Cortex-M3 (ARMv7-M-Architektur): ] else if lang == "zh" [ Cortex-M3 (ARMv7-M 架构): ] else { todo } @@ -70,6 +92,9 @@ rustup target add thumbv7m-none-eabi #if lang == "en" [ Cortex-M4 and M7 without hardware floating point (ARMv7E-M architecture): +] else if lang == "de" [ + Cortex-M4 und M7 ohne Hardware-Gleitkommafunktion + (ARMv7E-M-Architektur): ] else if lang == "zh" [ 没有硬件浮点单元的Cortex-M4和M7 (ARMv7E-M架构) ] else { todo } @@ -80,6 +105,8 @@ rustup target add thumbv7em-none-eabi #if lang == "en" [ Cortex-M4F and M7F with hardware floating point (ARMv7E-M architecture): +] else if lang == "de" [ + Cortex-M4F und M7F mit Hardware-Gleitkomma (ARMv7E-M-Architektur): ] else if lang == "zh" [ 具有硬件浮点单元的Cortex-M4F和M7F (ARMv7E-M架构) ] else { todo } @@ -90,6 +117,8 @@ rustup target add thumbv7em-none-eabihf #if lang == "en" [ Cortex-M23 (ARMv8-M architecture): +] else if lang == "de" [ + Cortex-M23 (ARMv8-M-Architektur): ] else if lang == "zh" [ Cortex-M23 (ARMv8-M架构): ] else { todo } @@ -100,6 +129,8 @@ rustup target add thumbv8m.base-none-eabi #if lang == "en" [ Cortex-M33 and M35P (ARMv8-M architecture): +] else if lang == "de" [ + Cortex-M33 und M35P (ARMv8-M-Architektur): ] else if lang == "zh" [ Cortex-M33和M35P (ARMv8-M架构): ] else { todo } @@ -110,6 +141,8 @@ rustup target add thumbv8m.main-none-eabi #if lang == "en" [ Cortex-M33F and M35PF with hardware floating point (ARMv8-M architecture): +] else if lang == "de" [ + Cortex-M33F und M35PF mit Hardware-Gleitkomma (ARMv8-M-Architektur): ] else if lang == "zh" [ 具有硬件浮点单元的Cortex-M33F和M35PF (ARMv8-M架构): ] else { todo } @@ -129,6 +162,9 @@ rustup component add llvm-tools #let ln_vs = link("https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools&rel=16") #if lang == "en" [ WINDOWS: prerequisite C++ Build Tools for Visual Studio 2019 is installed. #ln_vs +] else if lang == "de" [ + WINDOWS: Voraussetzung ist, dass die C++-Build-Tools für Visual Studio 2019 + installiert sind. #ln_vs ] else if lang == "zh" [ WINDOWS: 需要预先安装 C++ Build Tools for Visual Studio 2019。#ln_vs ] else { todo } @@ -137,6 +173,8 @@ rustup component add llvm-tools #if lang == "en" [ We'll use this later to generate a project from a template. +] else if lang == "de" [ + Wir werden dies später verwenden, um ein Projekt aus einer Vorlage zu generieren. ] else if lang == "zh" [ 我们随后将使用这个来从模板生成一个项目。 ] else { todo } @@ -148,17 +186,24 @@ cargo install cargo-generate #if lang == "en" [ Note: on some Linux distros (e.g.~Ubuntu) you may need to install the packages `libssl-dev` and `pkg-config` prior to installing cargo-generate. +] else if lang == "de" [ + Hinweis: Bei einigen Linux-Distributionen (z. B. Ubuntu) müssen Sie + möglicherweise vor der Installation von `cargo-generate` die Pakete `libssl-dev` + und `pkg-config` installieren. ] else if lang == "zh" [ 注意:在某些Linux发行版上(e.g.~Ubuntu) 在安装cargo-generate之前,你可能需要安装`libssl-dev`和`pkg-config` ] else { todo } == #(if lang == "en" [OS-Specific Instructions] + else if lang == "de" [Betriebssystemspezifische Anweisungen] else if lang == "zh" [特定于操作系统的指令] else { todo }) #if lang == "en" [ Now follow the instructions specific to the OS you are using: +] else if lang == "de" [ + Befolgen Sie nun die Anweisungen für das von Ihnen verwendete Betriebssystem: ] else if lang == "zh" [ 现在根据你使用的操作系统,来执行对应的指令: ] else { todo } diff --git a/typ/start/interrupts.typ b/typ/start/interrupts.typ index 9b1b5923..021a2e29 100644 --- a/typ/start/interrupts.typ +++ b/typ/start/interrupts.typ @@ -2,6 +2,7 @@ #h1(offset: whole, if lang in ("en", "de") [Interrupts] + else if lang == "zh" [中断] else { todo }) #if lang == "en" [ @@ -18,6 +19,8 @@ sich bei Interrupts -- sowohl hinsichtlich der Benennung als auch der Funktionalität -- stets um herstellerspezifische (und oft sogar chip-spezifische) Implementierungen. +] else if lang == "zh" [ + 虽然中断和异常在很多方面都不一样,但是它们的操作和使用几乎是一样的,且它们也能被同一个中断控制器处理。然而异常是由Cortex-M微架构定义的,中断在命名和功能上总是由特定厂商(经常甚至是芯片)实现的。 ] else { todo } #if lang == "en" [ @@ -44,6 +47,11 @@ - Im Allgemeinen muss die Ursache für die Auslösung des Interrupts beseitigt werden, um zu verhindern, dass der Interrupt-Handler endlos erneut aufgerufen wird. +] else if lang == "zh" [ + 中断提供了更多的灵活性,当尝试用一种高级的方法使用它们时,我们需要对这种灵活性进行解释。但我们将不会在这本书里涵盖这些内容,最好把下面的东西记在心里: + - 中断有可以编程的优先级,其决定了它们的处理函数的执行顺序。 + - 中断能嵌套且抢占,i.e.~一个中断处理函数的执行可以被其它更高优先级的中断打断。 + - 通常需要清除掉导致中断被触发的原因,避免无限地再次进入中断处理函数。 ] else { todo } #if lang == "en" [ @@ -58,6 +66,11 @@ - Legen Sie die gewünschte Priorität des Interrupt-Handlers im Interrupt-Controller fest. - Aktivieren Sie den Interrupt-Handler im Interrupt-Controller. +] else if lang == "zh" [ + 运行时的初始化步骤总是相同的: + - 设置外设在想要的事件发生时产生中断请求 + - 在中断控制器中设置需要的中断处理函数的优先级 + - 在中断控制器中使能中断处理函数 ] else { todo } #let ln_int = link("https://docs.rs/cortex-m-rt-macros/0.1.5/cortex_m_rt_macros/attr.interrupt.html")[`interrupt`] @@ -74,6 +87,10 @@ wenn das Device-Feature aktiviert ist. Es ist allerdings nicht für die direkte Verwendung vorgesehen; eine solche würde zu einem Kompilierfehler führen. +] else if lang == "zh" [ + #todoupd("zh") + 与异常相似,`cortex-m-rt` + crate提供了一个#ln_int;属性去声明中断处理函数。可用的中断(及它们在中断向量表中的位置)通常由`svd2rust`从一个SVD描述文件自动地生成。 ] else { todo } #if lang == "en" [ @@ -93,29 +110,33 @@ automatisch aus einer SVD-Datei generiert. ] else { todo } -#if lang == "en" [ - ```rust - use lm3s6965::interrupt; // Re-exported attribute from the device crate - - // Interrupt handler for the Timer2 interrupt - #[interrupt] - fn TIMER2A() { - // .. - // Clear reason for the generated interrupt request - } - ``` -] else if lang == "de" [ - ```rust - use lm3s6965::interrupt; // Aus dem Device-Crate zurueck-exportiertes Attribut +#raw(block: true, lang: "rust", +"rust +use lm3s6965::interrupt; // " + if lang == "en" { + "Re-exported attribute from the device crate" + } else if lang == "de" { + "Aus dem Device-Crate zurueck-exportiertes Attribut" + } else { todos } + " - // Interrupt-Handler fuer den Timer2-Interrupt - #[interrupt] - fn TIMER2A() { - // .. - // Eindeutiger Grund fuer die generierte Interrupt-Anforderung - } - ``` -] else { todo } +// " + if lang == "en" { + "Interrupt handler for the Timer2 interrupt" + } else if lang == "de" { + "Interrupt-Handler fuer den Timer2-Interrupt" + } else if lang == "zh" { + "Timer2中断的中断处理函数" + } else { todos } + " +#[interrupt] +fn TIMER2A() { + // .. + // " + if lang == "en" { + "Clear reason for the generated interrupt request" + } else if lang == "de" { + "Eindeutiger Grund fuer die generierte Interrupt-Anforderung" + } else if lang == "zh" { + "清除生成中断请求的原因" + } else { todos } + " +} +") #if lang == "en" [ Interrupt handlers look like plain functions (except for the lack of @@ -130,6 +151,8 @@ Firmware aufgerufen werden. Es ist allerdings möglich, Interrupt-Anforderungen per Software zu erzeugen, um einen Sprung zum Interrupt-Handler auszulösen. +] else if lang == "zh" [ + 中断处理函数和异常处理函数一样看起来像是普通的函数(除了没有入参)。然而由于特殊的调用规定,它不能被固件的其它部分直接调用。然而,可以在软件中生成中断请求,转移到中断处理函数中。 ] else { todo } #if lang == "en" [ @@ -139,6 +162,9 @@ Ähnlich wie bei Exception-Handlern ist es auch hier möglich, `static mut`-Variablen innerhalb der Interrupt-Handler zu deklarieren, um den Zustand auf _sichere_ Weise zu speichern. +] else if lang == "zh" [ + 与异常处理函数一样,也能在中断处理函数中声明`static mut`变量且保持 + _safe_ 状态。 ] else { todo } #raw(block: true, lang: "rust", @@ -150,6 +176,8 @@ fn TIMER2A() { "`COUNT` has type `&mut u32` and it's safe to use" } else if lang == "de" { "`COUNT` hat den Typ `&mut u32` und ist sicher zu verwenden." + } else if lang == "zh" { + "`COUNT` 的类型是 `&mut u32` 且它用起来安全" } else { todos } + " *COUNT += 1; } @@ -162,4 +190,6 @@ fn TIMER2A() { Für eine detailliertere Beschreibung der hier veranschaulichten Mechanismen konsultieren Sie bitte den #link()[Abschnitt zu Ausnahmen]. +] else if lang == "zh" [ + 关于这里所说的机制的更多细节描述,请参考#link()[异常章节]。 ] else { todo } diff --git a/typ/unsorted/speed-vs-size.typ b/typ/unsorted/speed-vs-size.typ index d0b65a20..f02dc0f4 100644 --- a/typ/unsorted/speed-vs-size.typ +++ b/typ/unsorted/speed-vs-size.typ @@ -274,6 +274,7 @@ opt-level = \"z\" ] else { todo } = #(if lang == "en" [Optimize for size] + else if lang == "de" [Nach Größe optimieren] else { todo }) #if lang == "en" [ From 18cc5067d6bc066ad2c9e0f0576fac388a9f912b Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Fri, 7 Aug 2026 16:16:35 +0300 Subject: [PATCH 06/11] finish Chinese by @XxChang --- typ/appendix/glossary.typ | 32 ++- typ/book.typ | 3 +- typ/c-tips/index.typ | 185 +++++++++++++- typ/collections/index.typ | 95 +++++++ typ/concurrency/index.typ | 238 ++++++++++++++++-- typ/design-patterns/hal/checklist.typ | 14 ++ typ/design-patterns/hal/gpio.typ | 47 +++- typ/design-patterns/hal/index.typ | 10 + typ/design-patterns/hal/interoperability.typ | 29 ++- typ/design-patterns/hal/naming.typ | 4 + typ/design-patterns/hal/predictability.typ | 12 + typ/design-patterns/index.typ | 3 + typ/interoperability/c-with-rust.typ | 93 ++++++- typ/interoperability/index.typ | 30 +++ typ/interoperability/rust-with-c.typ | 54 +++- typ/peripherals/a-first-attempt.typ | 51 +++- typ/peripherals/borrowck.typ | 16 ++ typ/peripherals/index.typ | 45 +++- typ/peripherals/singletons.typ | 70 +++++- typ/portability/index.typ | 71 +++++- typ/start/panicking.typ | 61 ++++- typ/start/qemu.typ | 177 ++++++++++++- typ/start/registers.typ | 131 +++++++++- typ/start/semihosting.typ | 36 ++- typ/static-guarantees/design-contracts.typ | 65 +++++ typ/static-guarantees/gpio-table.typ | 17 ++ typ/static-guarantees/index.typ | 9 + typ/static-guarantees/state-machines.typ | 43 ++++ .../typestate-programming.typ | 18 +- .../zero-cost-abstractions.typ | 18 ++ typ/unsorted/index.typ | 1 + typ/unsorted/math.typ | 14 +- typ/unsorted/speed-vs-size.typ | 80 +++++- 33 files changed, 1694 insertions(+), 78 deletions(-) diff --git a/typ/appendix/glossary.typ b/typ/appendix/glossary.typ index 88ab88ec..a0da7097 100644 --- a/typ/appendix/glossary.typ +++ b/typ/appendix/glossary.typ @@ -2,6 +2,7 @@ #h1(if lang == "en" [Appendix A: Glossary] else if lang == "de" [Anhang A: Glossar] + else if lang == "zh" [附录A: 词汇表] else { todo }) #if lang == "en" [ @@ -13,8 +14,9 @@ Das Embedded-Ökosystem ist geprägt von einer Vielzahl unterschiedlicher Protokolle, Hardwarekomponenten und herstellerspezifischer Elemente, für die jeweils eigene Begriffe und Abkürzungen verwendet werden. Dieses - Glossar soll diese auflisten und Hinweise zum besseren Verständnis - geben. + Glossar soll diese auflisten und Hinweise zum besseren Verständnis geben. +] else if lang == "zh" [ + 嵌入式生态系统中充满了不同的协议,硬件组件,还有许多与生产商相关的东西,它们都使用自己的缩写和项目名。这个词汇表尝试列出它们以便更好理解它们。 ] else { todo } *BSP* @@ -34,6 +36,10 @@ #link()[Seite zu den im Speicher abgebildeten Registern] oder für einen umfassenderen Überblick sehen Sie sich #link(url_bspvideo)[dieses Video] an. +] else if lang == "zh" [ + 板级支持的Crate(Board Support + Crate)提供为某个特定板子配置的高级接口。它通常依赖一个#link()[HAL] + crate 。在#link()[存储映射的寄存器那页]有更多细节的描述或者看#link(url_bspvideo)[这个视频]来获取一个更广泛的概述。 ] else { todo } *FPU* @@ -44,6 +50,8 @@ ] else if lang == "de" [ Gleitkommaeinheit. Ein „Mathematikprozessor", der ausschließlich Operationen mit Gleitkommazahlen ausführt. +] else if lang == "zh" [ + 浮点单元(Floating-Point Unit)。一个只运行在浮点数上的'数学处理器'。 ] else { todo } *HAL* @@ -67,6 +75,11 @@ Eine ausführlichere Beschreibung finden Sie auf der #link()[Seite zu den im Speicher abgebildeten Registern] oder für einen umfassenderen Überblick in #link(url_hal_video)[diesem Video]. +] else if lang == "zh" [ + 硬件抽象层(Hardware Abstraction Layer) + crate为微控制器的功能和外设提供一个开发者友好的接口。它通常在#link()[Peripheral Access Crate (PAC)]之上被实现。它可能也会实现来自#ln_hal + crate的traits + 。在#link()[存储映射的寄存器那页]上有更多的细节或者看#link(url_hal_video)[这个视频]获取一个更广泛的概述。 ] else { todo } *I2C* @@ -81,6 +94,9 @@ um ein Protokoll für die Hardware-Kommunikation innerhalb eines einzelnen integrierten Schaltkreises. Weitere Informationen finden Sie #link(wiki_i2c)[hier]. +] else if lang == "zh" [ + 有时又被称为 `I²C` 或者 Intere-IC + 。它是一种用于在单个集成电路中进行硬件通信的协议。看#link(wiki_i2c)[这里]来获取更多细节。 ] else { todo } *PAC* @@ -108,6 +124,9 @@ #link()[Seite zu den im Speicher abgebildeten Registern] oder für einen umfassenderen Überblick in #link(url_pacvideo)[diesem Video]. +] else if lang == "zh" [ + 一个外设访问 Crate (Peripheral Access + Crate)提供了对一个微控制器的外设的访问。它是一个底层的crates且通常从提供的#link()[SVD]被直接生成,经常使用#ln_svd2rust。#link()[硬件抽象层]应该依赖这个crate。在#link()[存储映射的寄存器那页]有更细节的描述或者看#link(url_pacvideo)[这个视频]获取一个更广泛的概述。 ] else { todo } *SPI* @@ -119,6 +138,8 @@ ] else if lang == "de" [ Serial Peripheral Interface. Weitere Informationen finden Sie #link(wiki_spi)[hier]. +] else if lang == "zh" [ + 串行外设接口。看#link(wiki_spi)[这里]获取更多信息。 ] else { todo } *SVD* @@ -133,6 +154,9 @@ Programmierersicht eines Mikrocontrollers zu beschreiben. Weitere Informationen hierzu finden Sie auf #link(url_svd)[der ARM CMSIS-Dokumentationsseite]. +] else if lang == "zh" [ + 系统视图描述文件(System View + Description)是一个XML文件格式,以程序员视角来描述一个微控制器设备。你能在#link(url_svd)[the ARM CMSIS documentation site]上获取更多信息。 ] else { todo } *UART* @@ -144,6 +168,8 @@ ] else if lang == "de" [ Universeller asynchroner Empfänger-Sender. Weitere Informationen finden Sie #link(wiki_uart)[hier]. +] else if lang == "zh" [ + 通用异步收发器。看#link(wiki_uart)[这里]获取更多信息。 ] else { todo } *USART* @@ -155,4 +181,6 @@ ] else if lang == "de" [ Universeller synchroner und asynchroner Empfänger-Sender. Weitere Informationen finden Sie #link(wiki_usart)[hier]. +] else if lang == "zh" [ + 通用同步异步收发器。看#link(wiki_usart)[这里]获取更多信息。 ] else { todo } diff --git a/typ/book.typ b/typ/book.typ index cba464d0..e58ba0b4 100644 --- a/typ/book.typ +++ b/typ/book.typ @@ -60,8 +60,7 @@ ), ), ), - "start/index": - ( + "start/index": ( content: include "start/index.typ", title: if lang == "en" [Getting started] else if lang == "de" [Erste Schritte] diff --git a/typ/c-tips/index.typ b/typ/c-tips/index.typ index 8c215606..a236bb1e 100644 --- a/typ/c-tips/index.typ +++ b/typ/c-tips/index.typ @@ -2,6 +2,7 @@ #h1(if lang == "en" [Tips for embedded C developers] else if lang == "de" [Tipps für Embedded-C-Entwickler] + else if lang == "zh" [给嵌入式C开发者的贴士] else { todo }) #if lang == "en" [ @@ -14,10 +15,13 @@ Embedded-C-Entwickler nützlich sein können, die mit der Programmierung in Rust beginnen möchten. Dabei wird insbesondere hervorgehoben, wie sich Dinge, die man aus C bereits kennt, in Rust unterscheiden. +] else if lang == "zh" [ + 这个章节收集了可能对于刚开始编写Rust的,有经验的嵌入式C开发者来说,有用的各种各样的贴士。它将解释你在C中可能已经用到的那些东西与Rust中的有何不同。 ] else { todo } == #(if lang == "en" [Preprocessor] else if lang == "de" [Präprozessor] + else if lang == "zh" [预处理器] else { todo }) #if lang == "en" [ @@ -33,6 +37,11 @@ - Zur Kompilierzeit festgelegte Array-Größen und Berechnungen - Makros zur Vereinfachung häufiger Muster (um den Overhead von Funktionsaufrufen zu vermeiden) +] else if lang == "zh" [ + 在嵌入式C中,为了各种各样的目的使用预处理器是很常见的,比如: + - 使用`#ifdef`编译时选择代码块 + - 编译时的数组大小和计算 + - 用来简化常见的模式的宏(避免调用函数的开销) ] else { todo } #if lang == "en" [ @@ -44,10 +53,13 @@ Anwendungsfälle anders gelöst werden. Im weiteren Verlauf dieses Abschnitts behandeln wir verschiedene Alternativen zur Verwendung des Präprozessors. +] else if lang == "zh" [ + 在Rust中没有预处理器,所以许多案例有不同的处理方法。本章节剩下的部分,我们将介绍各种替代预处理器的方法。 ] else { todo } === #(if lang == "en" [Compile-Time Code Selection] else if lang == "de" [Code-Auswahl zur Kompilierzeit] + else if lang == "zh" [编译时的代码选择] else { todo }) #let url_features = "https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section" @@ -68,6 +80,9 @@ man ein Crate als Abhängigkeit angibt; sie sind zudem additiv: Wenn irgendein Crate im Abhängigkeitsbaum ein Feature für ein anderes Crate aktiviert, ist dieses Feature für alle Nutzer jenes Crates aktiviert. +] else if lang == "zh" [ + Rust中最接近`#ifdef ... #endif`的是#link(url_features)[Cargo features]。这些比C预处理器更正式一点: + 每个crate显式列举的,所有可能的features只能是关了的或者打开了的。当你把一个crate列为依赖项时,Features被打开,且是可添加的:如果你依赖树中的任何crate为另一个crate打开了一个feature,那么这个feature将为所有使用那个crate的用户而打开。 ] else { todo } #if lang == "en" [ @@ -81,6 +96,11 @@ zusätzliche Kompilierzeit beanspruchen oder eine umfangreiche Tabelle von Konstanten definieren, die du gerne vermeiden möchtest. Du könntest für jede Komponente in deiner `Cargo.toml` ein Cargo-Feature definieren: +] else if lang == "zh" [ + 比如,你可能有一个crate,其提供一个信号处理的基本类型库(library of + signal processing + primitives)。每个基本类型可能带来一些额外的时间去编译大量的常量,你想要避开这些常量。你可以为你的`Cargo.toml`中每个组件声明一个Cargo + feature。 ] else { todo } ```toml @@ -95,6 +115,8 @@ IIR = [] ] else if lang == "de" [ Verwenden Sie dann in Ihrem Code `#[cfg(feature="FIR")]`, um zu steuern, was einbezogen wird. +] else if lang == "zh" [ + 然后,在你的代码中,使用`#[cfg(feature="FIR")]`去控制要包含什么东西。 ] else { todo } #raw(block: true, lang: "rust", @@ -102,6 +124,8 @@ IIR = [] "In your top-level lib.rs" } else if lang == "de" { "In deiner lib.rs auf oberster Ebene" + } else if lang == "zh" { + "在你的顶层的lib.rs中" } else { todos } + " #[cfg(feature=\"FIR\")] @@ -118,6 +142,9 @@ pub mod iir; Sie können Codeblöcke analog dazu nur dann einfügen, wenn eine Funktion _nicht_ aktiviert ist oder wenn eine beliebige Kombination von Funktionen aktiviert oder deaktiviert ist. +] else if lang == "zh" [ + 同样地,你可以控制,只有当某个feature _没有_ + 被打开时,包含代码块,或者某些features的组合被打开或者被关闭时。 ] else { todo } #let url_conditional = "https://doc.rust-lang.org/reference/conditional-compilation.html" @@ -134,6 +161,10 @@ pub mod iir; Informationen zur bedingten Kompilierung finden Sie im Kapitel #link(url_conditional)[Bedingte Kompilierung] der Rust-Referenz. +] else if lang == "zh" [ + 另外,Rust提供了许多可以使用的自动配置了的条件,比如`target_arch`用来选择不同的代码所基于的架构。对于条件编译的全部细节,可以参看the + Rust + reference的#link(url_conditional)[conditional compilation]章节。 ] else { todo } #if lang == "en" [ @@ -153,10 +184,14 @@ pub mod iir; mehr benötigten Code („Dead Code") entfernen zu lassen: Dies ist für Sie und Ihre Nutzer einfacher, und der Compiler leistet im Allgemeinen gute Arbeit beim Entfernen von ungenutztem Code. +] else if lang == "zh" [ + 条件编译将只应用于下一条语句或者块。如果一个块不能在现在的作用域中被使用,那么`cfg`属性将需要被多次使用。值得注意的是大多数时间,仅是包含所有的代码而让编译器在优化时去删除死代码(dead + code)更好,通常,在移除不使用的代码方面的工作,编译器做得很好。 ] else { todo } === #(if lang == "en" [Compile-Time Sizes and Computation] else if lang == "de" [Größen und Berechnungen zur Kompilierzeit] + else if lang == "zh" [编译时大小和计算] else { todo }) #if lang == "en" [ @@ -170,6 +205,8 @@ pub mod iir; einsetzen lassen, wo Konstanten erforderlich sind, etwa bei der Größe von Arrays. Dies lässt sich mit den zuvor genannten Funktionen kombinieren, zum Beispiel: +] else if lang == "zh" [ + Rust支持`const fn`,`const fn`是在编译时可以被计算的函数,因此可以被用在需要常量的地方,比如在数组的大小中。这个能与上述的features一起使用,比如: ] else { todo } ```rust @@ -194,10 +231,14 @@ static BUF: [u32; array_size()] = [0u32; array_size()]; verfügbare Funktionsumfang ist zum Zeitpunkt der Erstellung dieses Textes stark eingeschränkt; es ist jedoch zu erwarten, dass die in einer `const fn` zulässigen Operationen in künftigen Rust-Versionen erweitert werden. +] else if lang == "zh" [ + 这些对于stable版本的Rust来说是新的特性,从1.31开始引入,因此文档依然很少。在写这篇文章的时候`const fn`可用的功能也非常有限; + 在未来的Rust release版本中,我们可以期望`const fn`将带来更多的功能。 ] else { todo } === #(if lang == "en" [Macros] else if lang == "de" [Makros] + else if lang == "zh" [宏] else { todo }) #let url_macros = "https://doc.rust-lang.org/book/ch19-06-macros.html" @@ -222,6 +263,11 @@ static BUF: [u32; array_size()] = [0u32; array_size()]; expandiert werden. Prozedurale Makros sind komplexer, ermöglichen jedoch äußerst leistungsfähige Erweiterungen der Sprache Rust: Sie können beliebige Rust-Syntax in neue Rust-Syntax umwandeln. +] else if lang == "zh" [ + Rust提供一个极度强大的#link(url_macros)[宏系统]。虽然C预处理器几乎直接在你的源代码之上进行操作,但是Rust宏系统可以在一个更高的级别上操作。存在两种Rust宏: + _声明宏_ 和 _过程宏_ 。前者更简单也最常见; + 它们看起来像是函数调用,且能扩展成一个完整的表达式,语句,项,或者模式。过程宏更复杂但是却能让Rust更强大: + 它们可以把任一条Rust语法变成一个新的Rust语法。 ] else { todo } #if lang == "en" [ @@ -242,6 +288,8 @@ static BUF: [u32; array_size()] = [0u32; array_size()]; expandieren müssen; bestimmte Anwendungsfälle von C-Präprozessor-Makros funktionieren daher nicht -- etwa Makros, die nur einen Teil eines Variablennamens oder unvollständige Listenelemente erzeugen. +] else if lang == "zh" [ + 通常,你可能想知道在那些使用一个C预处理器宏的地方,能否使用一个声明宏做同样的工作。你可以在crate中定义它们,且在你的crate中轻松使用它们或者导出给其他人用。但是请注意,因为它们必须扩展成完整的表达式,语句,项或者模式,因此C预处理器宏的某些用例没法用,比如可以扩展成一个变量名的一部分的宏或者可以把列表中的项扩展成不完整的集合的宏。 ] else { todo } #let url_inline = "https://doc.rust-lang.org/reference/attributes.html#inline-attribute" @@ -264,6 +312,12 @@ static BUF: [u32; array_size()] = [0u32; array_size()]; Vorsicht geboten ist: Der Compiler inlined Funktionen aus demselben Crate ohnehin automatisch, wenn dies sinnvoll ist; ein erzwungenes Inlining in ungeeigneten Fällen kann die Leistung sogar verschlechtern. +] else if lang == "zh" [ + 和Cargo + features一样,值得考虑下你是否真的需要宏。在一些例子中一个常规的函数更容易被理解,它也能被内联成和一个和宏一样的代码。`#[inline]`和`#[inline(always)]` + #link(url_inline)[attributes] + 能让你更深入控制这个过程,这里也要小心 - + 编译器会从同一个crate的恰当的地方自动地内联函数,因此不恰当地强迫它内联函数实际可能会导致性能下降。 ] else { todo } #if lang == "en" [ @@ -274,9 +328,12 @@ static BUF: [u32; array_size()] = [0u32; array_size()]; Eine Erläuterung des gesamten Rust-Makrosystems würde den Rahmen dieser Tipps-Seite sprengen; für alle Einzelheiten sei daher auf die Rust-Dokumentation verwiesen. +] else if lang == "zh" [ + 研究完整的Rust宏系统超出了本节内容,因此我们鼓励你去查阅Rust文档了解完整的细节。 ] else { todo } == #(if lang in ("en", "de") [Build System] + else if lang == "zh" [编译系统] else { todo }) #let url_build_scripts = "https://doc.rust-lang.org/cargo/reference/build-scripts.html" @@ -296,6 +353,9 @@ static BUF: [u32; array_size()] = [0u32; array_size()]; stellt Cargo #link(url_build_scripts)[`build.rs`-Skripte] bereit. Dabei handelt es sich um Rust-Skripte, die bei Bedarf mit dem Cargo-Build-System interagieren können. +] else if lang == "zh" [ + 大多数Rust crates使用Cargo编译 + (即使这不是必须的)。这解决了传统编译系统带来的许多难题。然而,你可能希望自定义编译过程。为了实现这个目的,Cargo提供了#link(url_build_scripts)[`build.rs`脚本]。它们是可以根据需要与Cargo编译系统进行交互的Rust脚本。 ] else { todo } #if lang == "en" [ @@ -315,6 +375,13 @@ static BUF: [u32; array_size()] = [0u32; array_size()]; Funktionen oder anderer Logik. - die Cargo-Build-Konfiguration ändern - Zusätzliche statische Bibliotheken für den Linkvorgang hinzufügen +] else if lang == "zh" [ + 与编译脚本有关的常见用例包括: + - 提供编译时信息,比如静态嵌入编译日期或者Git commit + hash进你的可执行文件中 + - 根据被选择的features或者其它逻辑在编译时生成链接脚本 + - 改变Cargo的编译配置 + - 添加额外的静态链接库以进行链接 ] else { todo } #if lang == "en" [ @@ -326,10 +393,13 @@ static BUF: [u32; array_size()] = [0u32; array_size()]; üblicherweise für Aufgaben wie die automatische Erstellung von Binärdateien aus den Build-Objekten oder die Ausgabe von Build-Informationen verwendet hat. +] else if lang == "zh" [ + 现在还不支持post-build脚本,通常将它用于像是从编译的对象自动生生成二进制文件或者打印编译信息这类任务中。 ] else { todo } === #(if lang == "en" [Cross-Compiling] else if lang == "de" [Cross-Kompilierung] + else if lang == "zh" [交叉编译] else { todo }) #if lang == "en" [ @@ -341,6 +411,9 @@ static BUF: [u32; array_size()] = [0u32; array_size()]; Cross-Kompilierung. In den meisten Fällen genügt es, Cargo die Option `--target thumbv6m-none-eabi` mitzugeben und die entsprechende ausführbare Datei unter `target/thumbv6m-none-eabi/debug/myapp` zu finden. +] else if lang == "zh" [ + 为你的编译系统使用Cargo也能简化交叉编译。在大多数例子里,告诉Cargo + `--target thumbv6m-none-eabi`就行了,可以在`target/thumbv6m-none-eabi/debug/myapp`中找到一个合适的可执行文件。 ] else { todo } #if lang == "en" [ @@ -354,10 +427,13 @@ static BUF: [u32; array_size()] = [0u32; array_size()]; Plattformen lässt sich #link("https://github.com/japaric/xargo")[Xargo] als Ersatz für Cargo verwenden, da es `libcore` automatisch für Sie erstellt. +] else if lang == "zh" [ + 对于那些并不是Rust原生支持的平台,将需要自己为那个目标平台编译`libcore`。遇到这样的平台,#link("https://github.com/japaric/xargo")[Xargo]可以作为Cargo的替代来使用,它可以自动地为你编译`libcore`。 ] else { todo } == #(if lang == "en" [Iterators vs Array Access] else if lang == "de" [Iteratoren vs.~Array-Zugriff] + else if lang == "zh" [迭代器与数组访问] else { todo }) #if lang == "en" [ @@ -365,6 +441,8 @@ static BUF: [u32; array_size()] = [0u32; array_size()]; ] else if lang == "de" [ In C sind Sie es wahrscheinlich gewohnt, direkt über den Index auf Arrays zuzugreifen: +] else if lang == "zh" [ + 在C中,你可能习惯于通过索引直接访问数组: ] else { todo } ```c @@ -388,12 +466,17 @@ for(i=0; i)[Nebenläufigkeit]). +] else if lang == "zh" [ + 在你的代码中直接使用这些函数是很少见的,因为它们通常由更高级的库封装起来为你提供服务。对于存储映射的外设,提供外设访问的crates将自动实现volatile访问,而对于并发的基本类型,存在更好的抽象可用。(看#link()[并发章节]) ] else { todo } == #(if lang == "en" [Packed and Aligned Types] else if lang == "de" [Gepackte und ausgerichtete Datentypen] + else if lang == "zh" [填充和对齐类型] else { todo }) #if lang == "en" [ @@ -661,8 +798,9 @@ fn driver() { In der Embedded-Programmierung mit C ist es üblich, dem Compiler vorzugeben, dass eine Variable eine bestimmte Ausrichtung (Alignment) aufweisen oder eine Struktur „gepackt" (packed) statt ausgerichtet sein - muss -- meist, um spezifische Hardware- oder Protokollanforderungen zu - erfüllen. + muss -- meist, um spezifische Hardware- oder Protokollanforderungen zu erfüllen. +] else if lang == "zh" [ + 在嵌入式C中,告诉编译器一个变量必须遵守某个对齐或者一个结构体必须被填充而不是对齐,是很常见的行为,通常是为了满足特定的硬件或者协议要求。 ] else { todo } #if lang == "en" [ @@ -678,6 +816,8 @@ fn driver() { Hardware oder C interagiert. Der Compiler kann die Reihenfolge der Strukturmitglieder ändern oder Füllbytes (Padding) einfügen, und das Verhalten kann sich in zukünftigen Rust-Versionen ändern. +] else if lang == "zh" [ + 在Rust中,这由一个结构体或者联合体上的`repr`属性来控制。默认的表示(representation)不保障布局,因此不应该被用于与硬件或者C互用的代码。编译器可能会对结构体成员重新排序或者插入填充,且这种行为可能在未来的Rust版本中改变。 ] else { todo } #raw(block: true, lang: "rust", @@ -698,6 +838,9 @@ fn main() { } else if lang == "de" { "Die Reihenfolge der Noten wurde auf x, z, y geaendert, um die Packdichte zu // verbessern." + } else if lang == "zh" { + "0x7ffecb3511d0 0x7ffecb3511d4 0x7ffecb3511d2 +// 注意为了改进填充,顺序已经被变成了x, z, y" } else { todos } + " ") @@ -705,6 +848,8 @@ fn main() { To ensure layouts that are interoperable with C, use `repr(C)`: ] else if lang == "de" [ Um Layouts zu gewährleisten, die mit C interoperabel sind, verwenden Sie `repr(C)`: +] else if lang == "zh" [ + 使用`repr(C)`可以确保布局可以与C互用。 ] else { todo } #raw(block: true, lang: "rust", @@ -729,6 +874,10 @@ fn main() { // Zeit nicht. // `z` ist auf Zwei-Byte-Grenzen ausgerichtet, sodass sich zwischen `y` und `z` // ein Padding-Byte befindet." + } else if lang == "zh" { + "0x7fffd0d84c60 0x7fffd0d84c62 0x7fffd0d84c64 +// 顺序被保留了,布局将不会随着时间而改变 +// `z`是两个字节对齐,因此在`y`和`z`之间填充了一个字节。" } else { todos } + " ") @@ -736,6 +885,8 @@ fn main() { To ensure a packed representation, use `repr(packed)`: ] else if lang == "de" [ Um eine kompakte Darstellung zu gewährleisten, verwenden Sie `repr(packed)`: +] else if lang == "zh" [ + 使用`repr(packed)`去确保表示(representation)被填充了: ] else { todo } #raw(block: true, lang: "rust", @@ -757,6 +908,9 @@ fn main() { // Adressen der Struct-Felder zu ueberpruefen, verwenden wir // `std::ptr::addr_of!()`, um einen Rohzeiger (Raw Pointer) zu erhalten, // anstatt einfach `&v.x` auszugeben." + } else if lang == "zh" { + "引用必须总是对齐的,因此为了检查结构体字段的地址,我们使用 + // `std::ptr::addr_of!()`去获取一个裸指针而不仅是打印`&v.x`" } else { todos } + " let px = std::ptr::addr_of!(v.x); let py = std::ptr::addr_of!(v.y); @@ -770,6 +924,9 @@ fn main() { } else if lang == "de" { "Zwischen `y` und `z` wurde kein Padding eingefuegt, daher ist `z` nun // nicht ausgerichtet." + } else if lang == "zh" { + "0x7ffd33598490 0x7ffd33598492 0x7ffd33598493 +// 在`y`和`z`没有填充被插入,因此现在`z`没有被对齐。" } else { todos } + " ") @@ -778,6 +935,8 @@ fn main() { ] else if lang == "de" [ Beachten Sie, dass die Verwendung von `repr(packed)` die Ausrichtung des Typs ebenfalls auf `1` setzt. +] else if lang == "zh" [ + 注意使用`repr(packed)`也会将类型的对齐设置成`1` 。 ] else { todo } #if lang == "en" [ @@ -787,6 +946,8 @@ fn main() { Um eine bestimmte Ausrichtung festzulegen, verwenden Sie schließlich `repr(align(n))`, wobei `n` die Anzahl der auszurichtenden Bytes ist (und eine Zweierpotenz sein muss). +] else if lang == "zh" [ + 最后,为了指定一个特定的对齐,可以使用`repr(align(n))`,`n`是要对齐的字节数(必须是2的幂): ] else { todo } #raw(block: true, lang: "rust", @@ -813,6 +974,9 @@ fn main() { } else if lang == "de" { "Die beiden Instanzen `u` und `v` wurden an 4096-Byte-Grenzen ausgerichtet, // was an den `000` am Ende ihrer Adressen erkennbar ist." + } else if lang == "zh" { + "`u`和`v`两个实例已经被放置在4096字节的对齐上。 +// 它们地址结尾处的`000`证明了这件事。" } else { todos } + " ") @@ -829,6 +993,8 @@ fn main() { da `repr(packed)` die Ausrichtung auf `1` setzt. Ebenso ist es nicht zulässig, dass ein `repr(packed)`-Typ einen `repr(align(n))`-Typ enthält. +] else if lang == "zh" [ + 注意我们可以结合`repr(C)`和`repr(align(n))`来获取一个对齐的c兼容的布局。不允许将`repr(align(n))`和`repr(packed)`一起使用,因为`repr(packed)`将对齐设置为`1`。也不允许一个`repr(packed)`类型包含一个`repr(align(n))`类型。 ] else { todo } #let url_layout = "https://doc.rust-lang.org/reference/type-layout.html" @@ -839,10 +1005,14 @@ fn main() { Weitere Details zu Typ-Layouts finden Sie im Kapitel #link(url_layout)[Typ-Layout] der Rust-Referenz. +] else if lang == "zh" [ + 关于类型布局更多的细节,参考the Rust + Reference的#link("https://doc.rust-lang.org/reference/type-layout.html")[type layout]章节。 ] else { todo } == #(if lang == "en" [Other Resources] else if lang == "de" [Weitere Ressourcen] + else if lang == "zh" [其它资源] else { todo }) #let url_faq = "https://docs.rust-embedded.org/faq.html" @@ -862,4 +1032,11 @@ fn main() { - #link(url_faq)[Die Rust-Embedded-FAQs] - #link(url_for_c)[Rust-Pointer für C-Programmierer] - #link(url_pointers)[Früher habe ich Pointer verwendet -- und jetzt?] +] else if lang == "zh" [ + - 这本书中: + - #link()[使用C的Rust] + - #link()[使用Rust的C] + - #link(url_faq)[The Rust Embedded FAQs] + - #link(url_for_c)[Rust Pointers for C Programmers] + - #link(url_pointers)[I used to use pointers - now what?] ] else { todo } diff --git a/typ/collections/index.typ b/typ/collections/index.typ index 65f419df..a86932b5 100644 --- a/typ/collections/index.typ +++ b/typ/collections/index.typ @@ -2,6 +2,7 @@ #h1(if lang == "en" [Collections] else if lang == "de" [Sammlungen (Collections)] + else if lang == "zh" [集合] else { todo }) #let ln_vec = link("https://doc.rust-lang.org/std/vec/struct.Vec.html")[`Vec`] @@ -20,6 +21,10 @@ #ln_vec, #ln_string, #ln_map usw. Alle in `std` implementierten Collections nutzen einen globalen dynamischen Speicherverwalter (den sogenannten Heap). +] else if lang == "zh" [ + 最后,还希望在程序里使用动态数据结构(也称为集合)。`std` + 提供了一组常见的集合: + #ln_vec,#ln_string,#ln_map,等等。所有这些在`std`中被实现的集合都使用一个全局动态分配器(也称为堆)。 ] else { todo } #if lang == "en" [ @@ -30,6 +35,9 @@ Da `core` definitionsgemäß frei von Speicherzuweisungen ist, stehen diese Implementierungen dort nicht zur Verfügung; sie sind jedoch im `alloc`-Crate zu finden, das mit dem Compiler ausgeliefert wird. +] else if lang == "zh" [ + 因为`core`的定义中是没有内存分配的,所以这些实现在`core`中是没有的,但是我们可以在编译器附带的`alloc` + crate中找到。 ] else { todo } #let ln_heapless = link("https://crates.io/crates/heapless")[`heapless`] @@ -42,6 +50,9 @@ Implementierung nicht Ihre einzige Option. Sie können auch Sammlungen mit _fester Kapazität_ verwenden; eine solche Implementierung findet sich im #ln_heapless;-Crate. +] else if lang == "zh" [ + 如果需要集合,一个基于堆分配的实现不是唯一的选择。也可以使用 _fixed capacity_ 集合; 其实现可以在 + #ln_heapless crate中被找到。 ] else { todo } #if lang == "en" [ @@ -49,10 +60,13 @@ ] else if lang == "de" [ In diesem Abschnitt werden wir diese beiden Implementierungen untersuchen und vergleichen. +] else if lang == "zh" [ + 在这部分,我们将研究和比较这两个实现。 ] else { todo } == #(if lang == "en" [Using `alloc`] else if lang == "de" [Verwendung von `alloc`] + else if lang == "zh" [使用 `alloc`] else { todo }) #if lang == "en" [ @@ -63,6 +77,9 @@ Die `alloc`-Crate ist in der Standard-Rust-Distribution enthalten. Um das Crate zu importieren, können Sie es direkt per `use` einbinden, _ohne_ es als Abhängigkeit in Ihrer `Cargo.toml`-Datei zu deklarieren. +] else if lang == "zh" [ + `alloc` crate与标准的Rust发行版在一起。你可以直接 `use` + 导入这个crate,而不需要在`Cargo.toml`文件中把它声明为一个依赖。 ] else { todo } ```rust @@ -84,6 +101,9 @@ use alloc::vec::Vec; Attribut `global_allocator` verwenden, um den globalen Allocator zu deklarieren, den Ihr Programm einsetzen soll. Der gewählte Allocator muss zwingend das Trait #ln_alloc implementieren. +] else if lang == "zh" [ + 为了能使用集合,首先需要使用`global_allocator`属性去声明程序将使用的全局分配器。它要求选择的分配器实现了#link("https://doc.rust-lang.org/core/alloc/trait.GlobalAlloc.html")[`GlobalAlloc`] + trait 。 ] else { todo } #if lang == "en" [ @@ -97,6 +117,10 @@ use alloc::vec::Vec; Bump-Pointer-Allokator und verwenden diesen als globalen Allokator. Wir empfehlen Ihnen jedoch _dringend_, in Ihrem Programm stattdessen einen praxiserprobten Allokator von crates.io zu verwenden. +] else if lang == "zh" [ + 为了完整性和尽可能保持本节的自包含性,我们将实现一个简单线性指针分配器且用它作为全局分配器。然而,我们 + _强烈地_ + 建议你在你的程序中使用一个来自crates.io的久经战斗测试的分配器而不是这个分配器。 ] else { todo } #raw(block: true, lang: "rust", @@ -104,6 +128,8 @@ use alloc::vec::Vec; "Bump pointer allocator implementation" } else if lang == "de" { "Implementierung eines Bump-Pointer-Allocators" + } else if lang == "zh" { + "线性指针分配器实现" } else { todos } + " use core::alloc::{GlobalAlloc, Layout}; @@ -116,6 +142,8 @@ use cortex_m::interrupt; "Bump pointer allocator for *single* core systems" } else if lang == "de" { "Bump-Pointer-Allocator fuer Systeme mit *einem* Rechenkern" + } else if lang == "zh" { + "用于单核系统的线性指针分配器" } else { todos } + " struct BumpPointerAlloc { head: UnsafeCell, @@ -132,6 +160,8 @@ unsafe impl GlobalAlloc for BumpPointerAlloc { } else if lang == "de" { "`interrupt::free` ist ein kritischer Abschnitt, der die sichere // Verwendung unseres Allocators aus Interrupts heraus ermoeglicht." + } else if lang == "zh" { + "`interrupt::free`是一个临界区,临界区让我们的分配器在中断中用起来安全" } else { todos } + " interrupt::free(|_| { let head = self.head.get(); @@ -143,6 +173,8 @@ unsafe impl GlobalAlloc for BumpPointerAlloc { "move start up to the next alignment boundary" } else if lang == "de" { "Verschiebe den Startpunkt zur naechsten Ausrichtungsgrenze." + } else if lang == "zh" { + "将start移至下一个对齐边界。" } else { todos } + " let start = (*head + align - 1) & align_mask; @@ -151,6 +183,8 @@ unsafe impl GlobalAlloc for BumpPointerAlloc { "a null pointer signal an Out Of Memory condition" } else if lang == "de" { "ein Nullzeiger signalisiert einen „Out of Memory“-Zustand" + } else if lang == "zh" { + "一个空指针通知内存不足" } else { todos } + " ptr::null_mut() } else { @@ -165,6 +199,8 @@ unsafe impl GlobalAlloc for BumpPointerAlloc { "this allocator never deallocates memory" } else if lang == "de" { "Dieser Allokator gibt niemals Speicher frei." + } else if lang == "zh" { + "这个分配器从不释放内存" } else { todos } + " } } @@ -178,6 +214,10 @@ unsafe impl GlobalAlloc for BumpPointerAlloc { // HINWEIS: Der Benutzer muss sicherstellen, dass der Speicherbereich // `[0x2000_0100, 0x2000_0200]` nicht von anderen Teilen des Programms // verwendet wird." + } else if lang == "zh" { + "全局内存分配器的声明 +// 注意 用户必须确保`[0x2000_0100, 0x2000_0200]`内存区域 +// 没有被程序的其它部分使用" } else { todos } + " #[global_allocator] static HEAP: BumpPointerAlloc = BumpPointerAlloc { @@ -195,6 +235,8 @@ static HEAP: BumpPointerAlloc = BumpPointerAlloc { festlegen, wie mit „Out of Memory" (OOM)-Fehlern umgegangen wird; hierfür wird das _instabile_ Attribut `alloc_error_handler` verwendet. +] else if lang == "zh" [ + 除了选择一个全局分配器,用户也必须要定义如何使用_不稳定的_`alloc_error_handler`属性来处理内存溢出错误。 ] else { todo } ```rust @@ -215,6 +257,8 @@ fn on_oom(_layout: Layout) -> ! { ] else if lang == "de" [ Sobald alles vorhanden ist, kann der Benutzer endlich die Sammlungen in „alloc" verwenden. +] else if lang == "zh" [ + 一旦一切都完成了,用户最后就可以在`alloc`中使用集合。 ] else { todo } ```rust @@ -238,10 +282,14 @@ fn main() -> ! { Wenn Sie die Sammlungen aus dem `std`-Crate bereits verwendet haben, werden Ihnen diese vertraut vorkommen, da es sich um exakt dieselbe Implementierung handelt. +] else if lang == "zh" [ + 如果你已经使用了`std` + crate中的集合,那么这些对你来说将非常熟悉,因为他们的实现一样。 ] else { todo } == #(if lang == "en" [Using `heapless`] else if lang == "de" [Verwendung von `heapless`] + else if lang == "zh" [使用 `heapless`] else { todo }) #if lang == "en" [ @@ -251,6 +299,8 @@ fn main() -> ! { `heapless` erfordert keine Einrichtung, da seine Datenstrukturen nicht von einem globalen Speicher-Allocator abhängen. Binde die Datenstrukturen einfach per `use` ein und instanziiere sie: +] else if lang == "zh" [ + `heapless`无需设置,因为它的集合不依赖一个全局内存分配器。只是`use`它的集合然后实例化它们: ] else { todo } ```rust @@ -273,6 +323,8 @@ fn main() -> ! { ] else if lang == "de" [ Sie werden zwei Unterschiede zwischen diesen Sammlungen und denen in `alloc` feststellen. +] else if lang == "zh" [ + 你会注意到这些集合与`alloc`中的集合有两个不一样的地方。 ] else { todo } #let ln_typenum = link("https://crates.io/crates/typenum")[`typenum`] @@ -291,6 +343,9 @@ fn main() -> ! { Elementen hat, d.~h. der Vektor kann maximal 8 Elemente aufnehmen. Dies wird durch das `u8` (siehe #ln_typenum) in der Typsignatur angezeigt. +] else if lang == "zh" [ + 第一,你必须预先声明集合的容量。`heapless`集合从来不会发生重分配且具有固定的容量;这个容量是集合的类型签名的一部分。在这个例子里,我们已经声明了`xs`的容量为8个元素,也就是说,这个vector最多只能有八个元素。这是通过类型签名中的`U8` + (看#ln_typenum)来指定的。 ] else { todo } #if lang == "en" [ @@ -309,6 +364,8 @@ fn main() -> ! { erfolgreich war oder nicht. Im Gegensatz dazu passen `alloc`-Datenstrukturen ihre Kapazität durch eine erneute Speicherzuweisung auf dem Heap an. +] else if lang == "zh" [ + 第二,`push`方法和另外一些方法返回的是一个`Result`。因为`heapless`集合有一个固定的容量,所以所有插入的操作都可能会失败。通过返回一个`Result`,API反应了这个问题,指出操作是否成功还是失败。相反,`alloc`集合自己将会在堆上重新分配去增加它的容量。 ] else { todo } #if lang == "en" [ @@ -323,10 +380,13 @@ fn main() -> ! { `let x = heapless::Vec::new();` die Datenstruktur auf dem Stack anlegt; es ist jedoch auch möglich, sie in einer `static`-Variablen oder sogar auf dem Heap (`Box>`) zu speichern. +] else if lang == "zh" [ + 自v0.4.x版本起,所有的`heapless`集合将所有的元素内联地存储起来了。这意味着像是`let x = heapless::Vec::new()`这样的一个操作将会在栈上分配集合,但是它也能够在一个`static`变量上分配集合,或者甚至在堆上(`Box>`)。 ] else { todo } == #(if lang == "en" [Trade-offs] else if lang == "de" [Abwägungen] + else if lang == "zh" [取舍] else { todo }) #if lang == "en" [ @@ -336,10 +396,13 @@ fn main() -> ! { Berücksichtigen Sie diese Punkte, wenn Sie zwischen auf dem Heap alloziierten, verschiebbaren Sammlungen und Sammlungen mit fester Kapazität wählen. +] else if lang == "zh" [ + 当在堆分配的可重定位的集合和固定容量的集合间进行选择的时候,记住这些内容。 ] else { todo } === #(if lang == "en" [Out Of Memory and error handling] else if lang == "de" [„Out of Memory" und Fehlerbehandlung] + else if lang == "zh" [内存溢出和错误处理] else { todo }) #if lang == "en" [ @@ -359,6 +422,10 @@ fn main() -> ! { `alloc`-Collections bieten `try_reserve`-Methoden an, mit denen sich potenzielle OOM-Situationen beim Vergrößern der Collection überprüfen lassen; man muss diese Methoden jedoch aktiv einsetzen. +] else if lang == "zh" [ + 使用堆分配,内存溢出总是有可能出现的且会发生在任何一个集合需要增长的地方: + 比如,所有的 `alloc::Vec.push` 调用会潜在地产生一个OOM(Out of + Memory)条件。因此一些操作可能会_隐式地_失败。一些`alloc`集合暴露了`try_reserve`方法,可以当增加集合时让你检查潜在的OOM条件,但是你需要主动地使用它们。 ] else { todo } #if lang == "en" [ @@ -374,6 +441,8 @@ fn main() -> ! { die Kapazität einer Collection erschöpft ist. Das bedeutet, man muss _alle_ `Result`-Werte behandeln, die von Methoden wie `Vec.push` zurückgegeben werden. +] else if lang == "zh" [ + 如果你只使用`heapless`集合,而不使用内存分配器,那么一个OOM条件不可能出现。反而,你必须逐个处理容量不足的集合。也就是必须处理_所有_的`Result`,`Result`由像是`Vec.push`这样的方法返回的。 ] else { todo } #if lang == "en" [ @@ -391,10 +460,13 @@ fn main() -> ! { kann beispielsweise selbst `vec.reserve(1)` ein OOM auslösen, wenn der Allocator nahezu erschöpft ist, weil eine andere Collection Speicher verloren hat (Speicherlecks sind auch in „Safe Rust" möglich). +] else if lang == "zh" [ + 与在所有由`heapless::Vec.push`返回的`Result`上调用`unwrap`相比,OOM错误更难调试,因为错误被发现的位置可能与导致问题的位置_不_一致。比如,甚至如果分配器接近消耗完`vec.reserve(1)`都能触发一个OOM,因为一些其它的集合正在泄露内存(内存泄露在安全的Rust是会发生的)。 ] else { todo } === #(if lang == "en" [Memory usage] else if lang == "de" [Speichernutzung] + else if lang == "zh" [内存使用] else { todo }) #if lang == "en" [ @@ -418,6 +490,9 @@ fn main() -> ! { reduziert wird. Zudem muss der Allocator unter Umständen mit Speicherfragmentierung umgehen, was den _scheinbaren_ Speicherverbrauch erhöhen kann. +] else if lang == "zh" [ + 推理堆分配集合的内存使用是很难的因为长期使用的集合的大小会在运行时改变。一些操作可能隐式地重分配集合,增加了它的内存使用,一些集合暴露的方法,像是`shrink_to_fit`,会潜在地减少集合使用的内存 -- + 最终,它由分配器去决定是否确定减小内存的分配或者不。另外,分配器可能不得不处理内存碎片,它会_明显_增加内存的使用。 ] else { todo } #if lang == "en" [ @@ -430,6 +505,8 @@ fn main() -> ! { legt die meisten davon in `static`-Variablen ab und legt eine maximale Größe für den Aufruf-Stack (Call Stack) fest, so erkennt der Linker, wenn mehr Speicher beansprucht wird, als physisch verfügbar ist. +] else if lang == "zh" [ + 另一方面,如果你只使用固定容量的集合,请把大多数的数据保存在`static`变量中,并为调用栈设置一个最大尺寸,随后如果你尝试使用大于可用的物理内存的内存大小时,链接器会发现它。 ] else { todo } #let ln_z_emit = link("https://doc.rust-lang.org/beta/unstable-book/compiler-flags/emit-stack-sizes.html")[`-Z emit-stack-sizes`] @@ -444,6 +521,8 @@ fn main() -> ! { Kapazität durch das Flag #ln_z_emit erfasst; das bedeutet, dass Werkzeuge zur Analyse der Stack-Nutzung (wie #ln_stack_sizes) diese in ihre Auswertung einbeziehen. +] else if lang == "zh" [ + 另外,在栈上分配的固定容量的集合可以通过#ln_z_emit;标识来报告,其意味着用来分析栈使用的工具(像是#ln_stack_sizes)将会把在栈上分配的集合包含进它们的分析中。 ] else { todo } #if lang == "en" [ @@ -455,10 +534,13 @@ fn main() -> ! { werden, was zu niedrigeren Auslastungsgraden (dem Verhältnis zwischen der Größe der Sammlung und ihrer Kapazität) führen kann, als sie bei Sammlungen mit veränderbarer Kapazität möglich sind. +] else if lang == "zh" [ + 然而,固定容量的集合_不_能被减少,与可重定位集合所能达到的负载系数(集合的大小和它的容量之间的比值)相比,它能产生更低的负载系数。 ] else { todo } === #(if lang == "en" [Worst Case Execution Time (WCET)] else if lang == "de" [Maximale Ausführungszeit (WCET)] + else if lang == "zh" [最坏执行时间 (WCET)] else { todo }) #if lang == "en" [ @@ -469,6 +551,8 @@ fn main() -> ! { Wenn Sie zeitkritische oder Echtzeitanwendungen entwickeln, ist Ihnen die Worst-Case-Ausführungszeit (WCET) der verschiedenen Programmteile wahrscheinlich sehr wichtig. +] else if lang == "zh" [ + 如果你正在搭建时间敏感型应用或者硬实时应用,那么你可能更关心你程序的不同部分的最坏执行时间。 ] else { todo } #if lang == "en" [ @@ -485,6 +569,8 @@ fn main() -> ! { Collection ab. Daher ist es schwierig, die WCET beispielsweise der Operation `alloc::Vec.push` zu bestimmen, da sie sowohl vom verwendeten Allokator als auch von dessen Laufzeitkapazität abhängt. +] else if lang == "zh" [ + `alloc`集合能重分配,所以操作的WCET可能会增加,集合也将包括它用来重分配集合所需的时间,它取决于集合的_运行时_容量。这使得它更难去决定操作,比如`alloc::Vec.push`,的WCET,因为它依赖被使用的分配器和它的运行时容量。 ] else { todo } #if lang == "en" [ @@ -495,10 +581,13 @@ fn main() -> ! { Collections mit fester Kapazität hingegen allokieren nie Speicher neu, sodass alle Operationen eine vorhersehbare Ausführungszeit haben. Beispielsweise wird `heapless::Vec.push` in konstanter Zeit ausgeführt. +] else if lang == "zh" [ + 另一方面固定容量的集合不会重分配,因此所有的操作有个可预期的执行时间。比如,`heapless::Vec.push`以固定时间执行。 ] else { todo } === #(if lang == "en" [Ease of use] else if lang == "de" [Benutzerfreundlichkeit] + else if lang == "zh" [易用性] else { todo }) #if lang == "en" [ @@ -509,6 +598,8 @@ fn main() -> ! { Für `alloc` muss ein globaler Allocator eingerichtet werden, für `heapless` hingegen nicht. Allerdings erfordert `heapless`, dass man bei der Instanziierung jeder Collection deren Kapazität festlegt. +] else if lang == "zh" [ + `alloc`要求配置一个全局分配器而`heapless`不需要。然而,`heapless`要求你去选择你要实例化的每一个集合的容量。 ] else { todo } #if lang == "en" [ @@ -522,4 +613,8 @@ fn main() -> ! { unterscheidet sich jedoch aufgrund der expliziten Fehlerbehandlung -- manche Entwickler empfinden diese explizite Fehlerbehandlung womöglich als übertrieben oder zu umständlich. +] else if lang == "zh" [ + `alloc` API几乎为每一个Rust开发者所熟知。`heapless` API尝试模仿`alloc` + API,但是因为`heapless`的显式错误处理,它们不可能会一模一样 -- + 一些开发者可能会觉得显式的错误处理过多或太麻烦。 ] else { todo } diff --git a/typ/concurrency/index.typ b/typ/concurrency/index.typ index 8e2f909d..2418a484 100644 --- a/typ/concurrency/index.typ +++ b/typ/concurrency/index.typ @@ -2,6 +2,7 @@ #h1(if lang == "en" [Concurrency] else if lang == "de" [Nebenläufigkeit] + else if lang == "zh" [并发] else { todo }) @@ -28,6 +29,11 @@ - und in einigen Systemen Mehrkern-Mikroprozessoren, bei denen jeder Kern gleichzeitig und unabhängig voneinander einen anderen Teil Ihres Programms ausführen kann. +] else if lang == "zh" [ + 当程序的不同部分有可能会在不同的时刻被执行或者不按顺序地被执行时,那并发就出现了。在一个嵌入式环境中,这包括: + - 中断处理函数,一旦相关的中断发生时,中断处理函数就会运行, + - 不同的多线程形式,在这块,微处理器通常会在程序的不同部分间进行切换, + - 在一些多核微处理器系统中,每个核可以同时独立地运行程序的不同部分。 ] else { todo } #if lang == "en" [ @@ -41,10 +47,13 @@ können auch viele schwer zu findende und komplexe Fehler auftreten. Glücklicherweise bietet Rust eine Reihe von Abstraktionen und Sicherheitsgarantien, die uns dabei helfen, korrekten Code zu schreiben. +] else if lang == "zh" [ + 因为许多嵌入式程序需要处理中断,因此并发迟早会出现,这也是许多微妙和困难的bugs会出现的地方。幸运地是,Rust提供了许多抽象和安全保障去帮助我们写正确的代码。 ] else { todo } == #(if lang == "en" [No Concurrency] else if lang == "de" [Keine Nebenläufigkeit] + else if lang == "zh" [没有并发] else { todo }) #if lang == "en" [ @@ -61,6 +70,10 @@ die vorliegende Aufgabenstellung ideal! Typischerweise liest die Schleife Eingabewerte ein, führt Berechnungen oder Verarbeitungen durch und gibt Ergebnisse aus. +] else if lang == "zh" [ + 对于一个嵌入式程序来说最简单的并发是没有并发: + 软件由一个保持运行的main循环组成,一点中断也没有。有时候这非常适合手边的问题! + 通常你的循环将会读取一些输入,执行一些处理,且写入一些输出。 ] else { todo } ```rust @@ -85,10 +98,13 @@ fn main() { die gemeinsame Datennutzung zwischen verschiedenen Programmteilen oder die Synchronisierung des Zugriffs auf Peripheriegeräte machen. Wenn ein solch einfacher Ansatz ausreicht, kann dies eine hervorragende Lösung sein. +] else if lang == "zh" [ + 因为这里没有并发,因此不需要担心程序不同部分间的共享数据或者同步对外设的访问。如果可以使用一个简单的方法来解决问题,这种方法是个不错的选择。 ] else { todo } == #(if lang == "en" [Global Mutable Data] else if lang == "de" [Globale veränderliche Daten] + else if lang == "zh" [全局可变数据] else { todo }) #if lang == "en" [ @@ -109,6 +125,9 @@ fn main() { über _statisch reservierten_, veränderbaren Speicher verfügen müssen, auf den sowohl der Interrupt-Handler als auch der Hauptprogrammcode zugreifen können. +] else if lang == "zh" [ + 不像非嵌入式Rust,我们通常不会奢侈地在堆上分配数据,并将对该数据的引用传递到新创建的线程中。相反,我们的中断处理函数随时可能被调用,且必须知道如何访问我们正在使用的共享内存。从最底层看来,这意味着我们必须有 + _静态分配的_ 可变的内存,中断处理函数和main代码都可以引用这块内存。 ] else { todo } #let ln_staticmut = link("https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable")[`static mut`] @@ -126,6 +145,8 @@ fn main() { (Wettlaufsituation) kommen: Der Zugriff auf die Variable wird mitten im Vorgang durch einen Interrupt unterbrochen, der seinerseits ebenfalls auf diese Variable zugreift. +] else if lang == "zh" [ + 在Rust中,#ln_staticmut;这样的变量读取或者写入总是unsafe的,因为不特别关注它们的话,可能会触发一个竞态条件,对变量的访问在中途就被一个也访问那个变量的中断打断了。 ] else { todo } #if lang == "en" [ @@ -137,6 +158,8 @@ fn main() { Code verursachen kann, betrachten Sie ein eingebettetes Programm, das die steigenden Flanken eines Eingangssignals innerhalb jedes Ein-Sekunden-Intervalls zählt (einen Frequenzzähler): +] else if lang == "zh" [ + 为了举例这种行为如何在代码中导致了微妙的错误,思考一个嵌入式程序,这个程序在每一秒的周期内计数一些输入信号的上升沿(一个频率计数器): ] else { todo } #raw(block: true, lang: "rust", @@ -154,6 +177,8 @@ fn main() -> ! { } else if lang == "de" { "GEFAHR – Nicht wirklich sicher! Koennte zu Datenwettlaeufen // fuehren." + } else if lang == "zh" { + "危险 - 实际不安全! 可能导致数据竞争。" } else { todos } + " unsafe { COUNTER += 1 }; } @@ -193,10 +218,14 @@ fn timer() { ausgelöst, ginge das Zurücksetzen auf 0 nach der Rückkehr aus dem Interrupt verloren -- und wir würden für diesen Zeitraum doppelt so viele Signalwechsel zählen. +] else if lang == "zh" [ + 每秒计时器中断会把计数器设置回0。这期间,main循环连续地测量信号,且当看到从低电平到高电平的变化时,增加计数器的值。因为它是`static mut`的,我们不得不使用`unsafe`去访问`COUNTER`,意思是我们向编译器保证我们的操作不会导致任何未定义的行为。你能发现竞态条件吗?`COUNTER`上的增加并不一定是原子的 - + 事实上,在大多数嵌入式平台上,它将被分开成一个读取操作,然后是增加,然后是写回。如果中断在计数器被读取之后但是在被写回之前被激活,在中断返回后,重置回0的操作会被忽略掉 - 那期间,我们会算出两倍的转换次数。 ] else { todo } == #(if lang == "en" [Critical Sections] else if lang == "de" [Kritische Abschnitte] + else if lang == "zh" [临界区(Critical Sections)] else { todo }) #if lang == "en" [ @@ -212,6 +241,10 @@ fn timer() { `COUNTER` in der Funktion `main` in einen kritischen Abschnitt einbetten, stellen wir sicher, dass der Timer-Interrupt erst ausgelöst wird, nachdem wir das Inkrementieren von `COUNTER` abgeschlossen haben: +] else if lang == "zh" [ + 因此,关于数据竞争可以做些什么?一个简单的方法是使用 + _临界区(critical sections)_ + ,在临界区的上下文中中断被关闭了。通过把对`main`中的`COUNTER`访问封装进一个临界区,我们能确保计时器中断将不会激活,直到我们完成了增加`COUNTER`的操作: ] else { todo } #raw(block: true, lang: "rust", @@ -229,6 +262,8 @@ fn main() -> ! { } else if lang == "de" { "Ein neuer kritischer Abschnitt gewaehrleistet den // synchronisierten Zugriff auf COUNTER." + } else if lang == "zh" { + "新的临界区确保对COUNTER的同步访问" } else { todos } + " cortex_m::interrupt::free(|_| { unsafe { COUNTER += 1 }; @@ -255,6 +290,9 @@ fn timer() { kritischen Abschnitt auszuführen. Dies entspricht im Grunde dem Deaktivieren von Interrupts, der Ausführung von Code und dem anschließenden erneuten Aktivieren der Interrupts. +] else if lang == "zh" [ + 在这个例子里,我们使用 + `cortex_m::interrupt::free`,但是其它平台将会有更简单的机制在一个临界区中执行代码。它们都有一样的逻辑,关闭中断,运行一些代码,然后重新使能中断。 ] else { todo } #if lang == "en" [ @@ -268,6 +306,10 @@ fn timer() { - Das Schreiben von 0 in `COUNTER` kann nicht von einer Race-Condition betroffen sein, da wir den Wert nicht lesen. - Es wird ohnehin niemals vom `main`-Thread unterbrochen werden. +] else if lang == "zh" [ + 注意,有两个理由,不需要把一个临界区放进计时器中断中: + - 向`COUNTER`写入0不会被一个竞争影响,因为我们不需要读取它 + - 无论如何,它永远不会被`main`线程中断 ] else { todo } #if lang == "en" [ @@ -278,6 +320,9 @@ fn timer() { Wenn `COUNTER` von mehreren Interrupt-Handlern gemeinsam genutzt würde, die sich gegenseitig _unterbrechen_ könnten, müsste jeder von ihnen ebenfalls einen kritischen Abschnitt verwenden. +] else if lang == "zh" [ + 如果`COUNTER`被多个可能相互 _抢占_ + 的中断处理函数共享,那么每一个也需要一个临界区。 ] else { todo } #if lang == "en" [ @@ -300,6 +345,8 @@ fn timer() { bis zur Verarbeitung schwankt stärker). Ob dies ein Problem darstellt, hängt vom jeweiligen System ab, doch im Allgemeinen sollten wir dies vermeiden. +] else if lang == "zh" [ + 这解决了我们眼前的问题,但是我们仍然要编写许多unsafe的代码,我们需要仔细推敲这些代码,有些我们可能不需要使用临界区。因为每个临界区暂时暂停了中断处理,就会带来一些相关的成本,一些额外的代码大小,更高的中断延迟和抖动(中断可能花费很长时间去处理,等待被处理的时间变化非常大)。这是否是个问题取决于你的系统,但是通常,我们想要避免它。 ] else { todo } #if lang == "en" [ @@ -315,10 +362,14 @@ fn timer() { könnte -- selbst ohne Interrupts -- problemlos auf denselben Speicher zugreifen wie Ihr eigener Kern. Wenn Sie mehrere Rechenkerne nutzen, benötigen Sie daher leistungsfähigere Synchronisationsmechanismen. +] else if lang == "zh" [ + 值得注意的是,虽然一个临界区保障了不会发生中断,但是它在多核系统上不提供一个排他性保证(exclusivity + guarantee)!其它核可能很开心地访问与你的核一样的内存区域,即使没有中断。如果你正在使用多核,你将需要更强的同步原语(synchronisation primitives)。 ] else { todo } == #(if lang == "en" [Atomic Access] else if lang == "de" [Atomarer Zugriff] + else if lang == "zh" [原子访问] else { todo }) #if lang == "en" [ @@ -343,6 +394,10 @@ fn timer() { durchzuführen -- was meistens gelingt --, doch sollte eine Unterbrechung auftreten, wird der gesamte Vorgang automatisch erneut versucht. Diese atomaren Operationen sind auch in Mehrkernsystemen sicher. +] else if lang == "zh" [ + 在一些平台上,可以使用特定的原子指令,它保障了读取-修改-写回操作。针对Cortex-M: + `thumbv6`\(Cortex-M0,Cortex-M0+)只提供原子读取和存取指令,而`thumv7`\(Cortex-M3及以上)提供完整的比较和交换(CAS)指令。这些CAS指令可以替代过重的禁用所有中断的方法: + 我们可以尝试执行加法操作,它在大多数情况下都会成功,但是如果它被中断了它将会自动重试完整的加法操作。这些原子操作甚至在多核间也是安全的。 ] else { todo } #raw(block: true, lang: "rust", @@ -361,6 +416,8 @@ fn main() -> ! { "Use `fetch_add` to atomically add 1 to COUNTER" } else if lang == "de" { "Verwenden Sie `fetch_add`, um 1 atomar zu COUNTER zu addieren." + } else if lang == "zh" { + "使用 `fetch_add` 原子性地给 COUNTER 加一" } else { todos } + " COUNTER.fetch_add(1, Ordering::Relaxed); } @@ -374,6 +431,8 @@ fn timer() { "Use `store` to write 0 directly to COUNTER" } else if lang == "de" { "Verwenden Sie `store`, um 0 direkt in COUNTER zu schreiben." + } else if lang == "zh" { + "使用 `store` 将 0 直接写入 COUNTER" } else { todos } + " COUNTER.store(0, Ordering::Relaxed) } @@ -391,6 +450,8 @@ fn timer() { Hauptthread aus sicher geändert werden, ohne Interrupts zu deaktivieren. Wenn möglich, ist dies die bessere Lösung -- wird aber möglicherweise von Ihrer Plattform nicht unterstützt. +] else if lang == "zh" [ + 这时,`COUNTER`是一个safe的`static`变量。多亏了`AtomicUsize`类型,不需要禁用中断,`COUNTER`能从中断处理函数和main线程被安全地修改。当可以这么做时,这是一个更好的解决方案 - 然而平台上可能不支持这么做。 ] else { todo } #let ln_ordering = link("https://doc.rust-lang.org/core/sync/atomic/enum.Ordering.html")[`Ordering`] @@ -414,6 +475,10 @@ fn timer() { Je nachdem, wofür Sie atomare Operationen verwenden, benötigen Sie dies möglicherweise nicht. Die genauen Details des atomaren Modells sind komplex und werden am besten an anderer Stelle beschrieben. +] else if lang == "zh" [ + 关于#ln_ordering;的提醒: + 它可能影响编译器和硬件如何重新排序指令,也会影响缓存可见性。假设目标是个单核平台,在这个案例里`Relaxed`是充足的和最有效的选择。更严格的排序将导致编译器在原子操作周围产生内存屏障(Memory + Barriers);取决于你做什么原子操作,你可能需要或者不需要这个排序!原子模型的精确细节是复杂的,最好写在其它地方。 ] else { todo } #let url_atomics = "https://doc.rust-lang.org/nomicon/atomics.html" @@ -423,10 +488,13 @@ fn timer() { ] else if lang == "de" [ Weitere Informationen zu atomaren Operationen und deren Reihenfolge finden Sie im #link(url_atomics)[nomicon]. +] else if lang == "zh" [ + 关于原子操作和排序的更多细节,可以看这里#link(url_atomics)[nomicon]。 ] else { todo } == #(if lang == "en" [Abstractions, Send, and Sync] else if lang == "de" [Abstraktionen, Send und Sync] + else if lang == "zh" [抽象,Send和Sync] else { todo }) #if lang == "en" [ @@ -437,6 +505,8 @@ fn timer() { Keine der oben genannten Lösungen ist wirklich zufriedenstellend. Sie erfordern unsichere Blöcke, die sehr sorgfältig geprüft werden müssen und nicht ergonomisch sind. In Rust geht das doch bestimmt besser! +] else if lang == "zh" [ + 上面的解决方案都不是特别令人满意。它们需要`unsafe`块,`unsafe`块必须要被十分小心地检查且不符合人体工程学。确实,我们在Rust中可以做得更好! ] else { todo } #if lang == "en" [ @@ -449,6 +519,8 @@ fn timer() { die überall im Code sicher verwendet werden kann. In diesem Beispiel verwenden wir den Zähler für kritische Abschnitte, aber mit atomaren Operationen ließe sich etwas sehr Ähnliches realisieren. +] else if lang == "zh" [ + 我们可以把我们的计数器抽象进一个安全的接口中,它可以在代码的其它地方被安全地使用。在这个例子里,我们将使用临界区的(cirtical-section)计数器,但是你可以用原子操作做一些非常类似的事情。 ] else { todo } #raw(block: true, lang: "rust", @@ -465,6 +537,10 @@ use cortex_m::interrupt; // der „Interior Mutability“ in Rust. Dank dieses Konzepts können wir `COUNTER` // als `static` statt als `static mut` definieren und dennoch den Zaehlerwert // veraendern." + } else if lang == "zh" { + "我们的计数器只是包围UnsafeCell的一个封装,它是Rust中内部可变性 +// (interior mutability)的关键。通过使用内部可变性,我们能让COUNTER +// 变成`static`而不是`static mut`,但是仍能改变它的计数器值。" } else { todos } + " struct CSCounter(UnsafeCell); @@ -481,6 +557,9 @@ impl CSCounter { // wir, dass wir uns innerhalb einer `CriticalSection` befinden; daher // koennen wir bedenkenlos diesen `unsafe`-Block verwenden (der für den // Aufruf von `UnsafeCell::get` erforderlich ist)." + } else if lang == "zh" { + "通过要求一个CriticalSection被传递进来,我们知道我们肯定正在一个 + // CriticalSection中操作,且因此可以自信地使用这个unsafe块(调用UnsafeCell::get的前提)。" } else { todos } + " unsafe { *self.0.get() = 0 }; } @@ -495,6 +574,8 @@ impl CSCounter { } else if lang == "de" { "Erforderlich, um ein statisches CSCounter zu ermoeglichen. Siehe Erlaeuterung // unten." + } else if lang == "zh" { + "允许静态CSCounter的前提。看下面的解释。" } else { todos } + " unsafe impl Sync for CSCounter {} @@ -504,6 +585,9 @@ unsafe impl Sync for CSCounter {} } else if lang == "de" { "COUNTER ist nicht mehr `mut`, da es „Interior Mutability“ verwendet; daher // sind fuer den Zugriff auch keine `unsafe`-Bloecke mehr erforderlich." + } else if lang == "zh" { + "COUNTER不再是`mut`的因为它使用内部可变性; +// 因此访问它也不再需要unsafe块。" } else { todos } + " static COUNTER: CSCounter = CS_COUNTER_INIT; @@ -516,6 +600,8 @@ fn main() -> ! { if state && !last_state { // " + if lang in ("en", "de") { "No unsafe here!" + } else if lang == "zh" { + "这里不用unsafe!" } else { todos } + " interrupt::free(|cs| COUNTER.increment(cs)); } @@ -533,6 +619,9 @@ fn timer() { "Wir muessen hier tatsaechlich einen kritischen Abschnitt betreten, nur um // ein gueltiges CS-Token zu erhalten, auch wenn wir wissen, dass kein // anderer Interrupt diesen unterbrechen koennte." + } else if lang == "zh" { + "这里我们需要进入一个临界区,只是为了传递进一个有效的cs token,尽管我们知道 + // 没有其它中断可以抢占这个中断。 " } else { todos } + " interrupt::free(|cs| COUNTER.reset(cs)); @@ -543,6 +632,9 @@ fn timer() { "Wir koennten „unsicheren“ Code (unsafe code) verwenden, um eine // gefaelschte CriticalSection zu erzeugen, falls wir das wirklich wollten, // und so den Overhead vermeiden:" + } else if lang == "zh" { + "如果我们真的需要,我们可以使用unsafe代码去生成一个假CriticalSection, + // 避免开销:" } else { todos } + " // let cs = unsafe { interrupt::CriticalSection::new() }; } @@ -556,6 +648,8 @@ fn timer() { Wir haben unseren `unsafe`-Code in unsere sorgfältig entworfene Abstraktion verlagert; nun enthält unser Anwendungscode keine `unsafe`-Blöcke mehr. +] else if lang == "zh" [ + 我们已经把我们的`unsafe`代码移进了精心安排的抽象中,现在我们的应用代码不包含任何`unsafe`块。 ] else { todo } #if lang == "en" [ @@ -577,6 +671,11 @@ fn timer() { entsteht also kein Laufzeit-Overhead durch `cs`. Hätten wir mehrere Zähler, könnten diese alle dasselbe `cs` verwenden, ohne dass mehrere verschachtelte kritische Abschnitte erforderlich wären. +] else if lang == "zh" [ + 这个设计要求应用传递一个`CriticalSection` token进来: + 这些tokens仅由`interrupt::free`安全地产生,因此通过要求传递进一个`CriticalSection` + token,我们确保我们正在一个临界区中操作,不用自己动手锁起来。这个保障由编译器静态地提供: + 这将不会带来任何与`cs`有关的运行时消耗。如果我们有多个计数器,它们都可以被指定同一个`cs`,而不用要求多个嵌套的临界区。 ] else { todo } #let url_sendsync = "https://doc.rust-lang.org/nomicon/send-and-sync.html" @@ -599,6 +698,11 @@ fn timer() { vom Anwendungscode getrennten Thread ausgeführt werden; daher müssen Variablen, auf die sowohl ein Interrupt als auch der Hauptcode zugreift, `Sync` implementieren. +] else if lang == "zh" [ + 这也带来了Rust中关于并发的一个重要主题: + #link(url_sendsync)[`Send` and `Sync`] + traits。总结一下Rust + book,当一个类型能够安全地被移动到另一个线程,它是Send,当一个类型能被安全地在多个线程间共享的时候,它是Sync。在一个嵌入式上下文中,我们认为中断是在应用代码的一个独立线程中执行的,因此在一个中断和main代码中都能被访问的变量必须是Sync。 ] else { todo } #let ln_unsafecell = link("https://doc.rust-lang.org/core/cell/struct.UnsafeCell.html")[`UnsafeCell`] @@ -613,8 +717,10 @@ fn timer() { Compiler für dich abgeleitet. Da `CSCounter` jedoch eine #ln_unsafecell enthält, ist der Typ nicht `Sync`\; folglich konnten wir keinen `static CSCounter` definieren, denn `static`-Variablen _müssen_ - `Sync` sein, da von mehreren Threads aus auf sie zugegriffen werden - kann. + `Sync` sein, da von mehreren Threads aus auf sie zugegriffen werden kann. +] else if lang == "zh" [ + 在Rust中的大多数类型,这两个traits都会由你的编译器为你自动地产生。然而,因为`CSCounter`包含了一个#ln_unsafecell,它不是Sync,因此我们不能使用一个`static CSCounter`: + `static` 变量 _必须_ 是Sync,因此它们能被多个线程访问。 ] else { todo } #if lang == "en" [ @@ -630,10 +736,15 @@ fn timer() { Verwendung kritischer Abschnitte ist dies nur auf Single-Core-Plattformen sicher; bei mehreren Kernen wäre ein deutlich höherer Aufwand erforderlich, um die Sicherheit zu gewährleisten. +] else if lang == "zh" [ + 为了告诉编译器我们已经注意到`CSCounter`事实上在线程间共享是安全的,我们显式地实现了Sync + trait。与之前使用的临界区一样,这只在单核平台上是安全的: + 对于多核,你需要做更多的事来确保安全。 ] else { todo } == #(if lang == "en" [Mutexes] else if lang == "de" [Mutexe] + else if lang == "zh" [互斥量(Mutexs)] else { todo }) #if lang == "en" [ @@ -643,6 +754,8 @@ fn timer() { Wir haben eine nützliche, speziell auf unser Zählerproblem zugeschnittene Abstraktion entwickelt; es gibt jedoch viele gängige Abstraktionen für Nebenläufigkeit. +] else if lang == "zh" [ + 我们已经为我们的计数器问题创造了一个有用的抽象,但是关于并发这里还存在许多通用的抽象。 ] else { todo } #let ln_drop = link("https://doc.rust-lang.org/core/ops/trait.Drop.html")[`Drop`] @@ -672,6 +785,15 @@ fn timer() { #ln_drop;-Traits implementieren; so ist sichergestellt, dass der Mutex immer freigegeben wird, sobald er den Gültigkeitsbereich (Scope) verlässt. +] else if lang == "zh" [ + 一个互斥量(mutex),互斥(mutual exclusion)的缩写,就是这样的一个 + _同步原语_ + 。这些构造确保了对一个变量的排他访问,比如我们的计数器。一个线程会尝试 + _lock_ (或者 _acquire_) + 互斥量,或者当互斥量不能被锁住时返回一个错误。当线程持有锁时,它有权访问被保护的数据,当线程工作完成了,它 + _unlocks_ (或者 _releases_) + 互斥量,允许其它线程锁住它。在Rust中,我们通常使用#ln_drop + trait实现unlock去确保当互斥量超出作用域时它总是被释放。 ] else { todo } #if lang == "en" [ @@ -691,6 +813,12 @@ fn timer() { freigeben, da die Ausführung im Interrupt-Handler verbleibt). Ein Deadlock gilt nicht als „unsicher" (unsafe): Er ist selbst in sicherem Rust möglich. +] else if lang == "zh" [ + 将中断处理函数与一个互斥量一起使用可能有点棘手: + 阻塞中断处理函数通常是不可接受的,如果它阻塞等待main线程去释放一个锁,那将是一场灾难。因为我们会 + _死锁_ + (因为执行停留在中断处理函数中,主线程将永远不会释放锁)。死锁被认为是不安全的: + 即使在安全的Rust中这也是可能发生的。 ] else { todo } #if lang == "en" [ @@ -706,6 +834,9 @@ fn timer() { Sektion der Dauer der Sperre entspricht, ist sichergestellt, dass wir exklusiven Zugriff auf die gekapselte Variable haben, ohne den Sperrstatus des Mutex explizit nachverfolgen zu müssen. +] else if lang == "zh" [ + 为了完全避免这个行为,我们可以实现一个要求临界区的互斥量去锁住,就像我们的计数器例子一样。临界区的存在时间必须和锁存在的时间一样长,我们能确保我们对被封装的变量有排他式访问,甚至不需要跟踪互斥量的 + lock/unlock 状态。 ] else { todo } #if lang == "en" [ @@ -714,6 +845,9 @@ fn timer() { ] else if lang == "de" [ Genau dies wird uns bereits durch die `cortex_m`-Crate abgenommen! Wir hätten unseren Zähler auch unter Verwendung dieser Crate implementieren können: +] else if lang == "zh" [ + 实际上我们在 `cortex_m` + crate中就是这么做的!我们可以用它来写入我们的计数器: ] else { todo } #raw(block: true, lang: "rust", @@ -743,6 +877,8 @@ fn timer() { } else if lang == "de" { "Wir muessen hier noch einen kritischen Abschnitt betreten, um die // Mutex-Bedingung zu erfuellen." + } else if lang == "zh" { + "这里我们仍然需要进入一个临界区去满足互斥量。" } else { todos } + " interrupt::free(|cs| COUNTER.borrow(cs).set(0)); } @@ -775,6 +911,10 @@ fn timer() { Verwendung sicher, allerdings lässt sie sich nicht direkt in einer `static`-Variablen einsetzen, da `static`-Elemente die Eigenschaft `Sync` erfüllen müssen. +] else if lang == "zh" [ + 我们现在使用了#link("https://doc.rust-lang.org/core/cell/struct.Cell.html")[`Cell`],它与它的兄弟`RefCell`一起被用于提供safe的内部可变性。我们已经见过`UnsafeCell`了,在Rust中它是内部可变性的底层: + 它允许你去获得对某个值的多个可变引用,但是只能与不安全的代码一起工作。一个`Cell`像一个`UnsafeCell`一样但是它提供了一个安全的接口: + 它只允许拷贝现在的值或者替换它,不允许获取一个引用,因此它不是Sync,它不能被在线程间共享。这些限制意味着它用起来是safe的,但是我们不能直接将它用于`static`变量因为一个`static`必须是Sync。 ] else { todo } #if lang == "en" [ @@ -788,6 +928,9 @@ fn timer() { Dies ist sicher möglich, da der Zugriff auf den Inhalt nur während eines kritischen Abschnitts gewährt wird. Dadurch erhalten wir einen sicheren Zähler ohne jeglichen unsicheren Code! +] else if lang == "zh" [ + 因此为什么上面的例子可以工作?`Mutex`对于任何是Send的`T`实现了Sync - + 比如一个`Cell`。因为它只能在临界区对它的内容进行访问,所以它这么做是safe的。因此我们可以即使没有一点unsafe的代码我们也能获取一个safe的计数器! ] else { todo } #if lang == "en" [ @@ -799,12 +942,14 @@ fn timer() { Das ist ideal für einfache Typen wie den `u32` unseres Zählers. Was aber ist mit komplexeren Typen, die nicht `Copy` sind? Ein sehr häufiges Beispiel in eingebetteten Systemen ist eine Peripheriestruktur, die in - der Regel nicht `Copy` ist. Für diesen Fall können wir `RefCell` - verwenden. + der Regel nicht `Copy` ist. Für diesen Fall können wir `RefCell` verwenden. +] else if lang == "zh" [ + 对于我们的简单类型,像是我们的计数器的`u32`来说是很棒的,但是对于更复杂的不能拷贝的类型呢?在一个嵌入式上下文中一个极度常见的例子是一个外设结构体,通常它们不是Copy。针对那种情况,我们可以使用`RefCell`。 ] else { todo } == #(if lang == "en" [Sharing Peripherals] else if lang == "de" [Gemeinsame Nutzung von Peripheriegeräten] + else if lang == "zh" [共享外设] else { todo }) #if lang == "en" [ @@ -821,6 +966,8 @@ fn timer() { die Sicherheit, erschwert jedoch den gleichzeitigen Zugriff auf eine Peripheriekomponente sowohl aus dem Haupt-Thread als auch aus einem Interrupt-Handler heraus. +] else if lang == "zh" [ + 使用`svd2rust`生成的设备crates和相似的抽象,通过强制要求同时只能存在一个外设结构体的实例,提供了对外设的安全的访问。这个确保了安全性,但是使得它很难从main线程和一个中断处理函数一起访问一个外设。 ] else { todo } #let ln_refcell = link("https://doc.rust-lang.org/core/cell/struct.RefCell.html")[`RefCell`] @@ -834,13 +981,15 @@ fn timer() { ] else if lang == "de" [ Um den Zugriff auf Peripheriekomponenten sicher zu teilen, können wir den bereits bekannten `Mutex` verwenden. Zudem benötigen wir - #link("https://doc.rust-lang.org/core/cell/struct.RefCell.html")[`RefCell`]\; + #ln_refcell;; dieses stellt mittels einer Laufzeitprüfung sicher, dass jeweils nur eine einzige Referenz auf eine Peripheriekomponente ausgegeben wird. Dies ist zwar mit einem höheren Overhead verbunden als bei einem einfachen `Cell`, doch da wir Referenzen anstatt Kopien weitergeben, müssen wir sicherstellen, dass zu jedem Zeitpunkt nur eine einzige Referenz existiert. +] else if lang == "zh" [ + 为了安全地共享对外设的访问,我们能使用我们之前看到的`Mutex`。我们也将需要使用#ln_refcell,它使用一个运行时检查去确保对一个外设每次只有一个引用被给出。这个比纯`Cell`消耗更多,但是因为我们正给出引用而不是拷贝,我们必须确保每次只有一个引用存在。 ] else { todo } #if lang == "en" [ @@ -853,8 +1002,9 @@ fn timer() { Peripheriekomponente in die gemeinsam genutzte Variable übertragen werden kann, nachdem sie im Hauptcode initialisiert wurde. Hierfür können wir den Typ `Option` verwenden, der zunächst mit `None` - initialisiert und später auf die Instanz der Peripheriekomponente - gesetzt wird. + initialisiert und später auf die Instanz der Peripheriekomponente gesetzt wird. +] else if lang == "zh" [ + 最终,我们也必须考虑在main代码中初始化外设后,如何将外设移到共享变量中。为了做这个,我们使用`Option`类型,初始成`None`,之后设置成外设的实例。 ] else { todo } #raw(block: true, lang: "rust", @@ -875,6 +1025,9 @@ fn main() -> ! { "Die Peripherie-Singletons abrufen und konfigurieren. // Dieses Beispiel stammt aus einer mit svd2rust erstellten Crate, die // meisten Crates für eingebettete Geräte sind jedoch ähnlich." + } else if lang == "zh" { + "获得外设的单例并配置它。这个例子来自一个svd2rust生成的crate, + // 但是大多数的嵌入式设备crates都相似。" } else { todos } + " let dp = stm32f405::Peripherals::take().unwrap(); let gpioa = &dp.GPIOA; @@ -886,6 +1039,8 @@ fn main() -> ! { "Eine Art Konfigurationsfunktion. // Gehen wir davon aus, dass es PA0 als Eingang und PA1 als Ausgang // konfiguriert." + } else if lang == "zh" { + "某个配置函数。假设它把PA0设置成一个输入和把PA1设置成一个输出。" } else { todos } + " configure_gpio(gpioa); @@ -900,6 +1055,8 @@ fn main() -> ! { } else if lang == "de" { "Wir koennen `gpioa` oder `dp.GPIOA` nicht mehr verwenden, sondern // muessen stattdessen ueber den Mutex darauf zugreifen." + } else if lang == "zh" { + "把GPIOA存进互斥量中,移动它。" } else { todos } + " // access it via the mutex. @@ -913,6 +1070,11 @@ fn main() -> ! { // Andernfalls koennte der Interrupt ausgeloest werden, solange MY_GPIO // noch None enthaelt, was – wie implementiert (mit `unwrap()`) – zu einer // Panic fuehren wuerde." + } else if lang == "zh" { + "我可以不再用`gpioa`或者`dp.GPIOA`,反而必须通过互斥量访问它。 + + // 请注意,只有在设置MY_GPIO后才能使能中断: 要不然当MY_GPIO还是包含None的时候, + // 中断可能会发生,然后像上面写的那样操作(使用`unwrap()`),它将发生运行时恐慌。" } else { todos } + " set_timer_1hz(); let mut last_state = false; @@ -921,6 +1083,8 @@ fn main() -> ! { "We'll now read state as a digital input, via the mutex" } else if lang == "de" { "Wir lesen den Status nun als digitalen Eingang ueber den Mutex aus." + } else if lang == "zh" { + "我们现在将通过互斥量,读取其作为数字输入时的状态。" } else { todos } + " let state = interrupt::free(|cs| { let gpioa = MY_GPIO.borrow(cs).borrow(); @@ -933,6 +1097,8 @@ fn main() -> ! { } else if lang == "de" { "Setze PA1 auf High, wenn an PA0 eine steigende Flanke erkannt // wurde." + } else if lang == "zh" { + "如果我们在PA0上已经看到了一个上升沿,拉高PA1。" } else { todos } + " interrupt::free(|cs| { let gpioa = MY_GPIO.borrow(cs).borrow(); @@ -949,6 +1115,8 @@ fn timer() { "This time in the interrupt we'll just clear PA0." } else if lang == "de" { "Diesmal setzen wir im Interrupt lediglich PA0 zurueck." + } else if lang == "zh" { + "这次在中断中,我们将清除PA0。" } else { todos } + " interrupt::free(|cs| { // " + if lang == "en" { @@ -959,6 +1127,9 @@ fn timer() { "Wir koennen `unwrap()` verwenden, da wir wissen, dass der Interrupt // erst aktiviert wurde, nachdem `MY_GPIO` gesetzt war; andernfalls // muessten wir den moeglichen Fall eines `None`-Werts behandeln." + } else if lang == "zh" { + "我们可以使用`unwrap()` 因为我们知道直到MY_GPIO被设置后,中断都是禁用的; + // 否则我应该处理会出现一个None值的潜在可能" } else { todos } + " let gpioa = MY_GPIO.borrow(cs).borrow(); gpioa.as_ref().unwrap().odr.modify(|_, w| w.odr1().clear_bit()); @@ -971,6 +1142,8 @@ fn timer() { ] else if lang == "de" [ Das ist eine ganze Menge, die man erst einmal verarbeiten muss -- schauen wir uns also die wichtigen Zeilen genauer an. +] else if lang == "zh" [ + 这需要理解的内容很多,所以让我们把重要的内容分解一下。 ] else { todo } ```rust @@ -999,6 +1172,9 @@ static MY_GPIO: Mutex>> = eigentlichen Wert erst später hineinzubewegen. Da wir nicht statisch, sondern nur zur Laufzeit auf das Peripherie-Singleton zugreifen können, ist dieses Vorgehen erforderlich. +] else if lang == "zh" [ + 我们的共享变量现在是一个包围了一个`RefCell`的`Mutex`,`RefCell`包含一个`Option`。`Mutex`确保只在一个临界区中的时候可以访问,因此使变量变成了Sync,甚至即使一个纯`RefCell`不是Sync。`RefCell`赋予了我们引用的内部可变性,我们将需要使用我们的`GPIOA`。`Option`让我们可以初始化这个变量成空的东西,只在随后实际移动变量进来。只有在运行时,我们才能静态地访问外设单例,因此这是必须的。 + ] else { todo } ```rust @@ -1014,6 +1190,8 @@ interrupt::free(|cs| MY_GPIO.borrow(cs).replace(Some(dp.GPIOA))); Mutex aufrufen, was uns eine Referenz auf die `RefCell` liefert. Anschließend rufen wir `replace()` auf, um unseren neuen Wert in die `RefCell` zu verschieben. +] else if lang == "zh" [ + 在一个临界区中,我们可以在互斥量上调用`borrow()`,其给了我们一个指向`RefCell`的引用。然后我们调用`replace()`去移动我们的新值进来`RefCell`。 ] else { todo } ```rust @@ -1037,6 +1215,8 @@ interrupt::free(|cs| { Ausleihe; sobald die Referenz ihren Gültigkeitsbereich verlässt, wird der Status der `RefCell` aktualisiert, um anzuzeigen, dass sie nicht mehr ausgeliehen ist. +] else if lang == "zh" [ + 最终,我们用一种安全和并发的方式使用`MY_GPIO`。临界区禁止了中断像往常一样发生,让我们借用互斥量。`RefCell`然后给了我们一个`&Option`并追踪它还要借用多久 - 一旦引用超出作用域,`RefCell`将会被更新去指出引用不再被借用。 ] else { todo } #if lang == "en" [ @@ -1049,6 +1229,8 @@ interrupt::free(|cs| { umwandeln. Dieses können wir schließlich mit `unwrap()` auflösen, um das `&GPIOA` zu erhalten, das uns den Zugriff zur Modifikation der Peripherieeinheit ermöglicht. +] else if lang == "zh" [ + 因为我不能把`GPIOA`移出`&Option`,我们需要用`as_ref()`将它转换成一个`&Option<&GPIOA>`,最终我们能使用`unwrap()`获得`&GPIOA`,其让我们可以修改外设。 ] else { todo } #if lang == "en" [ @@ -1060,6 +1242,8 @@ interrupt::free(|cs| { Ressource benötigen, sollten stattdessen `borrow_mut` und `deref_mut` verwendet werden. Der folgende Code zeigt ein Beispiel unter Verwendung des TIM2-Timers. +] else if lang == "zh" [ + 如果我们需要一个共享的资源的可变引用,那么`borrow_mut`和`deref_mut`应该被使用。下面的代码展示了一个使用TIM2计时器的例子。 ] else { todo } #raw(block: true, lang: "rust", @@ -1085,6 +1269,9 @@ fn main() -> ! { "Eine Art Timer-Konfigurationsfunktion. // Angenommen, sie konfiguriert den TIM2-Timer sowie dessen NVIC-Interrupt // und startet schließlich den Timer." + } else if lang == "zh" { + "某个计时器配置函数。假设它配置了TIM2计时器和它的NVIC中断, + // 最终启动计时器。" } else { todos } + " let tim = configure_timer_interrupt(&mut cp, dp); @@ -1113,11 +1300,13 @@ fn timer() { ] else if lang == "de" [ Puh! Das ist zwar sicher, aber auch etwas unhandlich. Können wir sonst noch etwas tun? +] else if lang == "zh" [ + 呼!这是安全的,但也有点笨拙。我们还能做些什么吗? ] else { todo } == RTIC -#let url_rtic = "https://rtic.rs" +#let url_rtic = "https://github.com/rtic-rs/cortex-m-rtic" #if lang == "en" [ One alternative is the #link(url_rtic)[RTIC framework], short for Real Time Interrupt-driven Concurrency. It enforces static priorities and tracks @@ -1138,6 +1327,10 @@ fn timer() { (wie bei `RefCell`) entstünde. Dies bietet eine Reihe von Vorteilen, wie etwa die Garantie von Deadlock-Freiheit sowie einen extrem geringen Zeit- und Speicheraufwand. +] else if lang == "zh" [ + 另一个方法是使用#link(url_rtic)[RTIC框架],Real + Time Interrupt-driven + Concurrency的缩写。它强制执行静态优先级并追踪对`static mut`变量("资源")的访问去确保共享资源总是能被安全地访问,而不需要总是进入临界区和使用引用计数带来的消耗(如`RefCell`中所示)。这有许多好处,比如保证没有死锁且时间和内存的消耗极度低。 ] else { todo } #if lang == "en" [ @@ -1150,11 +1343,12 @@ fn timer() { herkömmlichen synchronen APIs auch `async`-APIs nutzen. ] else { todo } +#let url_rtic_doc = "https://rtic.rs" #if lang == "en" [ The framework also includes other features like message passing, which reduces the need for explicit shared state, and the ability to schedule tasks to run at a given time, which can be used to implement periodic - tasks. Check out #link(url_rtic)[the documentation] for more + tasks. Check out #link(url_rtic_doc)[the documentation] for more information! ] else if lang == "de" [ Das Framework umfasst zudem Funktionen wie Message Passing -- was den @@ -1162,7 +1356,10 @@ fn timer() { -- sowie die Möglichkeit, Tasks für einen bestimmten Zeitpunkt zu planen, womit sich beispielsweise periodische Aufgaben realisieren lassen. Weitere Informationen finden Sie in - #link(url_rtic)[der Dokumentation]! + #link(url_rtic_doc)[der Dokumentation]! +] else if lang == "zh" [ + 这个框架也包括了其它的特性,像是消息传递(message + passing),消息传递减少了对显式共享状态的需要,还提供了在一个给定时间调度任务去运行的功能,这功能能被用来实现周期性的任务。看下#link(url_rtic_doc)[文档]可以知道更多的信息! ] else { todo } == Embassy @@ -1210,6 +1407,7 @@ fn timer() { == #(if lang == "en" [Real Time Operating Systems] else if lang == "de" [Echtzeitbetriebssysteme] + else if lang == "zh" [实时操作系统] else { todo }) #let ln_freertos = link("https://freertos.org/")[FreeRTOS] @@ -1237,6 +1435,9 @@ fn timer() { Typischerweise stellen RTOS Mechanismen wie Mutexe und andere Synchronisationsprimitive bereit und interagieren häufig mit Hardwarefunktionen wie DMA-Controllern. +] else if lang == "zh" [ + #todoupd("zh") + 与嵌入式并发有关的另一个模型是实时操作系统(RTOS)。虽然现在在Rust中的研究较少,但是它们被广泛用于传统的嵌入式开发。开源的例子包括#ln_freertos;和#ln_chibi。这些RTOSs提供对运行多个应用线程的支持,CPU在这些线程间进行切换,切换要么发生在当线程让出控制权的时候(被称为非抢占式多任务),要么是基于一个常规计时器或者中断(抢占式多任务)。RTOS通常提供互斥量或者其它的同步原语,经常与硬件功能相互使用,比如DMA引擎。 ] else { todo } #if lang == "en" [ @@ -1246,10 +1447,14 @@ fn timer() { Zum jetzigen Zeitpunkt gibt es noch nicht viele Beispiele für Rust-RTOS, auf die man verweisen könnte; es handelt sich jedoch um ein interessantes Gebiet -- bleiben Sie also dran! +] else if lang == "zh" [ + 在撰写本文时,没有太多的Rust + RTOS示例可供参考,但这是一个有趣的领域,所以请关注这块! ] else { todo } == #(if lang == "en" [Multiple Cores] else if lang == "de" [Mehrere Kerne] + else if lang == "zh" [多个核心] else { todo }) #if lang == "en" [ @@ -1269,6 +1474,9 @@ fn timer() { Mehrkernsystem trifft dies jedoch nicht mehr zu. Stattdessen benötigen wir Synchronisierungsprimitive, die speziell für Mehrkernsysteme (auch SMP, für symmetrisches Multiprocessing, genannt) entwickelt wurden. +] else if lang == "zh" [ + 在嵌入式处理器中有两个或者多个核心很正常,其为并发添加了额外一层复杂性。所有使用临界区的例子(包括`cortex_m::interrupt::Mutex`)都假设了另一个执行的线程仅是中断线程,但是在一个多核系统中,这不再是正确的假设。反而,我们将需要为多核设计的同步原语(也被叫做SMP,symmetric + multi-processing的缩写)。 ] else { todo } #if lang == "en" [ @@ -1276,8 +1484,9 @@ fn timer() { processing system will ensure that atomicity is maintained over all cores. ] else if lang == "de" [ Diese verwenden typischerweise die bereits erwähnten atomaren Befehle, - da das Verarbeitungssystem die Atomarität über alle Kerne hinweg - gewährleistet. + da das Verarbeitungssystem die Atomarität über alle Kerne hinweg gewährleistet. +] else if lang == "zh" [ + 我们之前看到的,这些通常使用原子指令,因为处理系统将确保原子性在所有的核中都保持着。 ] else { todo } #if lang == "en" [ @@ -1285,6 +1494,7 @@ fn timer() { book, but the general patterns are the same as for the single-core case. ] else if lang == "de" [ Eine detaillierte Behandlung dieser Themen würde den Rahmen dieses - Buches sprengen, die allgemeinen Muster sind jedoch dieselben wie im - Einzelkernfall. + Buches sprengen, die allgemeinen Muster sind jedoch dieselben wie im Einzelkernfall. +] else if lang == "zh" [ + 覆盖这些主题的细节已经超出了本书的范围,但是常规的模式与单核的相似。 ] else { todo } diff --git a/typ/design-patterns/hal/checklist.typ b/typ/design-patterns/hal/checklist.typ index c2c6860e..fbdb389d 100644 --- a/typ/design-patterns/hal/checklist.typ +++ b/typ/design-patterns/hal/checklist.typ @@ -3,6 +3,7 @@ #h1(offset: whole*2, if lang == "en" [HAL Design Patterns Checklist] else if lang == "de" [Checkliste HAL-Design-Muster] + else if lang == "zh" [HAL设计检查清单] else { todo }) @@ -32,4 +33,17 @@ - ☐ Pin-Typen haben standardmäßig die Größe Null. (#link()[C-ZST-PIN]) - ☐ Pin-Typen stellen Methoden zum Löschen von Pins und Ports bereit. (#link()[C-ERASED-PIN]) - ☐ Der Pin-Zustand sollte als Typparameter kodiert werden. (#link()[C-PIN-STATE]) +] else if lang == "zh" [ +- *命名* (_crate要符合Rust命名规则_) + - ☐ crate被恰当地命名 (#link()[C-CRATE-NAME]) +- *互用性* (_crate要很好地与其它的库功能交互_) + - ☐ 封装类型提供一种析构方法 (#link()[C-FREE]) + - ☐ HALs重新导出了它们的寄存器访问crate (#link()[C-REEXPORT-PAC]) + - ☐ 类型实现了 `embedded-hal` traits (#link()[C-HAL-TRAITS]) +- *可预见性* (_crate的代码清晰可读,行为和看起来的一样_) + - ☐ 使用构造函数而不是扩展traits (#link()[C-CTOR]) +- *GPIO接口* (_GPIO接口要遵循一个公共的模式_) + - ☐ Pin类型默认是零大小类型 (#link()[C-ZST-PIN]) + - ☐ Pin类型提供擦除管脚和端口的方法 (#link()[C-ERASED-PIN]) + - ☐ Pin状态应该被编码为类型参数 (#link()[C-PIN-STATE]) ] else { todo } diff --git a/typ/design-patterns/hal/gpio.typ b/typ/design-patterns/hal/gpio.typ index 778dd9d4..61a03866 100644 --- a/typ/design-patterns/hal/gpio.typ +++ b/typ/design-patterns/hal/gpio.typ @@ -3,11 +3,13 @@ #h1(offset: whole*2, if lang == "en" [Recommendations for GPIO Interfaces] else if lang == "de" [Empfehlungen für GPIO-Schnittstellen] + else if lang == "zh" [关于GPIO接口的建议] else { todo }) = #(if lang == "en" [Pin types are zero-sized by default] else if lang == "de" [Pin-Typen haben standardmäßig die Größe Null] + else if lang == "zh" [Pin类型默认是零大小的] else { todo }) (C-ZST-PIN) @@ -20,6 +22,8 @@ Pin jeder Schnittstelle oder jedes Ports einen dedizierten Datentyp der Größe Null bereitstellen. Dies führt zu einer kostenlosen GPIO-Abstraktion, wenn alle Pinbelegungen statisch bekannt sind. +] else if lang == "zh" [ + 由HAL暴露的GPIO接口应该为所有接口或者端口上的每一个管脚提供一个专用的零大小类型,从而当所有的管脚分配静态已知时,提供一个零开销抽象。 ] else { todo } #if lang == "en" [ @@ -28,6 +32,8 @@ ] else if lang == "de" [ Jede GPIO-Schnittstelle oder jeder Port sollte eine `split`-Methode implementieren, die eine Struktur mit allen Pins zurückgibt. +] else if lang == "zh" [ + 每个GPIO接口或者端口应该实现一个`split`方法,它返回一个拥有所有管脚的结构体。 ] else { todo } #if lang == "en" [ @@ -62,6 +68,7 @@ pub struct PortAPins { = #(if lang == "en" [Pin types provide methods to erase pin and port] else if lang == "de" [Pin-Typen stellen Methoden zum Löschen von Pins und Ports bereit] + else if lang == "zh" [管脚类型提供方法去擦除管脚和端口] else { todo }) (C-ERASED-PIN) @@ -72,16 +79,24 @@ pub struct PortAPins { Pins sollten Methoden zur Typeliminierung (Type Erasure) bereitstellen, die ihre Eigenschaften von der Kompilierzeit in die Laufzeit verlagern und so mehr Flexibilität in Anwendungen ermöglichen. +] else if lang == "zh" [ + 从编译时到运行时,管脚都应该提供可以改变属性的类型擦出方法,允许在应用中有更多的灵活性。 ] else { todo } #if lang == "en" [ Example: ] else if lang == "de" [ Beispiel: +] else if lang == "zh" [ + 案例: ] else { todo } #raw(block: true, lang: "rust", -"/// Port A, pin 0. +"/// " + if lang in ("en", "de") { + "Port A, pin 0." + } else if lang == "zh" { + "端口 A, 管脚 0。" + } else { todos } + " pub struct PA0; impl PA0 { @@ -90,10 +105,16 @@ impl PA0 { } } -/// A pin on port A. +/// " + if lang in ("en", "de") { + "A pin on port A." + } else if lang == "zh" { + "端口A上的A管脚。" + } else { todos } + " pub struct PA { /// " + if lang == "en" { "The pin number." + } else if lang == "zh" { + "管脚号。" } else { todos } + " pin: u8, } @@ -115,6 +136,9 @@ pub struct Pin { } else if lang == "de" { "(Diese Felder koennen gepackt werden, um den Speicherbedarf zu // verringern.)" + } else if lang == "zh" { + "(这些字段) + // (这些字段可以打包以减少内存占用)" } else { todos } + " } @@ -128,6 +152,7 @@ enum Port { = #(if lang == "en" [Pin state should be encoded as type parameters] else if lang == "de" [Der Pin-Zustand sollte als Typparameter kodiert werden] + else if lang == "zh" [管脚状态应该被编码成类型参数] else { todo }) (C-PIN-STATE) @@ -140,6 +165,8 @@ enum Port { unterschiedlichen Eigenschaften konfiguriert werden. Dieser Zustand sollte im Typsystem abgebildet werden, um eine Verwendung der Pins in einem falschen Zustand zu verhindern. +] else if lang == "zh" [ + 取决于芯片或者芯片系列,管脚可能被配置为具有不同特性的输出或者输入。这个状态应该编码进类型系统中以避免在错误的状态中使用管脚。 ] else { todo } #if lang == "en" [ @@ -148,6 +175,9 @@ enum Port { ] else if lang == "de" [ Auch zusätzliche, chip-spezifische Zustände (z. B. die Treiberstärke) lassen sich auf diese Weise mittels zusätzlicher Typparameter kodieren. +] else if lang == "zh" [ + 另外,也可以用这个方法使用额外的类型参数编码芯片特定的状态(eg. + 驱动强度)。 ] else { todo } #if lang == "en" [ @@ -156,6 +186,8 @@ enum Port { ] else if lang == "de" [ Methoden zur Änderung des Pin-Zustands sollten als `into_input` und `into_output` bereitgestellt werden. +] else if lang == "zh" [ + 用来改变管脚状态的方法应该被实现成`into_input`和`into_output`方法。 ] else { todo } #if lang == "en" [ @@ -165,6 +197,8 @@ enum Port { Zusätzlich sollten Methoden wie `with_{input,output}_state` angeboten werden, die einen Pin vorübergehend in einen anderen Zustand versetzen, ohne ihn zu verschieben (d.~h. ohne Eigentumsübergang). +] else if lang == "zh" [ + 另外,应该提供`with_{input,output}_state`方法,在一个不同的状态中临时配置一个管脚而不是移动它。 ] else { todo } #if lang == "en" [ @@ -174,6 +208,8 @@ enum Port { Für jeden Pin-Typ sollten die folgenden Methoden bereitgestellt werden (das heißt, sowohl „erased" als auch „non-erased" Pin-Typen müssen dieselbe API bieten): +] else if lang == "zh" [ + 应该为每个的管脚类型提供下列的方法(也就是说,已擦除和未擦除的管脚类型应该提供一样的API): ] else { todo } - `pub fn into_input(self, input: N) -> Pin` - `pub fn into_output(self, output: N) -> Pin` @@ -201,12 +237,17 @@ enum Port { des HAL sollten keinen eigenen Zustand hinzufügen müssen. Die Traits können HAL-spezifische Methoden bereitstellen, die für die Implementierung der Pin-Zustands-API erforderlich sind. +] else if lang == "zh" [ + 管脚状态应该用sealed + traits来绑定。HAL的用户不必添加他们自己的状态。这个traits能提供HAL特定的方法,实现管脚状态API需要这些方法。 ] else { todo } #if lang == "en" [ Example: ] else if lang == "de" [ Beispiel: +] else if lang == "zh" [ + 案例: ] else { todo } #raw(block: true, lang: "rust", @@ -288,5 +329,7 @@ impl PA1 { "Same for `PA` and `Pin`, and other pin types." } else if lang == "de" { "Dasselbe gilt fuer `PA` und `Pin` sowie andere Pin-Typen." + } else if lang == "zh" { + "对于`PA`和`Pin`一样的,对于其它管脚类型来说也是。" } else { todos } + " ") diff --git a/typ/design-patterns/hal/index.typ b/typ/design-patterns/hal/index.typ index b4731358..b0674c75 100644 --- a/typ/design-patterns/hal/index.typ +++ b/typ/design-patterns/hal/index.typ @@ -3,6 +3,7 @@ #h1(offset: whole, if lang == "en" [HAL Design Patterns] else if lang == "de" [HAL-Design-Muster] + else if lang == "zh" [HAL设计模式] else { todo }) #let url_quide = "https://rust-lang.github.io/api-guidelines/" @@ -28,4 +29,13 @@ - #link()[Interoperabilität] - #link()[Vorhersehbarkeit] - #link()[GPIO] +] else if lang == "zh" [ + 这是一组关于使用Rust为微控制器写硬件抽象层的常见的和推荐的模式。当为微控制器编写HALs时,除了现有的 + #link(url_quide)[Rust API 指南] + 外,也可以使用这些模式。 + #link()[检查清单] + - #link()[命名] + - #link()[互用性] + - #link()[可预见性] + - #link()[GPIO] ] else { todo } diff --git a/typ/design-patterns/hal/interoperability.typ b/typ/design-patterns/hal/interoperability.typ index e51b6e9c..b4c94ef2 100644 --- a/typ/design-patterns/hal/interoperability.typ +++ b/typ/design-patterns/hal/interoperability.typ @@ -3,11 +3,13 @@ #h1(offset: whole*2, if lang == "en" [Interoperability] else if lang == "de" [Interoperabilität] + else if lang == "zh" [互用性] else { todo }) = #(if lang == "en" [Wrapper types provide a destructor method] else if lang == "de" [Wrapper-Typen stellen eine Destruktor-Methode bereit] + else if lang == "zh" [封装类型提供一个析构方法] else { todo }) (C-FREE) @@ -20,6 +22,8 @@ implementiert, sollte eine `free`-Methode anbieten; diese verbraucht den Wrapper und gibt das rohe Peripheriegerät (sowie gegebenenfalls weitere Objekte), aus dem er erstellt wurde, wieder frei. +] else if lang == "zh" [ + 任何由HAL提供的非`Copy`封装类型应该提供一个`free`方法,这个方法消费封装类且返回最初生成它的外设(可能是其它对象)。 ] else { todo } #if lang == "en" [ @@ -31,6 +35,8 @@ zurücksetzen. Ein Aufruf von `new` mit dem durch `free` zurückgegebenen rohen Peripheriegerät darf nicht aufgrund eines unerwarteten Zustands des Geräts fehlschlagen. +] else if lang == "zh" [ + 如果有必要,方法应该关闭和重置外设。使用由`free`返回的原始外设去调用`new`不应该由于设备的意外状态而失败, ] else { todo } #if lang == "en" [ @@ -42,12 +48,17 @@ sind, die nicht `Copy` implementieren (zum Beispiel I/O-Pins), sollten auch diese Objekte durch `free` freigegeben und zurückgegeben werden. In diesem Fall sollte `free` ein Tupel zurückgeben. +] else if lang == "zh" [ + 如果HAL类型要求构造其它的非`Copy`对象(比如 I/O + 管脚),任何这样的对象应该也由`free`返回和释放。在这种情况下`free`应该返回一个元组。 ] else { todo } #if lang == "en" [ For example: ] else if lang == "de" [ Zum Beispiel: +] else if lang == "zh" [ + 比如: ] else { todo } ```rust @@ -67,6 +78,7 @@ impl Timer { = #(if lang == "en" [HALs reexport their register access crate] else if lang == "de" [HALs exportieren ihr Registerzugriffs-Crate erneut] + else if lang == "zh" [HALs重新导出它们的寄存器访问crate] else { todo }) (C-REEXPORT-PAC) @@ -78,11 +90,14 @@ impl Timer { their crate root. ] else if lang == "de" [ HALs können auf der Basis von mit - #link("https://github.com/rust-embedded/svd2rust")[svd2rust] generierten + #ln_svd2rust generierten PACs oder anderen Crates implementiert werden, die einen direkten Registerzugriff ermöglichen. HALs sollten das Crate für den Registerzugriff, auf dem sie aufbauen, stets an der Wurzel ihres eigenen Crates erneut exportieren (re-exportieren). +] else if lang == "zh" [ + 可以在#ln_svd2rust;生成的PACs之上,或在其它纯寄存器访问的crates之上编写HALs。HALs需要在crate + root中重新导出它们所基于的寄存器访问crate ] else { todo } #if lang == "en" [ @@ -93,11 +108,15 @@ impl Timer { Ein PAC sollte unter dem Namen `pac` re-exportiert werden -- unabhängig vom eigentlichen Namen des Crates --, da bereits der Name der HAL verdeutlichen sollte, auf welches PAC zugegriffen wird. +] else if lang == "zh" [ + 一个PAC应该被重新导出在`pac`名下,无论这个crate实际的名字是什么,因为HAL的名字应该已经明确了正被访问的是什么PAC ] else { todo } -= #(if lang == "en" [Types implement the `embedded-hal` traits (C-HAL-TRAITS)] += #(if lang == "en" [Types implement the `embedded-hal` traits] else if lang == "de" [Typen implementieren die `embedded-hal`-Traits] - else { todo }) + else if lang == "zh" [类型实现`embedded-hal` traits] + else { todo }) (C-HAL-TRAITS) + #let ln_hal = link("https://github.com/rust-embedded/embedded-hal")[`embedded-hal`] #if lang == "en" [ @@ -106,10 +125,14 @@ impl Timer { ] else if lang == "de" [ Vom HAL bereitgestellte Typen sollten alle anwendbaren Traits implementieren, die vom #ln_hal;-Crate bereitgestellt werden. +] else if lang == "zh" [ + HAL提供的类型应该实现所有的由#ln_hal crate提供的能用的traits。 ] else { todo } #if lang == "en" [ Multiple traits may be implemented for the same type. ] else if lang == "de" [ Für denselben Typ können mehrere Traits implementiert werden. +] else if lang == "zh" [ + 同个类型可能实现多个traits。 ] else { todo } diff --git a/typ/design-patterns/hal/naming.typ b/typ/design-patterns/hal/naming.typ index 17dafcca..5663f9d4 100644 --- a/typ/design-patterns/hal/naming.typ +++ b/typ/design-patterns/hal/naming.typ @@ -3,11 +3,13 @@ #h1(offset: whole*2, if lang == "en" [Naming] else if lang == "de" [Benennung] + else if lang == "zh" [命名] else { todo }) = #(if lang == "en" [The crate is named appropriately] else if lang == "de" [Das Crate trägt einen passenden Namen] + else if lang == "zh" [crate要被恰当地命名] else { todo }) (C-CRATE-NAME) @@ -21,4 +23,6 @@ die sie unterstützen sollen. Ihr Name sollte auf `-hal` enden, um sie von Registerzugriffs-Crates zu unterscheiden. Der Name sollte keine Unterstriche enthalten (stattdessen sind Bindestriche zu verwenden). +] else if lang == "zh" [ + HAL crates应该在目标支持的芯片或者芯片系列之后被命名。它们的名字应该以`-hal`结尾,为了将它们与PAC区分开来。名字不应该包含下划线(请改用破折号)。 ] else { todo } diff --git a/typ/design-patterns/hal/predictability.typ b/typ/design-patterns/hal/predictability.typ index 15489f41..4dfea211 100644 --- a/typ/design-patterns/hal/predictability.typ +++ b/typ/design-patterns/hal/predictability.typ @@ -3,11 +3,13 @@ #h1(offset: whole*2, if lang == "en" [Predictability] else if lang == "de" [Vorhersehbarkeit] + else if lang == "zh" [可预见性] else { todo }) = #(if lang == "en" [Constructors are used instead of extension traits] else if lang == "de" [Anstelle von Extension Traits werden Konstruktoren verwendet] + else if lang == "zh" [使用构造函数而不是扩展traits] else { todo }) (C-CTOR) @@ -19,6 +21,8 @@ Alle Peripheriegeräte, denen die HAL Funktionalität hinzufügt, sollten in einen neuen Typ gekapselt werden -- selbst dann, wenn für diese Funktionalität keine zusätzlichen Felder erforderlich sind. +] else if lang == "zh" [ + 所有由HAL添加功能的外设应该被封装进一个新类型,即使该功能不需要额外的字段。 ] else { todo } #if lang == "en" [ @@ -26,11 +30,14 @@ ] else if lang == "de" [ Die Implementierung von Extension Traits für das rohe Peripheriegerät (Raw Peripheral) sollte vermieden werden. +] else if lang == "zh" [ + 应该避免为基本外设扩展traits。 ] else { todo } = #(if lang == "en" [Methods are decorated with `#[inline]` where appropriate] else if lang == "de" [Methoden werden dort, wo es angebracht ist, mit `#[inline]` versehen] + else if lang == "zh" [方法在适当的地方用`#[inline]`修饰] else { todo }) (C-INLINE) @@ -61,4 +68,9 @@ ermöglicht es dem Compiler, selbst komplexe Initialisierungslogik zur Kompilierzeit zu berechnen, sofern die Eingabewerte der Funktion bekannt sind. +] else if lang == "zh" [ + Rust编译器默认不会越过crate边界执行完全内联。因为嵌入式应用对于不可预期的代码大小的增加很敏感,`#[inline]`应该如下所示用来指导编译器: + - 所有的"小"函数应该被标记`#[inline]`。什么是"小"是主观的,但是通常所有有可能被编译成一位数的指令序列(single-digit + instruction sequences)都可以被视为"小"。 + - 非常有可能把一个常量数值作为参数的函数应该被标记为`#[inline]`。这让编译器在编译时就可以进行计算甚至是复杂的初始化逻辑,前提是函数输入是已知的。 ] else { todo } diff --git a/typ/design-patterns/index.typ b/typ/design-patterns/index.typ index 06add5d2..4a70d4e9 100644 --- a/typ/design-patterns/index.typ +++ b/typ/design-patterns/index.typ @@ -2,6 +2,7 @@ #h1(if lang == "en" [Design Patterns] else if lang == "de" [Design-Muster] + else if lang == "zh" [设计模式] else { todo }) #if lang == "en" [ @@ -9,4 +10,6 @@ ] else if lang == "de" [ Dieses Kapitel zielt darauf ab, verschiedene nützliche Entwurfsmuster für Embedded-Rust zusammenzustellen. +] else if lang == "zh" [ + 这个章节的目标是为嵌入式Rust收集不同的有用的设计模式。 ] else { todo } diff --git a/typ/interoperability/c-with-rust.typ b/typ/interoperability/c-with-rust.typ index f45aaa60..96b59496 100644 --- a/typ/interoperability/c-with-rust.typ +++ b/typ/interoperability/c-with-rust.typ @@ -3,6 +3,7 @@ #h1(offset: whole, if lang == "en" [A little C with your Rust] else if lang == "de" [Ein bisschen C zu Ihrem Rust] + else if lang == "zh" [使用C的Rust] else { todo }) @@ -15,6 +16,10 @@ wesentliche Aspekte: - Einkapselung der bereitgestellten C-API für die Verwendung mit Rust - Erstellen Ihres C- oder C++-Codes zur Integration mit dem Rust-Code +] else if lang == "zh" [ + 要在一个Rust项目中使用C或者C++,主要有两个步骤: + - 用Rust封装要暴露出来使用的C API + - 编译要和Rust代码集成在一起的C或者C++代码 ] else { todo } #if lang == "en" [ @@ -24,10 +29,13 @@ Da C++ über keine stabile ABI verfügt, auf die der Rust-Compiler abzielen könnte, wird empfohlen, bei der Kombination von Rust mit C oder C++ die `C`-ABI zu verwenden. +] else if lang == "zh" [ + 因为对于Rust编译器来说,C++没有一个稳定的ABI,当要将Rust和C或者C++结合时,建议优先选择`C`。 ] else { todo } = #(if lang == "en" [Defining the interface] else if lang == "de" [Definition der Schnittstelle] + else if lang == "zh" [定义接口] else { todo }) #if lang == "en" [ @@ -43,6 +51,8 @@ Header-Datei (`.h` oder `.hpp`) einbinden, die diese Daten definiert. In Rust ist es erforderlich, diese Definitionen entweder manuell nach Rust zu übertragen oder ein Werkzeug zu ihrer Generierung zu verwenden. +] else if lang == "zh" [ + 在Rust消费C或者C++代码之前,必须定义(在Rust中定义),在要被链接的代码中存在什么数据类型和函数签名。在C或者C++中,你要包含一个头文件(`.h`或者`.hpp`),其定义了这个数据。而在Rust中,必须手动地将这些定义翻译成Rust,或者使用一个工具去生成这些定义。 ] else { todo } #if lang == "en" [ @@ -51,10 +61,13 @@ ] else if lang == "de" [ Zunächst behandeln wir die manuelle Übertragung dieser Definitionen von C/C++ nach Rust. +] else if lang == "zh" [ + 首先,我们将介绍如何将这些定义从C/C++手动地转换为Rust。 ] else { todo } == #(if lang == "en" [Wrapping C functions and Datatypes] else if lang == "de" [Einbinden von C-Funktionen und -Datentypen] + else if lang == "zh" [封装C函数和数据类型] else { todo }) #if lang == "en" [ @@ -64,8 +77,9 @@ ] else if lang == "de" [ Typischerweise stellen in C oder C++ geschriebene Bibliotheken eine Header-Datei bereit, die alle in öffentlichen Schnittstellen verwendeten - Typen und Funktionen definiert. Eine Beispieldatei könnte wie folgt - aussehen: + Typen und Funktionen definiert. Eine Beispieldatei könnte wie folgt aussehen: +] else if lang == "zh" [ + 通常,用C或者C++写的库会提供一个头文件,头文件定义了所有的类型和用于公共接口的函数。如下是一个示例文件: ] else { todo } ```c @@ -82,6 +96,8 @@ void cool_function(int i, char c, CoolStruct* cs); When translated to Rust, this interface would look as such: ] else if lang == "de" [ Nach Rust übertragen, sähe diese Schnittstelle folgendermaßen aus: +] else if lang == "zh" [ + 当翻译成Rust时,这个接口将看起来像是: ] else { todo } ```rust @@ -107,6 +123,8 @@ extern "C" { ] else if lang == "de" [ Schauen wir uns diese Definition Schritt für Schritt an, um die einzelnen Bestandteile zu erläutern. +] else if lang == "zh" [ + 让我们一次看一个语句,来解释每个部分。 ] else { todo } ```rust @@ -125,6 +143,8 @@ pub struct CoolStruct { ... } Kompatibilität mit C-Code zu gewährleisten, verwenden wir das Attribut `#[repr(C)]`; dieses weist den Rust-Compiler an, für die Anordnung der Daten innerhalb der Struktur stets dieselben Regeln wie C anzuwenden. +] else if lang == "zh" [ + 默认,Rust不会保证包含在`struct`中的数据的大小,填充,或者顺序。为了保证与C代码兼容,我们使用`#[repr(C)]`属性,它指示Rust编译器总是使用和C一样的规则去组织一个结构体中的数据。 ] else { todo } @@ -141,6 +161,8 @@ pub y: cty::c_int, Aufgrund der Flexibilität, wie C oder C++ ein „int" oder „char" definiert, wird empfohlen, in „cty" definierte primitive Datentypen zu verwenden, die Typen von C auf Typen in Rust abbilden. +] else if lang == "zh" [ + 由于C或者C++定义一个`int`或者`char`的方式很灵活,所以建议使用在`cty`中定义的基础类型,它将类型从C映射到Rust中的类型。 ] else { todo } ```rust @@ -160,6 +182,9 @@ extern "C" { pub fn cool_function( ... ); } an anderer Stelle bereitgestellt oder aus einer statischen Bibliothek in die endgültige Bibliothek bzw. das fertige Binärprogramm eingebunden werden. +] else if lang == "zh" [ + 这个语句定义了一个使用C + ABI的函数的签名,叫做`cool_function`。因为只定义了签名而没有定义函数的主体,所以这个函数的定义将需要在其它地方定义,或者从一个静态库链接进最终的库或者一个二进制文件中。 ] else { todo } ```rust @@ -177,6 +202,8 @@ extern "C" { pub fn cool_function( ... ); } der Funktionsargumente mithilfe von C-kompatiblen Definitionen. Der Übersichtlichkeit halber behalten wir zudem die ursprünglichen Argumentnamen bei. +] else if lang == "zh" [ + 与我们上面的数据类型一样,我们使用C兼容的定义去定义函数参数的数据类型。为了清晰可见,我们还保留了相同的参数名。 ] else { todo } #if lang == "en" [ @@ -194,13 +221,19 @@ extern "C" { pub fn cool_function( ... ); } tatsächlich um einen `null`-Zeiger handeln kann, ist bei der Interaktion mit C- oder C++-Code besondere Sorgfalt geboten, um die für Rust typischen Garantien zu wahren. +] else if lang == "zh" [ + 这里我们有了一个新类型,`*mut CoolStruct` 。因为C没有Rust中像 + `&mut CoolStruct` + 这样的引用,替代的是一个裸指针。所以解引用这个指针是`unsafe`的,因为这个指针实际上可能是一个`null`指针,因此当与C或者C++代码交互时必须要小心对待那些Rust做出的安全保证。 ] else { todo } == #(if lang == "en" [Automatically generating the interface] else if lang == "de" [Automatische Generierung der Schnittstelle] + else if lang == "zh" [自动产生接口] else { todo }) #let url_bindgen = "https://github.com/rust-lang/rust-bindgen" +#let url_bindgen_manual = "https://rust-lang.github.io/rust-bindgen/" #let ln_cty = link("https://crates.io/crates/cty")[`cty`] #if lang == "en" [ Rather than manually generating these interfaces, which may be tedious @@ -208,7 +241,7 @@ extern "C" { pub fn cool_function( ... ); } #link(url_bindgen)[bindgen] which will perform these conversions automatically. For instructions of the usage of #link(url_bindgen)[bindgen], please - refer to the #link(url_bindgen)[bindgen user's manual], + refer to the #link(url_bindgen_manual)[bindgen user's manual], however the typical process consists of the following: + Gather all C or C++ headers defining interfaces or datatypes you would like to use with Rust. @@ -231,7 +264,7 @@ extern "C" { pub fn cool_function( ... ); } #link(url_bindgen)[bindgen], das diese Konvertierungen automatisch durchführt. Hinweise zur Verwendung von #link(url_bindgen)[bindgen] finden Sie im - #link(url_bindgen)[bindgen-Benutzerhandbuch]\; + #link(url_bindgen_manual)[bindgen-Benutzerhandbuch]\; der typische Ablauf sieht jedoch folgendermaßen aus: + Sammeln Sie alle C- oder C++-Header, die Schnittstellen oder Datentypen definieren, die Sie mit Rust verwenden möchten. @@ -251,10 +284,21 @@ extern "C" { pub fn cool_function( ... ); } nicht, das #ln_cty;-Crate zu verwenden, falls die Typen in den generierten Bindings das Präfix `cty` aufweisen. +] else if lang == "zh" [ + 有一个叫做#link(url_bindgen)[bindgen]的工具,它可以自动执行这些转换,而不用手动生成这些接口,手动进行这样的操作非常繁琐且容易出错。关于#link(url_bindgen)[bindgen]的使用指令,可以参考#link(url_bindgen_manual)[bindgen user's manual],常用的步骤大致如下: + + 收集所有定义了你可能在Rust中会用到的数据类型或者接口的C或者C++头文件。 + + 写一个`bindings.h`文件,其`#include "..."`每一个你在步骤一中收集的文件。 + + 将这个`bindings.h`文件和任何用来编译你代码的编译标识发给`bindgen`。贴士: + 使用`Builder.ctypes_prefix("cty")` / `--ctypes-prefix=cty` 和 + `Builder.use_core()` / `--use-core` 去使生成的代码兼容`#![no_std]` + + `bindgen`将会在终端窗口输出生成的Rust代码。这个文件可能会被通过管道发送给你项目中的一个文件,比如`bindings.rs` + 。你可能要在你的Rust项目中使用这个文件来与被编译和链接成一个外部库的C/C++代码交互。贴士: + 如果你的类型在生成的绑定中被前缀了`cty`,不要忘记使用#ln_cty crate 。 ] else { todo } = #(if lang == "en" [Building your C/C++ code] else if lang == "de" [Erstellen Ihres C/C++-Codes] + else if lang == "zh" [编译你的 C/C++ 代码] else { todo }) #if lang == "en" [ @@ -265,6 +309,8 @@ extern "C" { pub fn cool_function( ... ); } Da der Rust-Compiler nicht direkt weiß, wie man C- oder C++-Code (oder Code einer anderen Sprache mit C-Schnittstelle) kompiliert, muss der Nicht-Rust-Code vorab kompiliert werden. +] else if lang == "zh" [ + 因为Rust编译器并不直接知道如何编译C或者C++代码(或者从其它语言来的代码,其提供了一个C接口),所以必须要静态编译你的非Rust代码。 ] else { todo } #if lang == "en" [ @@ -274,8 +320,10 @@ extern "C" { pub fn cool_function( ... ); } ] else if lang == "de" [ Bei Embedded-Projekten bedeutet dies meist, dass der C/C++-Code zu einem statischen Archiv (z. B. `cool-library.a`) kompiliert wird, welches dann - im abschließenden Link-Schritt mit dem Rust-Code zusammengeführt werden - kann. + im abschließenden Link-Schritt mit dem Rust-Code zusammengeführt werden kann. +] else if lang == "zh" [ + 对于嵌入式项目,这通常意味着把C/C++代码编译成一个静态库文档(比如 + `cool-library.a`),然后其能在最后链接阶段与你的Rust代码组合起来。 ] else { todo } #if lang == "en" [ @@ -289,6 +337,8 @@ extern "C" { pub fn cool_function( ... ); } die bereitgestellte Header-Datei für die Schnittstelle wie oben beschrieben umzuwandeln und das statische Archiv beim Kompilieren bzw. Linken einzubinden. +] else if lang == "zh" [ + 如果你要使用的库已经作为一个静态库文档被发布,那就没必要重新编译你的代码。只需按照上面所述转换提供的接口头文件,且在编译/链接时包含静态库文档。 ] else { todo } #if lang == "en" [ @@ -304,10 +354,16 @@ extern "C" { pub fn cool_function( ... ); } Portierung der erforderlichen Kompilierungsschritte auf das sogenannte `cc`-Crate erfolgen. Für beide Vorgehensweisen ist die Verwendung eines `build.rs`-Skripts erforderlich. +] else if lang == "zh" [ + 如果你的代码作为一个源项目(source + project)存在,将你的C/C++代码编译成一个静态库将是必须的,要么通过使用你现存的编译系统(比如 + `make`,`CMake`,等等),要么通过使用一个被叫做`cc` + crate的工具移植必要的编译步骤。关于这两个,都必须使用一个`build.rs`脚本。 ] else { todo } == #(if lang == "en" [Rust `build.rs` build scripts] else if lang == "de" [Rust-`build.rs`-Build-Skripte] + else if lang == "zh" [Rust的 `build.rs` 编译脚本] else { todo }) #if lang == "en" [ @@ -319,6 +375,9 @@ extern "C" { pub fn cool_function( ... ); } dem Kompilierrechner ausgeführt wird -- und zwar _nachdem_ die Abhängigkeiten Ihres Projekts erstellt wurden, aber _bevor_ Ihr Projekt selbst kompiliert wird. +] else if lang == "zh" [ + 一个 `build.rs` + 脚本是一个用Rust语法编写的文件,它被运行在你的编译机器上,发生在你项目的依赖项被编译*之后*,但是在你的项目被编译*之前* 。 ] else { todo } #let url_build_scripts = "https://doc.rust-lang.org/cargo/reference/build-scripts.html" @@ -334,10 +393,15 @@ extern "C" { pub fn cool_function( ... ); } mittels #link(url_bindgen)[bindgen]), zum Aufruf externer Build-Systeme wie `Make` oder zur direkten Kompilierung von C/C++-Code unter Verwendung des `cc`-Crates. +] else if lang == "zh" [ + 可能能在#link("https://doc.rust-lang.org/cargo/reference/build-scripts.html")[这里]发现完整的参考。`build.rs` + 脚本能用来生成代码(比如通过#link("https://github.com/rust-lang/rust-bindgen")[bindgen]),调用外部编译系统,比如`Make`,或者直接通过使用`cc` + crate来直接编译C/C++ 。 ] else { todo } == #(if lang == "en" [Triggering external build systems] else if lang == "de" [Auslösen externer Build-Systeme] + else if lang == "zh" [使用外部编译系统] else { todo }) #let ln_command = link("https://doc.rust-lang.org/std/process/struct.Command.html")[`std::process::Command`] @@ -356,6 +420,9 @@ extern "C" { pub fn cool_function( ... ); } festen Befehl aus (wie etwa `make library`) und kopieren anschließend die erzeugte statische Bibliothek an den entsprechenden Ort im `target`-Build-Verzeichnis. +] else if lang == "zh" [ + 对于有复杂的外部项或者编译系统的项目,使用#ln_command;通过遍历相对路径来向其它编译系统"输出",调用一个固定的命令(比如 + `make library`),然后拷贝最终的静态库到`target`编译文件夹中恰当的位置,可能是最简单的方法。 ] else { todo } #if lang == "en" [ @@ -368,10 +435,14 @@ extern "C" { pub fn cool_function( ... ); } ausschließlich auf dem Rechner ausgeführt, der die Crate kompiliert. Das bedeutet, dass Sie beliebige Rust-Crates verwenden können, die auf Ihrem Kompilier-Host lauffähig sind. +] else if lang == "zh" [ + 虽然你的crate目标可能是一个`no_std`嵌入式平台,但你的`build.rs`只运行在负责编译你的crate的机器上。这意味着你能使用任何Rust + crates,其将运行在你的编译主机上。 ] else { todo } == #(if lang == "en" [Building C/C++ code with the `cc` crate] else if lang == "de" [Kompilieren von C/C++-Code mit dem `cc`-Crate] + else if lang == "zh" [使用`cc` crate构建C/C++代码] else { todo }) #let url_cc = "https://github.com/alexcrichton/cc-rs" @@ -391,6 +462,8 @@ extern "C" { pub fn cool_function( ... ); } #link(url_cc)[`cc`-Crate] zu verwenden; dieses bietet eine idiomatische Rust-Schnittstelle zu dem vom Host bereitgestellten Compiler. +] else if lang == "zh" [ + 对于具有有限的依赖项或者复杂度的项目,或者对于那些难以修改编译系统去生成一个静态库(而不是一个二进制文件或者可执行文件)的项目,使用#url_cc;可能更容易,它提供了一个符合Rust语法的接口,这个接口是关于主机提供的编译器的。 ] else { todo } #if lang == "en" [ @@ -400,9 +473,9 @@ extern "C" { pub fn cool_function( ... ); } ] else if lang == "de" [ Im einfachsten Fall, bei dem eine einzelne C-Datei als Abhängigkeit für eine statische Bibliothek kompiliert wird, sähe ein Beispiel für ein - `build.rs`-Skript, das das - #link(url_cc)[`cc`-Crate] verwendet, - folgendermaßen aus: + `build.rs`-Skript, das das #link(url_cc)[`cc`-Crate] verwendet, folgendermaßen aus: +] else if lang == "zh" [ + 在把一个C文件编译成一个静态库的依赖项的最简单的场景下,可以使用#link(url_cc)[`cc` crate],示例`build.rs`脚本看起来像这样: ] else { todo } ```rust @@ -422,4 +495,6 @@ fn main() { `cargo build` kompiliert und führt sie dann vor dem eigentlichen Build-Vorgang des Pakets aus. Dabei wird ein statisches Archiv namens `libfoo.a` erstellt und im Verzeichnis `target` abgelegt. +] else if lang == "zh" [ + 要把`build.rs`放在包的根目录下.然后`cargo build`会在构建包之前编译和执行它.一个静态的名为`libfoo.a`的归档文件会生成并被放在`target`文件夹中. ] else { todo } diff --git a/typ/interoperability/index.typ b/typ/interoperability/index.typ index 7b2861c2..67f16552 100644 --- a/typ/interoperability/index.typ +++ b/typ/interoperability/index.typ @@ -2,6 +2,7 @@ #h1(if lang == "en" [Interoperability] else if lang == "de" [Interoperabilität] + else if lang == "zh" [互操性] else { todo }) #let ln_ffi = link("https://doc.rust-lang.org/std/ffi/index.html")[`std::ffi`] @@ -13,6 +14,8 @@ Die Interoperabilität zwischen Rust- und C-Code hängt stets von der Umwandlung von Daten zwischen den beiden Sprachen ab. Hierfür gibt es in der Standardbibliothek (`stdlib`) ein spezielles Modul namens #ln_ffi. +] else if lang == "zh" [ + Rust和C代码之间的互操性始终依赖于数据在两个语言间的转换.为了互操性,在`stdlib`中有一个专用的模块,叫作 #ln_ffi. ] else { todo } #if lang == "en" [ @@ -26,6 +29,10 @@ Konvertierung komplexerer Typen wie Strings, indem es sowohl `&str` als auch `String` auf C-Typen abbildet, die sich einfacher und sicherer handhaben lassen. +] else if lang == "zh" [ + `std::ffi`提供了与C基础类型对应的类型定义,比如`char`, + `int`,和`long`. + 它也提供了一些工具用于更复杂的类型之间的转换,比如字符串,可以把`&str`和`String`映射成更容易和安全处理的C类型. ] else { todo } #let ln_cty = link("https://crates.io/crates/cty")[`cty`] @@ -40,6 +47,10 @@ `core::ffi` oder `alloc::ffi` zur Verfügung, je nachdem, ob Speicherreservierungen erforderlich sind oder nicht. Auch die Crates #ln_cty und #ln_cstr bieten vergleichbare Funktionalitäten an. +] else if lang == "zh" [ + 从Rust + 1.30以来,`std::ffi`的功能也出现在`core::ffi`或者`alloc::ffi`中,取决于是否涉及到内存分配. + #ln_cty;库和#ln_cstr;库也提供了相同的功能. ] else { todo } #figure( @@ -50,12 +61,15 @@ table.header( if lang == "en" [Rust type] else if lang == "de" [Rusttyp] + else if lang == "zh" [Rust类型] else { todo }, if lang == "en" [Intermediate] else if lang == "de" [Dazwischenliegend] + else if lang == "zh" [间接] else { todo }, if lang == "en" [C type] else if lang == "de" [C-Typ] + else if lang == "zh" [C类型] else { todo }, ), `String`, `CString`, `char *`, @@ -82,6 +96,9 @@ ein Typalias des letzteren ist. Beispielsweise lässt sich der folgende Code auf Plattformen kompilieren, auf denen `unsigned int` 32 Bit lang ist. +] else if lang == "zh" [ + 一个C基本类型的值可以被用来作为相关的Rust类型的值,反之亦然,因此前者仅仅是后者的一个类型伪名. + 比如,下列的代码可以在`unsigned int`是32位宽的平台上编译. ] else { todo } ```rust @@ -93,6 +110,7 @@ fn foo(num: u32) { == #(if lang == "en" [Interoperability with other build systems] else if lang == "de" [Interoperabilität mit anderen Build-Systemen] + else if lang == "zh" [与其它编译系统的互用性] else { todo }) #if lang == "en" [ @@ -102,6 +120,8 @@ fn foo(num: u32) { Eine häufige Voraussetzung für die Einbindung von Rust in Embedded-Projekte ist die Kombination von Cargo mit Ihrem bestehenden Build-System, beispielsweise Make oder CMake. +] else if lang == "zh" [ + 在嵌入式项目中引入Rust的一个常见需求是,把Cargo结合进你现存的编译系统中,比如make或者cmake。 ] else { todo } #let url_issue61 = "https://github.com/rust-embedded/book/issues/61" @@ -111,10 +131,14 @@ fn foo(num: u32) { ] else if lang == "de" [ Wir sammeln Beispiele und Anwendungsfälle hierzu in unserem Problem-Verfolgungswerkzeug unter #link(url_issue61)[Issue \#61]. +] else if lang == "zh" [ + 在#link(url_issue61)[issue \#61]的issue + tracker上,我们正在为这个需求收集例子和用例。 ] else { todo } == #(if lang == "en" [Interoperability with RTOSs] else if lang == "de" [Interoperabilität mit RTOS] + else if lang == "zh" [与RTOSs的互操性] else { todo }) #if lang == "en" [ @@ -125,6 +149,9 @@ fn foo(num: u32) { Die Integration von Rust in ein RTOS wie FreeRTOS oder ChibiOS befindet sich noch in der Entwicklung; insbesondere der Aufruf von RTOS-Funktionen aus Rust heraus kann sich als schwierig erweisen. +] else if lang == "zh" [ + 将Rust和一个RTOS集成在一起,比如FreeRTOS或者ChibiOS仍然在进行中; + 尤其是从Rust调用RTOS函数可能很棘手。 ] else { todo } #let url_zephyr = "https://docs.zephyrproject.org/latest/develop/languages/rust/index.html" @@ -146,4 +173,7 @@ fn foo(num: u32) { Wir sammeln hierfür Beispiele und Anwendungsfälle in unserem Problem-Verfolgungswerkzeug unter #link(url_issue62)[Issue \#62]. +] else if lang == "zh" [ + 在#link("https://github.com/rust-embedded/book/issues/62")[issue \#62]的issue + tracker上,我们正为这件事收集例子和用例。 ] else { todo } diff --git a/typ/interoperability/rust-with-c.typ b/typ/interoperability/rust-with-c.typ index 87ca223a..bfb2ec7c 100644 --- a/typ/interoperability/rust-with-c.typ +++ b/typ/interoperability/rust-with-c.typ @@ -3,6 +3,7 @@ #h1(offset: whole, if lang == "en" [A little Rust with your C] else if lang == "de" [Ein bisschen Rust zu Ihrem C] + else if lang == "zh" [使用Rust的C] else { todo }) @@ -15,6 +16,10 @@ größtenteils aus zwei Teilen. - Erstellung einer C-kompatiblen API in Rust - Einbindung Ihres Rust-Projekts in ein externes Build-System +] else if lang == "zh" [ + 在C或者C++中使用Rust代码通常由两部分组成。 + - 用Rust生成一个C友好的API + - 将你的Rust项目嵌入一个外部的编译系统 ] else { todo } #if lang == "en" [ @@ -26,10 +31,13 @@ native Rust-Unterstützung. Daher fährst du höchstwahrscheinlich am besten damit, einfach `cargo` für die Kompilierung deines Crates und aller Abhängigkeiten zu verwenden. +] else if lang == "zh" [ + 除了`cargo`和`meson`,大多数编译系统没有原生Rust支持。因此你最好只用`cargo`编译你的crate和依赖。 ] else { todo } = #(if lang == "en" [Setting up a project] else if lang == "de" [Ein Projekt einrichten] + else if lang == "zh" [设置一个项目] else { todo }) #if lang == "en" [ @@ -47,6 +55,8 @@ Systembibliothek anstelle des üblichen Rust-Ziels zu erstellen. Auf diese Weise können Sie auch einen abweichenden Namen für die Bibliothek festlegen, falls dieser sich von dem des restlichen Crates unterscheiden soll. +] else if lang == "zh" [ + 像往常一样创建一个新的`cargo`项目。有一些标志可以告诉`cargo`去生成一个系统库,而不是常规的rust目标文件。如果你想要它与crate的其它部分不一样,你也可以为你的库设置一个不同的输出名。 ] else { todo } #raw(block: true, lang: "toml", @@ -56,16 +66,21 @@ crate-type = [\"cdylib\"] # " + if lang == "en" { "Creates dynamic lib" } else if lang == "de" { "Erstellt eine dynamische Bibliothek" + } else if lang == "zh" { + "生成动态链接库" } else { todos } + " # crate-type = [\"staticlib\"] # " + if lang == "en" { "Creates static lib" } else if lang == "de" { "Erstellt eine statische Bibliothek" + } else if lang == "zh" { + "生成静态链接库" } else { todos } + " ") = #(if lang == "en" [Building a `C` API] else if lang == "de" [Erstellung einer C-API] + else if lang == "zh" [构建一个`C` API] else { todo }) #if lang == "en" [ @@ -77,6 +92,8 @@ crate-type = [\"cdylib\"] # " + if lang == "en" { abzielen könnte, nutzen wir C für die Interoperabilität zwischen verschiedenen Sprachen. Dies gilt auch für die Verwendung von Rust innerhalb von C- und C++-Code. +] else if lang == "zh" [ + 因为对于Rust编译器来说,C++没有稳定的ABI,因此对于不同语言间的互操性我们使用`C`。在C和C++代码的内部使用Rust时也不例外。 ] else { todo } == `#[no_mangle]` @@ -90,6 +107,8 @@ crate-type = [\"cdylib\"] # " + if lang == "en" { Code erwarten. Daher muss jeder Funktion, die Rust zur Verwendung außerhalb von Rust exportiert, mitgeteilt werden, dass der Compiler sie nicht verändern soll. +] else if lang == "zh" [ + Rust对符号名的修饰与主机的代码链接器所期望的不同。因此,需要告知任何被Rust导出到Rust外部去使用的函数不要被编译器修饰。 ] else { todo } == `extern "C"` @@ -103,6 +122,9 @@ crate-type = [\"cdylib\"] # " + if lang == "en" { (die ebenfalls nicht stabilisiert ist). Beim Erstellen von nach außen gerichteten FFI-APIs muss der Compiler daher angewiesen werden, die System-ABI zu verwenden. +] else if lang == "zh" [ + 默认,任何用Rust写的函数将使用Rust ABI(这也不稳定)。当编译面向外部的FFI + APIs时,我们需要告诉编译器去使用系统ABI 。 ] else { todo } #let url_external = "https://doc.rust-lang.org/reference/items/external-blocks.html" @@ -112,6 +134,8 @@ crate-type = [\"cdylib\"] # " + if lang == "en" { ] else if lang == "de" [ Je nach Plattform möchten Sie möglicherweise eine bestimmte ABI-Version anvisieren; diese sind #link(url_external)[hier] dokumentiert. +] else if lang == "zh" [ + 取决于你的平台,你可能想要针对一个特定的ABI版本,其记录在#link(url_external)[这里]。 ] else { todo } #divider() @@ -121,6 +145,8 @@ crate-type = [\"cdylib\"] # " + if lang == "en" { ] else if lang == "de" [ Wenn man diese Teile zusammensetzt, erhält man eine Funktion, die ungefähr so ​​aussieht. +] else if lang == "zh" [ + 把这些部分放在一起,你得到一个函数,其粗略看起来像是这个。 ] else { todo } ```rust @@ -138,22 +164,31 @@ pub extern "C" fn rust_function() { Genau wie bei der Verwendung von C-Code in Ihrem Rust-Projekt müssen Sie nun Daten in eine Form umwandeln -- und aus dieser zurück --, die der Rest der Anwendung versteht. +] else if lang == "zh" [ + 就像在Rust项目中使用`C`代码时那样,现在需要把数据转换为应用中其它部分可以理解的形式。 ] else { todo } = #(if lang == "en" [Linking and greater project context] else if lang == "de" [Verknüpfung und übergeordneter Projektkontext] + else if lang == "zh" [链接和更大的项目上下文] else { todo }) #if lang == "en" [ So then, that's one half of the problem solved. How do you use this now? ] else if lang == "de" [ Damit ist die eine Hälfte des Problems gelöst. Wie verwendet man das nun? +] else if lang == "zh" [ + 问题只解决了一半。 + + 你现在要如何使用它? ] else { todo } #if lang == "en" [ *This very much depends on your project and/or build system* ] else if lang == "de" [ *Das hängt stark von Ihrem Projekt bzw. Ihrem Build-System ab.* +] else if lang == "zh" [ + *这很大程度上取决于你的项目或者编译系统* ] else { todo } #if lang == "en" [ @@ -164,6 +199,8 @@ pub extern "C" fn rust_function() { `cargo` erstellt -- je nach Plattform und Einstellungen -- eine Datei namens `my_lib.so`, `my_lib.dll` oder `my_lib.a`. Diese Bibliothek kann einfach von Ihrem Build-System eingebunden (gelinkt) werden. +] else if lang == "zh" [ + `cargo`将生成一个`my_lib.so`/`my_lib.dll`或者`my_lib.a`文件,取决于你的平台和配置。可以通过编译系统简单地链接这个库。 ] else { todo } #if lang == "en" [ @@ -172,6 +209,8 @@ pub extern "C" fn rust_function() { ] else if lang == "de" [ Um jedoch eine Rust-Funktion aus C heraus aufzurufen, ist eine Header-Datei erforderlich, in der die Funktionssignaturen deklariert werden. +] else if lang == "zh" [ + 然而,从C调用一个Rust函数要求一个头文件去声明函数的签名。 ] else { todo } #if lang == "en" [ @@ -179,6 +218,8 @@ pub extern "C" fn rust_function() { ] else if lang == "de" [ Für jede Funktion in Ihrer Rust-FFI-API muss eine entsprechende Deklaration im Header vorhanden sein. +] else if lang == "zh" [ + 在Rust-ffi API中的每个函数需要有一个相关的头文件函数。 ] else { todo } ```rust @@ -190,6 +231,8 @@ pub extern "C" fn rust_function() {} would then become ] else if lang == "de" [ würde dann werden +] else if lang == "zh" [ + 将会变成 ] else { todo } ```c @@ -200,18 +243,23 @@ void rust_function(); etc. ] else if lang == "de" [ usw. +] else if lang == "zh" [ + 等等。 ] else { todo } +#let ln_cbindgen = link("https://github.com/eqrion/cbindgen")[cbindgen] #if lang == "en" [ There is a tool to automate this process, called - #link("https://github.com/eqrion/cbindgen")[cbindgen] which analyses + #ln_cbindgen which analyses your Rust code and then generates headers for your C and C++ projects from it. ] else if lang == "de" [ Es gibt ein Werkzeug zur Automatisierung dieses Prozesses namens - #link("https://github.com/eqrion/cbindgen")[cbindgen], das Ihren + #ln_cbindgen, das Ihren Rust-Code analysiert und daraus Header-Dateien für Ihre C- und C++-Projekte generiert. +] else if lang == "zh" [ + 这里有个工具可以自动化这个过程,叫做#ln_cbindgen,其会分析你的Rust代码然后为C和C++项目生成头文件。 ] else { todo } #if lang == "en" [ @@ -220,6 +268,8 @@ void rust_function(); ] else if lang == "de" [ An diesem Punkt ist die Verwendung der Rust-Funktionen aus C heraus so einfach, wie den Header einzubinden und die Funktionen aufzurufen! +] else if lang == "zh" [ + 此时从C中使用Rust函数非常简单,只需包含头文件和调用它们! ] else { todo } ```c diff --git a/typ/peripherals/a-first-attempt.typ b/typ/peripherals/a-first-attempt.typ index 7f4a6906..67a946c6 100644 --- a/typ/peripherals/a-first-attempt.typ +++ b/typ/peripherals/a-first-attempt.typ @@ -3,10 +3,12 @@ #h1(offset: whole, if lang == "en" [A First Attempt] else if lang == "de" [Ein erster Versuch in Rust] + else if lang == "zh" [Rust尝鲜] else { todo }) = #(if lang == "en" [The Registers] else if lang == "de" [Die Register] + else if lang == "zh" [寄存器] else { todo }) #let url_arm = "http://infocenter.arm.com/help/topic/com.arm.doc.dui0553a/Babieigh.html" @@ -25,6 +27,10 @@ ARM-Cortex-M-Kerne gilt, werfen wir einen Blick in das #link(url_arm)[ARM-Referenzhandbuch]. Wir sehen, dass es vier Register gibt: +] else if lang == "zh" [ + 让我们看下 'SysTick' 外设 - + 一个简单的计时器,它存在于每个Cortex-M处理器内核中。通常你能在芯片厂商的数据手册或者_技术参考手册_中看到它们,但是下面的例子对所有ARM + Cortex-M核心都是通用的,让我们看下#link(url_arm)[ARM参考手册]。我们能看到这里有四个寄存器: ] else { todo } #figure( @@ -33,35 +39,39 @@ columns: 4, align: (auto,auto,auto,auto,), table.header( - if lang in ("en", "de") [Offset] + if lang in ("en", "de", "zh") [Offset] else { todo }, - if lang in ("en", "de") [Name] + if lang in ("en", "de", "zh") [Name] else { todo }, - if lang == "en" [Description] + if lang in ("en", "zh") [Description] else if lang == "de" [Beschreibung] else { todo }, - if lang == "en" [Width] + if lang in ("en", "zh") [Width] else if lang == "de" [Breite] else { todo }, ), [0x00], [SYST_CSR], if lang == "en" [Control and Status Register] else if lang == "de" [Steuerung und Status Register] + else if lang == "zh" [控制和状态寄存器] else { todo }, [32 bits], [0x04], [SYST_RVR], if lang == "en" [Reload Value Register] else if lang == "de" [Wert-Register neu laden] + else if lang == "zh" [重装载值寄存器] else { todo }, [32 bits], [0x08], [SYST_CVR], if lang == "en" [Current Value Register] else if lang == "de" [Register für den aktuellen Wert] + else if lang == "zh" [当前值寄存器] else { todo }, [32 bits], [0x0C], [SYST_CALIB], if lang == "en" [Calibration Value Register] else if lang == "de" [Kalibrierwert-Register] + else if lang == "zh" [校准值寄存器] else { todo }, [32 bits], ) @@ -69,6 +79,7 @@ = #(if lang == "en" [The C Approach] else if lang == "de" [Der C-Ansatz] + else if lang == "zh" [C语言风格的方法(The C Approach)] else { todo }) #if lang == "en" [ @@ -77,6 +88,8 @@ ] else if lang == "de" [ In Rust können wir eine Sammlung von Registern auf genau dieselbe Weise darstellen wie in C -- mit einem `struct`. +] else if lang == "zh" [ + 在Rust中,我们可以像C语言一样,用一个 `struct` 表示一组寄存器。 ] else { todo } ```rust @@ -106,6 +119,9 @@ struct SysTick { Qualifizierer verfügen wir über unsere vier 32-Bit-Felder, die der obigen Tabelle entsprechen. Aber diese „Struktur" allein nützt natürlich nichts -- wir brauchen eine Variable. +] else if lang == "zh" [ + 告诉Rust编译器像C编译器一样去布局这个结构体。这非常重要,因为Rust允许结构体字段被重新排序,而C语言不允许。你可以想象下如果这些字段被编译器悄悄地重新排了序,在调试时会给我们带来多大的麻烦!有了这个限定符,我们就有了与上表对应的四个32位的字段。但当然,这个 + `struct` 本身没什么用处 - 我们需要一个变量。 ] else { todo } ```rust @@ -115,6 +131,7 @@ let time = unsafe { (*systick).cvr }; = #(if lang == "en" [Volatile Accesses] else if lang == "de" [Volatile-Zugriffe] + else if lang == "zh" [volatile访问(Volatile Accesses)] else { todo }) #if lang == "en" [ @@ -134,6 +151,12 @@ let time = unsafe { (*systick).cvr }; + Jeder Code-Abschnitt an beliebiger Stelle Ihres Programms könnte über diese Struktur auf die Hardware zugreifen. + Vor allem aber funktioniert es eigentlich nicht … +] else if lang == "zh" [ + 现在,上面的方法有一堆问题。 + + 每次想要访问外设,不得不使用unsafe 。 + + 无法指定哪个寄存器是只读的或者读写的。 + + 程序中任何地方的任何一段代码都可以通过这个结构体访问硬件。 + + 最重要的是,实际上它并不能工作。 ] else { todo } #if lang == "en" [ @@ -150,6 +173,8 @@ let time = unsafe { (*systick).cvr }; kennzeichnen, um sicherzustellen, dass jeder Lese- oder Schreibvorgang wie vorgesehen ausgeführt wird. In Rust hingegen kennzeichnen wir die _Zugriffe_ als volatile, nicht die Variable. +] else if lang == "zh" [ + 现在的问题是编译器很聪明。如果你往RAM同个地方写两次,一个接着一个,编译器会注意到这个行为,且完全跳过第一个写入操作。在C语言中,我们能标记变量为`volatile`去确保每个读或写操作按所想的那样发生。在Rust中,我们将_访问_操作标记为易变的(volatile),而不是将变量标记为volatile。 ] else { todo } ```rust @@ -165,6 +190,10 @@ let time = unsafe { core::ptr::read_volatile(&mut systick.cvr) }; Wir haben also eines unserer vier Probleme gelöst, aber jetzt haben wir noch mehr `unsafe`-Code! Glücklicherweise gibt es ein Crate eines Drittanbieters, das hierbei helfen kann -- #ln_volatile. +] else if lang == "zh" [ + 这样,我们已经修复了一个问题,但是现在我们有了更多的 `unsafe` + 代码!幸运的是,有个第三方的crate可以帮助到我们 - + #link("https://crates.io/crates/volatile_register")[`volatile_register`] ] else { todo } ```rust @@ -201,10 +230,15 @@ fn get_time() -> u32 { veränderlichem Zustand besteht und der Compiler nicht wissen kann, ob diese Schreibvorgänge tatsächlich sicher sind; daher ist dies eine sinnvolle Standardeinstellung. +] else if lang == "zh" [ + 现在通过`read`和`write`方法,volatile + accesses可以被自动执行。执行写操作仍然是 `unsafe` + 的,但是公平地讲,硬件有一堆可变的状态,对于编译器来说没有办法知道是否这些写操作是真正安全的,因此默认就这样是个不错的选择。 ] else { todo } = #(if lang == "en" [The Rusty Wrapper] else if lang == "de" [Der Rusty Wrapper] + else if lang == "zh" [Rust风格的封装] else { todo }) #if lang == "en" [ @@ -220,12 +254,16 @@ fn get_time() -> u32 { Nutzern dann eine sichere API zur Verfügung, sodass sie sich darum nicht kümmern müssen (vorausgesetzt, sie vertrauen darauf, dass wir alles richtig gemacht haben!). +] else if lang == "zh" [ + 我们需要把这个`struct`封装进一个更高抽象的API中,这个API对于用户来说,可以安全地调用。作为驱动的作者,我们亲手验证不安全的代码是否正确,然后为我们的用户提供一个safe的API,因此用户们不必担心它(让他们相信我们不会出错!)。 ] else { todo } #if lang == "en" [ One example might be: ] else if lang == "de" [ Ein Beispiel hierfür wäre: +] else if lang == "zh" [ + 有可能可以这样写: ] else { todo } ```rust @@ -272,6 +310,8 @@ pub fn example_usage() -> String { ] else if lang == "de" [ Das Problem bei diesem Ansatz ist nun, dass der folgende Code für den Compiler völlig zulässig ist: +] else if lang == "zh" [ + 现在,这种方法带来的问题是,下列的代码完全可以被编译器接受: ] else { todo } ```rust @@ -305,4 +345,7 @@ fn thread2() { „doppelten" Treiberinstanzen zu erkennen; sobald sich der Code jedoch über mehrere Module, Treiber, Entwickler und Zeiträume hinweg erstreckt, schleichen sich derartige Fehler immer leichter ein. +] else if lang == "zh" [ + 虽然 `set_reload` 函数的 `&mut self` + 参数保证了没有引用到其它的`SystemTimer`结构体,但是不能阻止用户去创造第二个`SystemTimer`,其指向同个外设!如果作者足够努力的话,他能发现所有这些'重复的'驱动实例,那么按这种方式写的代码就可以工作,但是一旦代码被散播一段时间,散播给多个模块,驱动,开发者,它会越来越容易触发此类错误。 ] else { todo } diff --git a/typ/peripherals/borrowck.typ b/typ/peripherals/borrowck.typ index 15756718..761d83f8 100644 --- a/typ/peripherals/borrowck.typ +++ b/typ/peripherals/borrowck.typ @@ -3,6 +3,7 @@ #h1(offset: whole, if lang == "en" [Mutable Global State] else if lang == "de" [Veränderbarer globaler Zustand] + else if lang == "zh" [可变的全局状态] else { todo }) #if lang == "en" [ @@ -15,10 +16,13 @@ Zustand, was auf einen Rust-Entwickler sehr beängstigend wirken kann. Hardware existiert unabhängig von den Strukturen des Codes, den wir schreiben, und kann jederzeit durch die reale Welt verändert werden. +] else if lang == "zh" [ + 不幸的是,硬件本质上是个可变的全局状态,Rust开发者可能会对此感到很害怕。因为硬件独立于我们所写的代码的结构,能被真实世界在任何时候改变。 ] else { todo } = #(if lang == "en" [What should our rules be?] else if lang == "de" [Wie sollten unsere Regeln sein?] + else if lang == "zh" [我们应该遵循什么规则?] else { todo }) #if lang == "en" [ @@ -38,10 +42,16 @@ How can we reliably interact with these peripherals? + Wenn eine Software Lese- und Schreibzugriff auf ein Peripheriegerät haben soll, sollte sie die einzige Referenz auf dieses Peripheriegerät besitzen. +] else if lang == "zh" [ + 我们如何才能做到可靠地与这些外设交互? + + 总是使用 `volatile` 方法去读或者写外设存储器。因为它随时会改变。 + + 在软件中,我们应该能共享任何数量的关于这些外设的只读访问 + + 如果某个软件可以读写一个外设,它应该保有对那个外设的唯一引用。 ] else { todo } = #(if lang == "en" [The Borrow Checker] else if lang == "de" [Der Borrow-Checker] + else if lang == "zh" [借用检查器] else { todo }) #if lang == "en" [ @@ -50,6 +60,8 @@ How can we reliably interact with these peripherals? ] else if lang == "de" [ Die letzten beiden dieser Regeln klingen verdächtig ähnlich wie das, was der Borrow Checker bereits tut! +] else if lang == "zh" [ + 这些规则最后两个听起来与借用检查器在做的事情很像! ] else { todo } #if lang == "en" [ @@ -59,6 +71,8 @@ How can we reliably interact with these peripherals? Stellen Sie sich vor, wir könnten die Besitzrechte an diesen Peripheriegeräten weitergeben oder unveränderliche bzw. veränderliche Referenzen darauf anbieten? +] else if lang == "zh" [ + 思考一下,我们是否可以传递这些外设的所有权,或者提供对它们的可变或者不可变的引用? ] else { todo } #if lang == "en" [ @@ -72,4 +86,6 @@ How can we reliably interact with these peripherals? kann. Glücklicherweise existiert in der Hardware nur eine Instanz jedes Peripheriegeräts, aber wie können wir dies in der Struktur unseres Codes sichtbar machen? +] else if lang == "zh" [ + 我们当然可以,但是对于借用检查器来说,每个外设只有一个实例的话,Rust才可以正确地处理这件事。幸运的是,在硬件中,任何给定的外设,只有一个实例,但是我们该如何将它暴露在代码的结构中呢? ] else { todo } diff --git a/typ/peripherals/index.typ b/typ/peripherals/index.typ index 4057f9ed..82ad25d9 100644 --- a/typ/peripherals/index.typ +++ b/typ/peripherals/index.typ @@ -2,11 +2,13 @@ #h1(if lang == "en" [Peripherals] else if lang == "de" [Peripheriegeräte] + else if lang == "zh" [外设] else { todo }) = #(if lang == "en" [What are Peripherals?] else if lang == "de" [Was sind Peripheriegeräte?] + else if lang == "zh" [什么是外设?] else { todo }) #if lang == "en" [ @@ -24,6 +26,9 @@ über Sensoren, Motorsteuerungen oder menschliche Schnittstellen wie ein Display oder eine Tastatur verwendet werden. Diese Komponenten werden zusammenfassend als Peripheriegeräte bezeichnet. +] else if lang == "zh" [ + 大多数微处理器不仅仅有一个CPU,RAM,或者Flash存储器 - + 它们还包含被用来与微处理器的外部系统进行交互的硅片部分,通过传感器,电机控制器,或者人机接口比如一个显示器或者键盘直接和间接地与周遭世界交互。这些组件统称为外设。 ] else { todo } #if lang == "en" [ @@ -41,6 +46,8 @@ würde, können Embedded-Entwickler einige Aufgaben auf Peripheriegeräte verlagern, sodass die CPU ihre Zeit mit anderen wichtigen Dingen verbringen oder gar nichts tun kann, um Strom zu sparen. +] else if lang == "zh" [ + 这些外设很有用,因为它们允许一个开发者将处理工作交给它们来做,避免了必须在软件中处理每件事。就像一个桌面开发者如何将图形处理工作让给一个显卡那样,嵌入式开发者能将一些任务让给外设去做,让CPU可以把时间放在做其它更重要的事上,或者为了省电啥事也不做。 ] else { todo } #if lang == "en" [ @@ -61,6 +68,12 @@ - Einen RAM-Chip - Einen ROM-Chip - Einen Ein-/Ausgabe-Controller +] else if lang == "zh" [ + 如果你看向来自1970s或者1980s的旧型号的家庭电脑的主板(其实,昨日的桌面PCs与今日的嵌入式系统没太大区别),你将看到: + - 一个处理器 + - 一个RAM芯片 + - 一个ROM芯片 + - 一个I/O控制器 ] else { todo } #if lang == "en" [ @@ -80,6 +93,9 @@ Daten überträgt. In unseren eingebetteten Mikrocontrollern gelten die gleichen Prinzipien -- nur dass alles auf einem einzigen Stück Silizium untergebracht ist. +] else if lang == "zh" [ + RAM芯片,ROM芯片和I/O控制器(这个系统中的外设)会通过一系列并行的迹(traces)又被称为一个"总线"被加进处理器中。地址总线搬运地址信息,其用来选择处理器希望跟总线上哪个设备通信,还有一个用来搬运实际数据的数据总线。在我们的嵌入式微控制器中,应用了相同的概念 + \- 只是所有的东西被打包到一片硅片上。 ] else { todo } #if lang == "en" [ @@ -91,10 +107,13 @@ wie Vulkan, Metal oder OpenGL verfügen, werden Peripheriegeräte unserem Mikrocontroller über eine Hardwareschnittstelle zugänglich gemacht, die einem Teil des Speichers zugeordnet ist. +] else if lang == "zh" [ + 然而,不像显卡,显卡通常有像是Vulkan,Metal,或者OpenGL这样的一个软件API。外设暴露给微控制器的是一个硬件接口,其被映射到一块存储区域。 ] else { todo } = #(if lang == "en" [Linear and Real Memory Space] else if lang == "de" [Linearer und realer Speicherraum] + else if lang == "zh" [线性的物理存储空间] else { todo }) #if lang == "en" [ @@ -105,6 +124,9 @@ Bei einem Mikrocontroller kann auch das Schreiben von Daten an eine beliebige andere Adresse -- etwa `0x4000_0000` oder `0x0000_0000` -- ein völlig zulässiger Vorgang sein. +] else if lang == "zh" [ + 在一个微控制器上,随便往一些地址写一些数据,比如 `0x4000_0000` 或者 + `0x0000_0000`,可能也是一个完全有效的动作。 ] else { todo } #if lang == "en" [ @@ -125,6 +147,9 @@ Speicherbereiche, die von der Software genutzt werden. Mikrocontroller verfügen in der Regel nicht über eine MMU; stattdessen verwendet die Software dort ausschließlich reale physische Adressen. +] else if lang == "zh" [ + 在一个桌面系统上,访问内存被MMU,或者内存管理单元紧紧地控制着。这个组件有两个主要责任: + 对部分内存加入访问权限(防止一个进程读取或者修改另一个进程的内存);重映射物理内存的段到软件中使用的虚拟内存范围上。微控制器通常没有一个MMU,反而在软件中只使用真实的物理地址。 ] else { todo } #if lang == "en" [ @@ -163,6 +188,12 @@ diesen beiden Bereichen ungenutzt zu lassen, haben die Entwickler der Mikrocontroller die Schnittstellen für Peripheriegeräte bestimmten Speicheradressen zugeordnet. Das Ergebnis sieht dann ungefähr so ​​aus: +] else if lang == "zh" [ + 虽然32位微控制器有一个从`0x0000_0000`到`0xFFFF_FFFF`的线性的物理地址空间,但是它们通常只使用几百KiB的实际内存。有相当大部分的地址空间保留着。在早期的章节中,我们说到RAM被放置在地址`0x2000_0000`处。如果我们的RAM是64KiB大小(i.e.~最大地址为0xFFFF),那么地址 + `0x2000_0000`到`0x2000_FFFF`与我们的RAM有关。当我们写入一个位于地址`0x2000_1234`的变量时,内部发生的是,一些逻辑发现了地址的上部(这个例子里是0x2000),然后激活RAM,以便能操作地址的下部(这个例子里是0x1234)。在一个Cortex-M上,我们也也会把Flash + ROM映射进地址 `0x000_0000` 到地址 `0x0007_FFFF` 上 (如果我们有一个512KiB + Flash + ROM)。微控制器设计者没有忽略这两个区域间的所有剩余空间,反而将外设的接口映射到这些地址上。最后看起来像这样: ] else { todo } #box(image("../assets/nrf52-memory-map.png")) @@ -171,6 +202,7 @@ = #(if lang == "en" [Memory Mapped Peripherals] else if lang == "de" [Im Speicher abgebildete Peripheriegeräte] + else if lang == "zh" [存储映射的外设] else { todo }) #if lang == "en" [ @@ -186,6 +218,9 @@ Schnittstelle genauso direkt sein wie das Schreiben dieses 32-Bit-Worts an eine bestimmte Speicheradresse. Das Serial-Port-Peripheriegerät würde dann übernehmen und die Daten automatisch versenden. +] else if lang == "zh" [ + 乍一看,与这些外设交互很简单 - + 将正确的数据写入正确的地址。比如,在一个串行端口上发送一个32位字,可以直接把那个32位字写入某个存储地址。串行端口外设然后能自动获取和发出数据。 ] else { todo } #if lang == "en" [ @@ -200,10 +235,14 @@ Die Konfiguration dieser Peripheriegeräte funktioniert ähnlich. Anstatt eine Funktion zum Konfigurieren eines Peripheriegeräts aufzurufen, wird ein Teil des Speichers verfügbar gemacht, der als Hardware-API dient. - Schreiben Sie „0x8000\_0000" in ein SPI-Frequenzkonfigurationsregister, + Schreiben Sie „0x8000_0000" in ein SPI-Frequenzkonfigurationsregister, und der SPI-Port sendet Daten mit 8 Megabit pro Sekunde. Schreiben Sie - „0x0200\_0000" an dieselbe Adresse und der SPI-Port sendet Daten mit 125 + „0x0200_0000" an dieselbe Adresse und der SPI-Port sendet Daten mit 125 Kilobit pro Sekunde. Diese Konfigurationsregister sehen in etwa so aus: +] else if lang == "zh" [ + 这些外设的配置工作相似。不是调用一个函数去配置一个外设,而是暴露一块地址空间作为硬件API。向一个SPI频率控制寄存器写入 + `0x8000_0000`,SPI端口将会按照每秒8MB的速度发送数据。向同个地址写入 + `0x0200_0000`,SPI端口将会按照每秒125KiB的速度发送数据。这些配置寄存器看起来有点像这个: ] else { todo } #box(image("../assets/nrf52-spi-frequency-register.png")) @@ -217,4 +256,6 @@ Über diese Schnittstelle erfolgen die Interaktionen mit der Hardware, unabhängig davon, welche Sprache verwendet wird -- sei es Assembler, C oder Rust. +] else if lang == "zh" [ + 这个接口是关于如何与硬件交互的,其与被使用的语言无关,无论这个语言是汇编,C,或者Rust。 ] else { todo } diff --git a/typ/peripherals/singletons.typ b/typ/peripherals/singletons.typ index e4ec57ed..e258b4c5 100644 --- a/typ/peripherals/singletons.typ +++ b/typ/peripherals/singletons.typ @@ -2,6 +2,7 @@ #h1(offset: whole, if lang in ("en", "de") [Singletons] + else if lang == "zh" [单例] else { todo }) #quote(block: true)[ @@ -11,6 +12,8 @@ ] else if lang == "de" [ In der Softwaretechnik ist das Singleton-Muster ein Entwurfsmuster, das die Instanziierung einer Klasse auf ein einziges Objekt beschränkt. +] else if lang == "zh" [ + 在软件工程中,单例模式是一个软件设计模式,其限制了一个类到一个对象的实例化。 ] else { todo } #let url_singleton = "https://en.wikipedia.org/wiki/Singleton_pattern" @@ -18,17 +21,22 @@ _Wikipedia: #link(url_singleton)[Singleton Pattern]_ ] else if lang == "de" [ _Wikipedia: #link(url_singleton)[Singleton-Muster]_ +] else if lang == "zh" [ + _Wikipedia: #link(url_singleton)[Singleton Pattern]_ ] else { todo } ] = #(if lang == "en" [But why can't we just use global variable(s)?] else if lang == "de" [Aber warum können wir nicht einfach globale Variable(n) verwenden?] + else if lang == "zh" [为什么不可以使用全局变量?] else { todo }) #if lang == "en" [ We could make everything a public static, like this ] else if lang == "de" [ Wir könnten alles als „public static" definieren, etwa so: +] else if lang == "zh" [ + 可以像这样,我们可以使每个东西都变成公共静态的(public static): ] else { todo } ```rust @@ -52,10 +60,13 @@ fn main() { in Rust stets „unsafe". Zudem sind diese Variablen im gesamten Programm sichtbar, was bedeutet, dass der Borrow Checker nicht dabei helfen kann, die Referenzen und die Ownership dieser Variablen zu verfolgen. +] else if lang == "zh" [ + 但是这个带来了一些问题。它是一个可变的全局变量,在Rust,与这些变量交互总是unsafe的。这些变量在你所有的程序间也是可见的,意味着借用检查器不能帮你跟踪这些变量的引用和所有权。 ] else { todo } = #(if lang == "en" [How do we do this in Rust?] else if lang == "de" [Wie machen wir das in Rust?] + else if lang == "zh" [在Rust中要怎么做?] else { todo }) #if lang == "en" [ @@ -67,6 +78,10 @@ fn main() { definieren, könnten wir uns stattdessen dazu entschließen, eine Struktur -- in diesem Fall mit dem Namen `PERIPHERALS` -- zu erstellen, die für jedes unserer Peripheriegeräte ein `Option` enthält. +] else if lang == "zh" [ + 与其只是让我们的外设变成一个全局变量,我们不如创造一个结构体,在这个例子里其被叫做 + `PERIPHERALS`,这个全局变量对于我们的每个外设,它都有一个与之对应的 + `Option` . ] else { todo } ```rust @@ -91,6 +106,8 @@ static mut PERIPHERALS: Peripherals = Peripherals { Diese Struktur ermöglicht es uns, eine einzelne Instanz unserer Peripheriekomponente zu erhalten. Wenn wir versuchen, `take_serial()` mehr als einmal aufzurufen, wird unser Code eine Panic auslösen! +] else if lang == "zh" [ + 这个结构体允许我们获得一个外设的实例。如果我们尝试调用`take_serial()`获得多个实例,我们的代码将会抛出运行时恐慌(panic)! ] else { todo } #raw(block: true, lang: "rust", @@ -100,6 +117,8 @@ static mut PERIPHERALS: Peripherals = Peripherals { "This panics!" } else if lang == "de" { "Dies loest eine panic aus!" + } else if lang == "zh" { + "这里造成运行时恐慌!" } else { todos } + " // let serial_2 = unsafe { PERIPHERALS.take_serial() }; } @@ -113,6 +132,9 @@ static mut PERIPHERALS: Peripherals = Peripherals { Obwohl die Interaktion mit dieser Struktur als `unsafe` gilt, benötigen wir -- sobald wir über die darin enthaltene `SerialPort`-Instanz verfügen -- weder `unsafe`-Code noch die `PERIPHERALS`-Struktur selbst. +] else if lang == "zh" [ + 虽然与这个结构体交互是`unsafe`,然而一旦我们获得了它包含的 + `SerialPort`,我们将不再需要使用`unsafe`,或者`PERIPHERALS`结构体。 ] else { todo } #if lang == "en" [ @@ -126,10 +148,16 @@ static mut PERIPHERALS: Peripherals = Peripherals { `take_serial()` aufrufen müssen; dieser geringe anfängliche Aufwand ermöglicht es uns jedoch, den Borrow-Checker im weiteren Verlauf des Programms voll zu nutzen. +] else if lang == "zh" [ + 这个带来了少量的运行时开销,因为我们必须打包 `SerialPort` + 结构体进一个option中,且我们将需要调用一次 + `take_serial()`,但是这种少量的前期成本,能使我们在接下来的程序中使用借用检查器(borrow + checker) 。 ] else { todo } = #(if lang == "en" [Existing library support] else if lang == "de" [Vorhandene Bibliotheksunterstützung] + else if lang == "zh" [已存在的库支持] else { todo }) #if lang == "en" [ @@ -140,6 +168,10 @@ static mut PERIPHERALS: Peripherals = Peripherals { Obwohl wir oben unsere eigene `Peripherals`-Struktur erstellt haben, ist dies für Ihren Code nicht erforderlich; das `cortex_m`-Crate enthält ein Makro namens `singleton!()`, das diese Aufgabe für Sie übernimmt. +] else if lang == "zh" [ + 虽然我们在上面生成了我们自己的 `Peripherals` + 结构体,但这并不是必须的。`cortex_m` crate 包含一个被叫做 `singleton!()` + 的宏,它可以为你完成这个任务。 ] else { todo } #raw(block: true, lang: "rust", @@ -150,6 +182,8 @@ fn main() { "OK if `main` is executed only once" } else if lang == "de" { "In Ordnung, wenn `main` nur einmal ausgefuehrt wird." + } else if lang == "zh" { + "OK 如果 `main` 只被执行一次" } else { todos } + " let x: &'static mut bool = singleton!(: bool = false).unwrap(); @@ -157,7 +191,7 @@ fn main() { ") #let url_sinmacro = "https://docs.rs/cortex-m/latest/cortex_m/macro.singleton.html" -#if lang == "en" [ +#if lang in ("en", "zh") [ #link(url_sinmacro)[cortex_m docs] ] else if lang == "de" [ #link(url_sinmacro)[cortex_m Dokumente] @@ -175,6 +209,10 @@ fn main() { dieser Peripheriekomponenten für Sie abstrahiert; stattdessen erhalten Sie eine `Peripherals`-Struktur, die eine Version aller von Ihnen definierten Elemente enthält, die nicht als `Option` vorliegen. +] else if lang == "zh" [ + 另外,如果你使用 + #ln_rtic,它将获取和定义这些外设的整个过程抽象了出来,你将获得一个`Peripherals`结构体,其包含了所有你定义了的项的一个非 + `Option` 的版本。 ] else { todo } @@ -190,6 +228,8 @@ const APP: () = { "Cortex-M peripherals" } else if lang == "de" { "Cortex-M-Peripherie" + } else if lang == "zh" { + "Cortex-M外设" } else { todos } + " let core: cortex_m::Peripherals = cx.core; @@ -197,6 +237,8 @@ const APP: () = { "Device specific peripherals" } else if lang == "de" { "Geraetespezifische Peripheriegeraete" + } else if lang == "zh" { + "设备特定的外设" } else { todos } + " let device: lm3s6965::Peripherals = cx.device; } @@ -205,6 +247,7 @@ const APP: () = { = #(if lang == "en" [But why?] else if lang == "de" [Aber warum?] + else if lang == "zh" [为什么?] else { todo }) #if lang == "en" [ @@ -213,6 +256,8 @@ const APP: () = { ] else if lang == "de" [ Aber wie bewirken diese Singletons einen spürbaren Unterschied in der Funktionsweise unseres Rust-Codes? +] else if lang == "zh" [ + 但是这些单例模式是如何使我们的Rust代码在工作方式上产生很大不同的? ] else { todo } #raw(block: true, lang: "rust", @@ -224,6 +269,8 @@ const APP: () = { "<------ This is really, really important" } else if lang == "de" { "<------ Das ist wirklich, wirklich wichtig." + } else if lang == "zh" { + "<------ 这个真的真的很重要" } else { todos } + " ) -> u32 { unsafe { @@ -245,6 +292,12 @@ const APP: () = { einen Ort, um eine `SerialPort`-Struktur zu erhalten. - Um die Methode `read_speed()` aufzurufen, benötigen wir den Besitz einer `SerialPort`-Struktur oder eine Referenz darauf. +] else if lang == "zh" [ + 这里有两个重要因素: + - 因为我们正在使用一个单例模式,所以我们只有一种方法或者地方去获得一个 + `SerialPort` 结构体。 + - 为了调用 `read_speed()` 方法,我们必须拥有一个 `SerialPort` + 结构体的所有权或者一个引用。 ] else { todo } #if lang == "en" [ @@ -258,6 +311,8 @@ const APP: () = { Borrow-Checkers ordnungsgemäß erfüllt haben -- das heißt, dass zu keinem Zeitpunkt mehrere veränderbare Referenzen auf dieselbe Hardware existieren! +] else if lang == "zh" [ + 这两个因素放在一起意味着,只有当我们满足了借用检查器的条件时,我们才有可能访问硬件,也意味着在任何时候不可能存在多个对同一个硬件的可变引用(&mut)! ] else { todo } #raw(block: true, lang: "rust", @@ -266,6 +321,8 @@ const APP: () = { "missing reference to `self`! Won't work." } else if lang == "de" { "Verweis auf `self` fehlt! Das wird nicht funktionieren." + } else if lang == "zh" { + "缺少对`self`的引用!将不会工作。" } else { todos } + " // SerialPort::read_speed(); @@ -275,6 +332,8 @@ const APP: () = { "you can only read what you have access to" } else if lang == "de" { "Sie koennen nur lesen, worauf Sie Zugriff haben" + } else if lang == "zh" { + "你只能读取你有权访问的内容" } else { todos } + " let _ = serial_1.read_speed(); } @@ -282,6 +341,7 @@ const APP: () = { = #(if lang == "en" [Treat your hardware like data] else if lang == "de" [Behandle deine Hardware wie Daten] + else if lang == "zh" [像对待数据一样对待硬件] else { todo }) #if lang == "en" [ @@ -292,12 +352,16 @@ const APP: () = { Da es zudem sowohl veränderbare als auch unveränderbare Referenzen gibt, lässt sich erkennen, ob eine Funktion oder Methode potenziell den Zustand der Hardware ändern könnte. Zum Beispiel: +] else if lang == "zh" [ + 另外,因为一些引用是可变的,一些是不可变的,就可以知道一个函数或者方法是否有能力修改硬件的状态。比如, ] else { todo } #if lang == "en" [ This is allowed to change hardware settings: ] else if lang == "de" [ Dies darf Hardware-Einstellungen ändern: +] else if lang == "zh" [ + 这个函数可以改变硬件的配置: ] else { todo } ```rust @@ -313,6 +377,8 @@ fn setup_spi_port( This isn't: ] else if lang == "de" [ Das nicht: +] else if lang == "zh" [ + 这个不行: ] else { todo } ```rust @@ -334,4 +400,6 @@ fn read_button(gpio: &GpioPin) -> bool { innerhalb einer einzelnen Anwendung funktioniert; da unsere Software für Bare-Metal-Systeme jedoch als eine einzige Anwendung kompiliert wird, stellt dies üblicherweise keine Einschränkung dar. +] else if lang == "zh" [ + 这允许我们在*编译时*而不是运行时强制代码是否应该或者不应该对硬件进行修改。要注意,这通常在只有一个应用的情况下起作用,但是对于裸机系统来说,我们的软件将被编译进一个单一应用中,因此这通常不是一个限制。 ] else { todo } diff --git a/typ/portability/index.typ b/typ/portability/index.typ index 11cd66d6..2c12288a 100644 --- a/typ/portability/index.typ +++ b/typ/portability/index.typ @@ -2,6 +2,7 @@ #h1(if lang == "en" [Portability] else if lang == "de" [Portabilität] + else if lang == "zh" [可移植性] else { todo }) @@ -16,6 +17,9 @@ Herstellers bietet unterschiedliche Peripheriegeräte und Funktionen an, und dementsprechend variieren auch die Interaktionsmöglichkeiten mit den Peripheriegeräten. +] else if lang == "zh" [ + 在嵌入式环境中,可移植性是一个非常重要的主题: + 每个供应商甚至同个制造商的不同系列间,都提供了不同的外设和功能。同样地,与外设交互的方式也将会不一样。 ] else { todo } #if lang == "en" [ @@ -24,6 +28,8 @@ ] else if lang == "de" [ Eine gängige Methode, diese Unterschiede auszugleichen, ist die sogenannte Hardware-Abstraktionsschicht (HAL). +] else if lang == "zh" [ + 通过一个被叫做硬件抽象层或者*HAL*的层去均等化这种差异是一种常见的方法。 ] else { todo } #quote(block: true)[ @@ -35,6 +41,8 @@ Hardware-Abstraktionen sind Sammlungen von Software-Routinen, die bestimmte plattformspezifische Details emulieren und Programmen so direkten Zugriff auf die Hardwareressourcen ermöglichen. +] else if lang == "zh" [ + 在软件中硬件抽象是一组函数,其模仿了一些平台特定的细节,让程序可以直接访问硬件资源。 ] else { todo } #if lang == "en" [ @@ -45,12 +53,12 @@ Sie ermöglichen es Programmierern häufig, geräteunabhängige Hochleistungsanwendungen zu schreiben, indem sie standardisierte Betriebssystemaufrufe für den Hardwarezugriff bereitstellen. +] else if lang == "zh" [ + 通过向硬件提供标准的操作系统(OS)调用,它可以让程序员编写独立于设备的高性能应用。 ] else { todo } #let url_hal = "https://en.wikipedia.org/wiki/Hardware_abstraction" -#if lang == "en" [ - _Wikipedia: #link(url_hal)[Hardware Abstraction Layer]_ -] else if lang == "de" [ +#if lang == ("en", "de", "zh") [ _Wikipedia: #link(url_hal)[Hardware Abstraction Layer]_ ] else { todo } ] @@ -71,6 +79,8 @@ beschriebene Ansatz könnte zwar theoretisch funktionieren, ist aber wahrscheinlich nicht der effizienteste Weg, um Portabilität zu gewährleisten. +] else if lang == "zh" [ + 在这方面,嵌入式系统有点特别,因为通常没有操作系统和用户可安装的软件,而只有固件镜像,其作为一个整体被编译且伴着许多约束。因此虽然维基百科定义的传统方法可能有用,但是它不是确保可移植性最有效的方法。 ] else { todo } #let ln_hal = link("https://crates.io/crates/embedded-hal")[embedded-hal] @@ -78,10 +88,13 @@ How do we do this in Rust? Enter *#ln_hal*… ] else if lang == "de" [ Wie gehen wir dabei in Rust vor? Hier kommt *#ln_hal* ins Spiel… +] else if lang == "zh" [ + 在Rust中我们要怎么实现这个目标呢?让我们进入*embedded-hal*… ] else { todo } == #(if lang == "en" [What is #ln_hal?] else if lang == "de" [Was ist #ln_hal?] + else if lang == "zh" [什么是embedded-hal?] else { todo }) #if lang == "en" [ @@ -102,12 +115,20 @@ Methoden (d.~h., wenn ein Typ, der ein Merkmal implementiert, erstellt werden kann, ist garantiert, dass die im Merkmal spezifizierten Methoden verfügbar sind). +] else if lang == "zh" [ + 简而言之,它是一组traits,其定义了*HAL + implementations*,*驱动*,*应用(或者固件)* + 之间的实现约定(implementation + contracts)。这些约定包括功能(即约定,如果为某个类型实现了某个trait,*HAL + implementation*就提供了某个功能)和方法(即,如果构造一个实现了某个trait的类型,约定保障类型肯定有在trait中指定的方法)。 ] else { todo } #if lang == "en" [ A typical layering might look like this: ] else if lang == "de" [ Ein typischer Schichtaufbau könnte wie folgt aussehen: +] else if lang == "zh" [ + 典型的分层可能如下所示: ] else { todo } #box(image("../assets/rust_layers.svg")) @@ -128,6 +149,14 @@ - SPI - Timers/Countdowns - Analog-Digital-Umsetzung +] else if lang == "zh" [ + 一些在*embedded-hal*中被定义的traits: + - GPIO (input and output pins) + - Serial communication + - I2C + - SPI + - Timers/Countdowns + - Analog Digital Conversion ] else { todo } #if lang == "en" [ @@ -160,19 +189,28 @@ hingegen *M + N* an. Natürlich ergeben sich daraus weitere Vorteile, wie etwa ein geringerer Aufwand durch „Trial-and-Error" dank wohldefinierter und sofort einsatzbereiter APIs. +] else if lang == "zh" [ + 使用*embedded-hal* + traits和依赖*embedded-hal*的crates的主要原因是为了控制复杂性。如果发现一个应用可能必须要实现对硬件外设的使用,以及需要实现应用程序和其它硬件组件间潜在的驱动,那么其应该很容易被看作是可复用性有限的。用数学语言来说就是,如果*M*是外设HAL + implementations的数量,*N*是驱动的数量,那么如果我们要为每个应用重新发明轮子我们最终会有*M\*N*个实现,然而通过使用*embedded-hal*的traits提供的 + _API_ 将会使实现复杂性变成*M+N* + 。当然还有其它好处,比如由于API定义良好,开箱即用,导致试错减少。 ] else { todo } == #(if lang == "en" [Users of the #ln_hal] else if lang == "de" [Nutzer von #ln_hal] + else if lang == "zh" [embedded-hal的用户] else { todo }) #if lang == "en" [ As said above there are three main users of the HAL: ] else if lang == "de" [ Wie bereits erwähnt, gibt es drei Hauptnutzer einer HAL: +] else if lang == "zh" [ + 像上面所说的,HAL有三个主要用户: ] else { todo } -=== #(if lang == "en" [HAL implementation] +=== #(if lang in ("en", "zh") [HAL implementation] else if lang == "de" [HAL-Implementierung] else { todo }) @@ -195,6 +233,13 @@ Betriebsmodus, verwendete Pins usw.) bereitstellen. - eine oder mehrere `trait`-Implementierungen (`impl`) von *#ln_hal*-Traits für diesen Typ +] else if lang == "zh" [ + HAL implentation提供硬件和HAL + traits的用户之间的接口。典型的实现由三部分组成: + - 一个或者多个硬件特定的类型 + - 生成和初始化这个类型的函数,函数经常提供不同的配置选项(速度,操作模式,使用的管脚,etc 。) + - 与这个类型有关的一个或者多个 *embedded-hal* traits 的 `trait` + `impl` ] else { todo } #if lang == "en" [ @@ -210,10 +255,17 @@ - Über das Betriebssystem, z. B. durch Verwendung von `sysfs` unter Linux - Über einen Adapter, z. B. ein Mock von Typen für Unit-Tests - Via-Treiber für Hardware-Adapter, z. B. I2C-Multiplexer oder GPIO-Expander +] else if lang == "zh" [ + 这样的一个 *HAL implementation* 可以有多个方法来实现: + - 通过低级硬件访问,比如通过寄存器。 + - 通过操作系统,比如通过使用Linux下的 `sysfs` + - 通过适配器,比如一个与单元测试有关的类型的仿真 + - 通过相关硬件适配器的驱动,e.g.~I2C多路复用器或者GPIO扩展器(I2C multiplexer or GPIO expander) ] else { todo } === #(if lang == "en" [Driver] else if lang == "de" [Treiber] + else if lang == "zh" [驱动] else { todo }) #if lang == "en" [ @@ -231,6 +283,9 @@ Sensoren (Temperatur-, Magnetometer-, Beschleunigungs- und Lichtsensoren), Anzeigegeräte (LED-Arrays, LCD-Displays) und Aktoren (Motoren, Sender). +] else if lang == "zh" [ + 驱动为一个外部或者内部组件实现了一组自定义的功能,被连接到一个实现了embedded-hal + traits的外设上。这种驱动的典型的例子包括多种传感器(温度计,磁力计,加速度计,光照计),显示设备(LED阵列,LCD显示屏)和执行器(电机,发送器)。 ] else { todo } #if lang == "en" [ @@ -245,10 +300,15 @@ implementiert. Dies wird durch Trait-Bound sichergestellt. Der Treiber stellt eine eigene Typinstanz mit einem benutzerdefinierten Satz von Methoden bereit, die die Interaktion mit dem angesteuerten Gerät +] else if lang == "zh" [ + 必须使用实现了embedded-hal的某个`trait`的类型的实例来初始化驱动,这是通过trait + bound来确保的,驱动也提供了它自己的类型实例,这个实例具有一组自定义的方法,这些方法允许与被驱动的设备交互。 + ] else { todo } === #(if lang == "en" [Application] else if lang == "de" [Anwendung] + else if lang == "zh" [应用] else { todo }) #if lang == "en" [ @@ -277,4 +337,7 @@ Nutzung interner Peripherie getroffen werden (etwa wenn mehrere Timer mit unterschiedlichen Fähigkeiten zur Verfügung stehen oder Konflikte zwischen verschiedenen Peripherieeinheiten auftreten). +] else if lang == "zh" [ + 应用把多个部分结合在一起并确保需要的功能被实现。当在不同的系统间移植时,这部分的适配是花费最多精力的地方,因为应用需要通过HAL + implementation正确地初始化真实的硬件,而且不同硬件的初始化也不相同,甚至有时候差别非常大。用户的选择也在其中扮演了非常重大的角色,因为组件能被物理连接到不同的端口,硬件总线有时候需要外部硬件去匹配配置,或者用户在内部外设的使用上有不同的考量。 ] else { todo } diff --git a/typ/start/panicking.typ b/typ/start/panicking.typ index 641045f1..3a67b053 100644 --- a/typ/start/panicking.typ +++ b/typ/start/panicking.typ @@ -3,6 +3,7 @@ #h1(offset: whole, if lang == "en" [Panicking] else if lang == "de" [In Panik geraten] + else if lang == "zh" [运行时恐慌(Panicking)] else { todo }) @@ -15,6 +16,8 @@ Rust. Eingebaute Operationen wie der Zugriff per Index werden zur Laufzeit auf Speichersicherheit überprüft. Erfolgt ein Zugriff außerhalb der zulässigen Grenzen, führt dies zu einer Panik. +] else if lang == "zh" [ + 运行时恐慌是Rust语言的一个核心部分。像是索引这样的内建的操作为了存储安全性是运行时检查的。当尝试越界索引时,这会导致运行时恐慌(panic)。 ] else { todo } #if lang == "en" [ @@ -26,6 +29,8 @@ Der Stack des betroffenen Threads wird abgewickelt (Stack Unwinding), es sei denn, der Benutzer hat sich für einen Programmabbruch im Falle einer Panik entschieden. +] else if lang == "zh" [ + 在标准库中,运行时恐慌的行为被定义成:展开(unwinds)恐慌的线程的栈,除非用户自己选择在恐慌时终止程序。 ] else { todo } #let ln_info = link("https://doc.rust-lang.org/core/panic/struct.PanicInfo.html")[`PanicInfo`] @@ -43,6 +48,12 @@ Abhängigkeitsgraphen des Programms genau _einmal_ vorkommen und folgende Signatur aufweisen: `fn(&PanicInfo) -> !`, wobei #ln_info eine Struktur ist, die Informationen über den Ort des Panic enthält. +] else if lang == "zh" [ + 然而在没有标准库的程序中,运行时恐慌的行为是未被定义了的。通过声明一个 + `#[panic_handler]` 函数可以选择一个运行时恐慌的行为。 + + 这个函数在一个程序的依赖图中必须只出现一次,且必须有这样的签名: + `fn(&PanicInfo) -> !`,`PanicInfo`是一个包含关于发生运行时恐慌的位置信息的结构体。 ] else { todo } #let ln_abort = link("https://crates.io/crates/panic-abort")[`panic-abort`] @@ -74,6 +85,18 @@ Cortex-M spezifischen Peripheriekomponente. - #ln_sh. Die Panik-Meldung wird mithilfe der Semihosting-Technik auf dem Host protokolliert. +] else if lang == "zh" [ + 鉴于嵌入式系统的范围从面向用户的系统到安全关键系统,没有一个运行时恐慌行为能满足所有场景,但是有许多常用的行为。这些常用的行为已经被打包进了一些crates中,这些crates中定义了 + `#[panic_handler]`函数。比如: + - #ln_abort. + 这个运行时恐慌会导致终止指令被执行。 + - #ln_halt. + 这个运行时恐慌会导致程序,或者现在的线程,通过进入一个无限循环中而挂起。 + - #ln_itm. + 运行时恐慌的信息会被ITM记录,ITM是一个ARM Cortex-M的特殊的外设。 + - #ln_sh. + 使用半主机技术,运行时恐慌的信息被记录到主机上。 + ] else { todo } #let ln_handler = link("https://crates.io/keywords/panic-handler")[`panic-handler`] @@ -83,6 +106,9 @@ ] else if lang == "de" [ Möglicherweise findest du noch mehr Crates, wenn du auf crates.io nach dem Schlüsselwort #ln_handler suchst. +] else if lang == "zh" [ + 在crates.io上搜索 + #ln_handler,你甚至可以找到更多的crates。 ] else { todo } #if lang == "en" [ @@ -98,6 +124,8 @@ ausgedrückt wird, ist nicht nur als Dokumentation nützlich, sondern ermöglicht es auch, dieses Verhalten je nach Kompilierungsprofil anzupassen. Zum Beispiel: +] else if lang == "zh" [ + 仅仅通过链接到相关的crate中,一个程序就可以简单地从这些行为中选择一个运行时恐慌行为。将运行时恐慌的行为作为一行代码放进一个应用的源码中,不仅仅是因为可以作为文档使用,而且能根据编译配置改变运行时恐慌的行为。比如: ] else { todo } #raw(block: true, lang: "rust", @@ -109,6 +137,8 @@ } else if lang == "de" { "dev-Profil: einfacheres Debuggen von Panics; man kann einen Breakpoint bei // `rust_begin_unwind` setzen." + } else if lang == "zh" { + "dev配置: 更容易调试运行时恐慌; 可以在 `rust_begin_unwind` 上放一个断点" } else { todos } + " #[cfg(debug_assertions)] use panic_halt as _; @@ -117,6 +147,8 @@ use panic_halt as _; "release profile: minimize the binary size of the application" } else if lang == "de" { "Release-Profil: Minimierung der Binaergroesse der Anwendung" + } else if lang == "zh" { + "release配置: 最小化应用的二进制文件的大小" } else { todos } + " #[cfg(not(debug_assertions))] use panic_abort as _; @@ -133,6 +165,11 @@ In diesem Beispiel verlinkt der Crate beim Bauen mit dem Dev-Profil (`cargo build`) auf den `panic-halt`-Crate, beim Bauen mit dem Release-Profil (`cargo build --release`) hingegen auf den `panic-abort`-Crate. +] else if lang == "zh" [ + 在这个例子里,当使用dev配置编译的时候(`cargo build`),crate链接到 + `panic-halt` + crate上,但是当使用release配置编译时(`cargo build --release`),crate链接到`panic-abort` + crate上。 ] else { todo } #quote(block: true)[ @@ -158,11 +195,19 @@ Release-Profil (`cargo build --release`) hingegen auf den nur noch für sogenannte „sysroot"-Crates (die gemeinsam mit Rust selbst ausgeliefert werden) -- wie etwa `proc_macro`, `alloc`, `std` und `test` -- verwendet werden sollte. +] else if lang == "zh" [ + `use panic_abort as _` 形式的 `use` 语句,被用来确保 `panic_abort` + 运行时恐慌函数被包含进我们最终的可执行程序里,同时让编译器清楚地知道我们不会从这个crate显式地使用任何东西。没有 + `_` 重命名,编译器将会警告我们有一个未使用的导入。有时候你可能会看到 + `extern crate panic_abort`,这是Rust + 2018之前的版本使用的更旧的写法,现在应该只被用于 "sysroot" crates + (与Rust一起发布的crates),比如 `proc_macro`,`alloc`,`std` 和 `test` 。 ] else { todo } ] = #(if lang == "en" [An example] else if lang == "de" [Ein Beispiel] + else if lang == "zh" [一个例子] else { todo }) #if lang == "en" [ @@ -171,6 +216,8 @@ Release-Profil (`cargo build --release`) hingegen auf den ] else if lang == "de" [ Hier ist ein Beispiel, das versucht, auf ein Array an einer Position zuzugreifen, die über dessen Länge hinausgeht. Der Vorgang führt zu einer Panic. +] else if lang == "zh" [ + 这里有一个尝试越界访问数组的例子。操作的结果导致了一个运行时恐慌(panic)。 ] else { todo } #raw(block: true, lang: "rust", @@ -189,6 +236,8 @@ fn main() -> ! { "out of bounds access" } else if lang == "de" { "Zugriff ausserhalb der zulaessigen Grenzen" + } else if lang == "zh" { + "out of bounds access" } else { todos } + " loop {} @@ -199,8 +248,10 @@ fn main() -> ! { This example chose the `panic-semihosting` behavior which prints the panic message to the host console using semihosting. ] else if lang == "de" [ -Für dieses Beispiel wurde das Verhalten `panic-semihosting` gewählt, das -die Panic-Meldung mittels Semihosting auf der Host-Konsole ausgibt. + Für dieses Beispiel wurde das Verhalten `panic-semihosting` gewählt, das + die Panic-Meldung mittels Semihosting auf der Host-Konsole ausgibt. +] else if lang == "zh" [ + 这个例子选择了`panic-semihosting`行为,运行时恐慌的信息会被打印至使用了半主机模式的主机控制台上。 ] else { todo } ```text @@ -213,6 +264,8 @@ panicked at 'index out of bounds: the len is 3 but the index is 4', src/main.rs: You can try changing the behavior to `panic-halt` and confirm that no message is printed in that case. ] else if lang == "de" [ -Sie können versuchen, das Verhalten auf `panic-halt` zu ändern, und -bestätigen, dass in diesem Fall keine Meldung ausgegeben wird. + Sie können versuchen, das Verhalten auf `panic-halt` zu ändern, und + bestätigen, dass in diesem Fall keine Meldung ausgegeben wird. +] else if lang == "zh" [ + 你可以尝试将行为改成`panic-halt`,确保在这个案例里没有信息被打印。 ] else { todo } diff --git a/typ/start/qemu.typ b/typ/start/qemu.typ index 7ec8d9d8..24efe17a 100644 --- a/typ/start/qemu.typ +++ b/typ/start/qemu.typ @@ -62,6 +62,8 @@ sodass Sie sich in diesem Abschnitt nicht mit der Hardware beschäftigen müssen und wir uns auf die Werkzeuge und den Entwicklungsprozess konzentrieren können. +] else if lang == "zh" [ + 我们将开始为#lm3s6965;编写程序,一个Cortex-M3微控制器。因为它能使用#link("https://wiki.qemu.org/Documentation/Platforms/ARM#Supported_in_qemu-system-arm")[QEMU仿真],所以我们选择它作为我们的第一个目标,本节中,不需要使用硬件,我们注意力可以集中在工具和开发过程上。 ] else { todo } #if lang == "en" [ @@ -75,10 +77,14 @@ den Namen ersetzen, den Sie für Ihr Projekt gewählt haben. Alternativ können Sie Ihr Projekt auch „app" nennen und so die Ersetzungen vermeiden. +] else if lang == "zh" [ + *重要* + 在这个引导里,我们将使用"app"这个名字来代指项目名。无论何时你看到单词"app",你应该用你选择的项目名来替代"app"。或者你也可以选择把你的项目命名为"app",避免要替换掉。 ] else { todo } = #(if lang == "en" [Creating a non standard Rust program] else if lang == "de" [Erstellen eines nicht standardmäßigen Rust-Programms] + else if lang == "zh" [生成一个非标准的 Rust program] else { todo }) #let ln_quick = link("https://github.com/rust-embedded/cortex-m-quickstart")[`cortex-m-quickstart`] @@ -96,16 +102,21 @@ eine neue Embedded-Rust-Anwendung. Darüber hinaus enthält das Projekt ein Verzeichnis `examples` mit mehreren separaten Anwendungen, die einige der wichtigsten Funktionen von Embedded Rust veranschaulichen. +] else if lang == "zh" [ + 我们将使用#ln_quick;项目模板来生成一个新项目。生成的项目将包含一个最基本的应用:对于一个新的嵌入式rust应用来说,是一个很好的开始。另外,项目将包含一个`example`文件夹,文件夹中有许多独立的应用,突出了一些关键的嵌入式rust的功能。 ] else { todo } == #(if lang == "en" [Using `cargo-generate`] else if lang == "de" [Verwendung von `cargo-generate`] + else if lang == "zh" [使用 `cargo-generate`] else { todo }) #if lang == "en" [ First install cargo-generate ] else if lang == "de" [ Installieren Sie zunächst cargo-generate +] else if lang == "zh" [ + 首先安装 cargo-generate ] else { todo } ```console @@ -116,6 +127,8 @@ cargo install cargo-generate Then generate a new project ] else if lang == "de" [ Erstellen Sie anschließend ein neues Projekt +] else if lang == "zh" [ + 然后生成一个新项目 ] else { todo } ```console @@ -134,12 +147,15 @@ cd app == #(if lang == "en" [Using `git`] else if lang == "de" [Verwendung von `git`] + else if lang == "zh" [使用 `git`] else { todo }) #if lang == "en" [ Clone the repository ] else if lang == "de" [ Das Repository klonen +] else if lang == "zh" [ + 克隆仓库 ] else { todo } ```console @@ -151,6 +167,8 @@ cd app And then fill in the placeholders in the `Cargo.toml` file ] else if lang == "de" [ Und fülle dann die Platzhalter in der Datei `Cargo.toml` aus +] else if lang == "zh" [ + 然后补充`Cargo.toml`文件中的占位符 ] else { todo } ```toml @@ -170,6 +188,7 @@ bench = false == #(if lang == "en" [Using neither] else if lang == "de" [Keines von beiden verwenden] + else if lang == "zh" [要么使用] else { todo }) #if lang == "en" [ @@ -178,6 +197,8 @@ bench = false ] else if lang == "de" [ Laden Sie den neuesten Snapshot der Vorlage `cortex-m-quickstart` herunter und entpacken Sie ihn. +] else if lang == "zh" [ + 抓取最新的 `cortex-m-quickstart` 模板,解压它。 ] else { todo } ```console @@ -191,8 +212,10 @@ cd app Or you can browse to #ln_quick, click the green "Clone or download" button and then click "Download ZIP". ] else if lang == "de" [ Oder Sie navigieren zu #ln_quick, - klicken auf die grüne Schaltfläche „Clone or download" und anschließend - auf „Download ZIP". + klicken auf die grüne Schaltfläche „Clone or download" und anschließend auf „Download ZIP". +] else if lang == "zh" [ + 或者你可以浏览#ln_quick,点击绿色的 + "Clone or download" 按钮,然后点击 "Download ZIP" 。 ] else { todo } #if lang == "en" [ @@ -201,10 +224,13 @@ cd app ] else if lang == "de" [ Füllen Sie anschließend die Platzhalter in der Datei `Cargo.toml` aus, wie im zweiten Teil der Version „Verwendung von `git` beschrieben. +] else if lang == "zh" [ + 然后像在 "使用 `git`" 那里的第二部分写的那样填充 `Cargo.toml` 。 ] else { todo } = #(if lang == "en" [Program Overview] else if lang == "de" [Programmübersicht] + else if lang == "zh" [项目概览] else { todo }) #if lang == "en" [ @@ -212,6 +238,8 @@ cd app ] else if lang == "de" [ Der Einfachheit halber sind hier die wichtigsten Teile des Quellcodes in `src/main.rs` aufgeführt: +] else if lang == "zh" [ + 这是`src/main.rs`中源码最重要的部分。 ] else { todo } #raw(block: true, lang: "rust", @@ -229,6 +257,8 @@ fn main() -> ! { "your code goes here" } else if lang == "de" { "Hier kommt Ihr Code hin." + } else if lang == "zh" { + "your code goes here" } else { todos } + " } } @@ -240,6 +270,8 @@ fn main() -> ! { ] else if lang == "de" [ Dieses Programm unterscheidet sich ein wenig von einem typischen Rust-Programm, schauen wir es uns also einmal genauer an. +] else if lang == "zh" [ + 这个程序与标准Rust程序有一点不同,让我们走近点看看。 ] else { todo } #if lang == "en" [ @@ -249,6 +281,9 @@ fn main() -> ! { `#![no_std]` gibt an, dass dieses Programm _nicht_ mit der Standard-Crate `std` verknüpft wird. Stattdessen wird es mit deren Teilmenge verknüpft: der Crate `core`. +] else if lang == "zh" [ + `#![no_std]`指出这个程序将 _不会_ + 链接标准crate`std`。反而它将会链接到它的子集: `core` crate。 ] else { todo } #if lang == "en" [ @@ -262,6 +297,9 @@ fn main() -> ! { Hauptgrund (kein Wortspiel beabsichtigt) für die Verwendung von `no_main` ist, dass die Nutzung der `main`-Schnittstelle im `no_std`-Kontext „Nightly" erfordert. +] else if lang == "zh" [ + `#![no_main]`指出这个程序将不会使用标准的且被大多数Rust程序使用的`main`接口。使用`no_main`的主要理由是,在`no_std`上下文中使用`main`接口需要 + nightly 版的 Rust。 ] else { todo } #if lang == "en" [ @@ -273,6 +311,8 @@ fn main() -> ! { der das Verhalten des Programms im Panikfall definiert. Wir werden darauf im Kapitel #link()[In Panik geraten] des Buches näher eingehen. +] else if lang == "zh" [ + `use panic_halt as _;`。这个crate提供了一个`panic_handler`,它定义了程序陷入`panic`时的行为。我们将会在这本书的#link()[运行时恐慌(Panicking)]章节中覆盖更多的细节。 ] else { todo } #let ln_entry = link("https://docs.rs/cortex-m-rt-macros/latest/cortex_m_rt_macros/attr.entry.html")[`#[entry]`] @@ -288,6 +328,8 @@ fn main() -> ! { nicht die Standard-Schnittstelle `main` verwenden, benötigen wir eine andere Möglichkeit, den Einstiegspunkt des Programms anzugeben, und das wäre `#[entry]`. +] else if lang == "zh" [ + #ln_entry 是一个由#ln_rt;提供的属性,它用来标记程序的入口。当我们不使用标准的`main`接口时,我们需要其它方法来指示程序的入口,那就是`#[entry]`。 ] else { todo } #let url_div = "https://doc.rust-lang.org/rust-by-example/fn/diverging.html" @@ -303,10 +345,15 @@ fn main() -> ! { Wir verwenden eine #link(url_div)[divergent function] (das `-> !` in der Funktionssignatur), um bereits zur Kompilierungszeit sicherzustellen, dass dies auch der Fall ist. +] else if lang == "zh" [ + `fn main() -> !`。我们的程序将会是运行在目标板子上的 _唯一_ + 的进程,因此我们不想要它结束!我们使用一个#link(url_div)[发散函数] + (函数签名中的 `-> !` )来确保在编译时就是这么回事儿。 ] else { todo } = #(if lang == "en" [Cross compiling] else if lang == "de" [Cross-Kompilierung] + else if lang == "zh" [交叉编译] else { todo }) #if lang == "en" [ @@ -404,13 +451,15 @@ initialisiert wird! */" mit dem Befehl `cargo build --target $TRIPLE`, sofern Sie wissen, wie das Kompilierungsziel (`$TRIPLE`) lauten soll. Glücklicherweise finden Sie die Antwort in der Datei `.cargo/config.toml` in der Vorlage: +] else if lang == "zh" [ + 下一步是为Cortex-M3架构_交叉_编译程序。如果你知道编译目标(`$TRIPLE`)应该是什么,运行`cargo build --target $TRIPLE`就可以了。幸运地,模板中的`.cargo/config.toml`有这个答案: ] else { todo } ```console tail -n6 .cargo/config.toml ``` -#if lang == "en" [ +#if lang in ("en", "zh") [ ```toml [build] # Pick ONE of these compilation targets @@ -441,6 +490,8 @@ tail -n6 .cargo/config.toml der Rust-Werkzeuge nicht automatisch installiert. Jetzt wäre ein guter Zeitpunkt, dieses Ziel zu den Werkzeugen hinzuzufügen, falls Sie dies noch nicht getan haben: +] else if lang == "zh" [ + 为了交叉编译Cortex-M3架构我们不得不使用`thumbv7m-none-eabi`。当安装Rust工具时,target不会自动被安装,如果还没有添加,现在可以去添加那个target到工具链上。 ] else { todo } ```console @@ -454,6 +505,8 @@ rustup target add thumbv7m-none-eabi Da das Kompilierungsziel `thumbv7m-none-eabi` in Ihrer Datei `.cargo/config.toml` als Standard festgelegt wurde, haben die beiden folgenden Befehle dieselbe Wirkung: +] else if lang == "zh" [ + 因为`thumbv7m-none-eabi`编译目标在你的`.cargo/config.toml`中被设置成默认值,下面的两个命令是一样的效果: ] else { todo } ```console @@ -463,6 +516,7 @@ cargo build = #(if lang == "en" [Inspecting] else if lang == "de" [Überprüfen] + else if lang == "zh" [检查] else { todo }) #if lang == "en" [ @@ -473,6 +527,8 @@ cargo build Nun haben wir eine nicht-native ELF-Binärdatei in `target/thumbv7m-none-eabi/debug/app`. Wir können sie mit `cargo-binutils` untersuchen. +] else if lang == "zh" [ + 现在在`target/thumbv7m-none-eabi/debug/app`中有一个非主机环境的ELF二进制文件。我们能使用`cargo-binutils`检查它。 ] else { todo } #if lang == "en" [ @@ -481,6 +537,8 @@ cargo build ] else if lang == "de" [ Mit `cargo-readobj` können wir die ELF-Header ausgeben, um zu überprüfen, ob es sich um eine ARM- Binärdatei handelt. +] else if lang == "zh" [ + 使用`cargo-readobj`我们能打印ELF头,确认这是一个ARM二进制。 ] else { todo } ```console @@ -496,6 +554,10 @@ cargo readobj --bin app -- --file-headers - `--bin app` ist eine vereinfachte Schreibweise für die Überprüfung der Binärdatei unter `target/$TRIPLE/debug/app` - `--bin app` kompiliert die Binärdatei bei Bedarf auch (neu) +] else if lang == "zh" [ + 注意: + - `--bin app` 是一个用来查看二进制项`target/$TRIPLE/debug/app`的语法糖 + - `--bin app` 需要时也会重新编译二进制项。 ] else { todo } ```text @@ -526,6 +588,8 @@ ELF Header: ] else if lang == "de" [ Mit `cargo-size` lässt sich die Größe der Linker-Abschnitte der Binärdatei anzeigen. +] else if lang == "zh" [ + `cargo-size` 能打印二进制项的linker section的大小。 ] else { todo } ```console @@ -536,6 +600,8 @@ cargo size --bin app --release -- -A we use `--release` to inspect the optimized version ] else if lang == "de" [ Wir verwenden `--release`, um die optimierte Version zu überprüfen. +] else if lang == "zh" [ + 我们使用`--release`查看优化后的版本 ] else { todo } ```text @@ -586,6 +652,15 @@ Total 14570 - Die Abschnitte `.ARM.attributes` und `.debug_*` enthalten Metadaten und werden _nicht_ beim Flashen der Binärdatei auf das Zielgerät geladen werden. +] else if lang == "zh" [ + ELF linker sections的复习 + - `.text` 包含程序指令 + - `.rodata` 包含像是字符串这样的常量 + - `.data` 包含静态分配的初始值_非_零的变量 + - `.bss` 也包含静态分配的初始值_是_零的变量 + - `.vector_table` 是一个我们用来存储向量(中断)表的_非_标准的section + - `.ARM.attributes` 和 `.debug_*` + sections包含元数据,当烧录二进制文件时,它们不会被加载到目标上。 ] else { todo } ] @@ -601,26 +676,34 @@ Total 14570 beanspruchen wird, wenn es auf ein Gerät geflasht wird. Verwenden Sie _immer_ `cargo-size`, um zu überprüfen, wie groß eine Binärdatei tatsächlich ist. -] +] else if lang == "zh" [ + *重要*: + ELF文件包含像是调试信息这样的元数据,因此它们在_硬盘上的尺寸_没有正确地反应处程序被烧录到设备上时将占据的空间的大小。要_一直_使用`cargo-size`检查一个二进制项的大小。 +] else { todo } #if lang == "en" [ `cargo-objdump` can be used to disassemble the binary. ] else if lang == "de" [ Mit `cargo-objdump` lässt sich die Binärdatei disassemblieren. -] +] else if lang == "zh" [ +`cargo-objdump` 能用来反编译二进制项。 +] else { todo } ```console cargo objdump --bin app --release -- --disassemble --no-show-raw-insn --print-imm-hex ``` #quote(block: true)[ -#let ln = link("https://github.com/rust-embedded/book/issues/269") +#let ln_issue269 = link("https://github.com/rust-embedded/book/issues/269") #if lang == "en" [ *NOTE* if the above command complains about - `Unknown command line argument` see the following bug report: #ln + `Unknown command line argument` see the following bug report: #ln_issue269 ] else if lang == "de" [ *HINWEIS*: Falls der obige Befehl die Fehlermeldung „Unbekanntes - Befehlszeilenargument" ausgibt, siehe den folgenden Fehlerbericht: #ln + Befehlszeilenargument" ausgibt, siehe den folgenden Fehlerbericht: #ln_issue269 +] else if lang == "zh" [ + *注意* 如果上面的命令抱怨 `Unknown command line argument` + 看下面的bug报告:#ln_issue269 ] else { todo } ] @@ -634,6 +717,9 @@ cargo objdump --bin app --release -- --disassemble --no-show-raw-insn --print-im Versionen von rustc, LLVM und Bibliotheken können unterschiedlichen Assemblercode erzeugen. Wir haben einige der Befehle gekürzt, um den Ausschnitt kurz zu halten. +] else if lang == "zh" [ + *注意* 在你的系统上这个输出可能不一样。rustc, LLVM + 和库的新版本能产出不同的汇编。我们截取了一些指令 ] else { todo } ] @@ -678,6 +764,7 @@ HardFault: = #(if lang == "en" [Running] else if lang == "de" [Ausführen] + else if lang == "zh" [运行] else { todo }) #if lang == "en" [ @@ -689,6 +776,10 @@ HardFault: QEMU ausführt! Diesmal verwenden wir das `hello`-Beispiel, das tatsächlich etwas tut. Standardmäßig nutzt dieses Beispiel `[defmt]` und RTT, um Text auszugeben. +] else if lang == "zh" [ + #todoupd("zh") + 接下来,让我们看一个嵌入式程序是如何在QEMU上运行的!此刻我们将使用 + `hello` 示例,来做些真正的事。 ] else { todo } #quote(block: true)[ @@ -755,6 +846,8 @@ cargo build --bin hello ] else if lang == "de" [ Um diese Binärdatei unter QEMU auszuführen, reicht in der Regel der folgende Befehl aus: +] else if lang == "zh" [ + 为了在QEMU上运行这个二进制项,执行下列的命令: ] else { todo } ```console @@ -839,6 +932,20 @@ echo $? und die Host-Eingabe (stdin) zu nutzen sowie Dateien auf dem Host zu erstellen. - `-kernel $file`. Damit wird QEMU mitgeteilt, welche Binärdatei auf der emulierten Maschine geladen und ausgeführt werden soll. +] else if lang == "zh" [ + #todoupd("zh") + 让我们看看QEMU命令: + - `qemu-system-arm`。这是QEMU仿真器。这些QEMU二进制项有一些变体,这个仿真器能做ARM机器的全系统仿真。 + - `-cpu cortex-m3`。这告诉QEMU去仿真一个Cortex-M3 + CPU。指定CPU模型会让我们捕捉到一些误编译错误:比如,运行一个为Cortex-M4F编译的程序,它具有一个硬件FPU,在执行时将会使QEMU报错。 + - `-machine lm3s6965evb`。这告诉QEMU去仿真 + LM3S6965EVB,一个包含LM3S6965微控制器的评估板。 + - `-nographic`。这告诉QEMU不要启动它的GUI。 + - `-semihosting-config (..)`。这告诉QEMU使能半主机模式。半主机模式允许被仿真的设备,使用主机的stdout,stderr,和stdin,并在主机上创建文件。 + - `-kernel $file`。这告诉QEMU在仿真机器上加载和运行哪个二进制项。 + + 输入这么长的QEMU命令太费功夫了!我们可以设置一个自定义运行器(runner)简化步骤。`.cargo/config.toml` + 有一个被注释掉的,可以调用QEMU的运行器。让我们去掉注释。 ] else { todo } #if lang == "en" [ @@ -875,6 +982,10 @@ runner = \"qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semih Dieser Runner gilt nur für das Ziel `thumbv7m-none-eabi`, das unser Standard-Kompilierungsziel ist. Nun kompiliert `cargo run` das Programm und führt es auf QEMU aus: +] else if lang == "zh" [ + 这个运行器只会应用于 `thumbv7m-none-eabi` + 目标,它是我们的默认编译目标。现在 `cargo run` + 将会编译程序且在QEMU上运行它。 ] else { todo } ```console @@ -892,6 +1003,7 @@ Hello, world! = #(if lang == "en" [Debugging] else if lang == "de" [Fehlerbehebung (Debugging)] + else if lang == "zh" [调试] else { todo }) #if lang == "en" [ @@ -899,6 +1011,8 @@ Hello, world! ] else if lang == "de" [ Das Debuggen ist für die Embedded-Entwicklung von entscheidender Bedeutung. Schauen wir uns einmal an, wie es funktioniert. +] else if lang == "zh" [ + 对于嵌入式开发来说,调试非常重要。让我们来看下如何调试它。 ] else { todo } #if lang == "en" [ @@ -909,6 +1023,9 @@ Hello, world! Das Debuggen eines Embedded-Geräts erfolgt _remote_, da das Programm, das wir debuggen möchten, nicht auf dem Rechner läuft, auf dem das Debugger-Programm (GDB oder LLDB) ausgeführt wird. +] else if lang == "zh" [ + 因为我们想要调试的程序所运行的机器上并没有运行一个调试器程序(GDB或者LLDB),所以调试一个嵌入式设备就涉及到了 + _远程_ 调试 ] else { todo } #if lang == "en" [ @@ -919,18 +1036,25 @@ Hello, world! Beim Remote-Debugging sind ein Client und ein Server beteiligt. In einer QEMU-Umgebung ist der Client ein GDB- (oder LLDB-)Prozess und der Server der QEMU-Prozess, auf dem auch das eingebettete Programm läuft. +] else if lang == "zh" [ + 远程调试涉及一个客户端和一个服务器。在QEMU的情况中,客户端将是一个GDB(或者LLDM)进程且服务器将会是运行着嵌入式程序的QEMU进程。 + ] else { todo } #if lang == "en" [ In this section we'll use the `hello` example we already compiled. ] else if lang == "de" [ In diesem Abschnitt verwenden wir das bereits kompilierte Beispiel `hello`. +] else if lang == "zh" [ + 在这部分,我们要使用我们已经编译的 `hello` 示例。 ] else { todo } #if lang == "en" [ The first debugging step is to launch QEMU in debugging mode: ] else if lang == "de" [ Der erste Schritt beim Debuggen besteht darin, QEMU im Debugging-Modus zu starten: +] else if lang == "zh" [ + 调试的第一步是在调试模式中启动QEMU: ] else { todo } ```console @@ -961,6 +1085,10 @@ qemu-system-arm \ einzufrieren. Ohne diese Option hätte das Programm das Ende der Funktion `main` erreicht, bevor wir die Gelegenheit gehabt hätten, den Debugger zu starten! +] else if lang == "zh" [ + 这个命令将不打印任何东西到调试台上,且将会阻塞住终端。此刻我们还传递了两个额外的标志。 + - `-gdb tcp::3333`。这告诉QEMU在3333的TCP端口上等待一个GDB连接。 + - `-S`。这告诉QEMU在启动时,冻结机器。没有这个,在我们有机会启动调试器之前,程序有可能已经到达了主程序的底部了! ] else { todo } #if lang == "en" [ @@ -969,6 +1097,8 @@ qemu-system-arm \ ] else if lang == "de" [ Als Nächstes starten wir GDB in einem anderen Terminal und weisen es an, die Debug-Symbole des Beispiels zu laden: +] else if lang == "zh" [ + 接下来我们在另一个终端启动GDB,且告诉它去加载示例的调试符号。 ] else { todo } ```console @@ -984,6 +1114,10 @@ gdb-multiarch -q target/thumbv7m-none-eabi/debug/examples/hello `gdb-multiarch` eine andere Version von gdb, je nachdem, welche Sie im Kapitel zur Installation installiert haben. Dies könnte auch `arm-none-eabi-gdb` oder einfach nur `gdb` sein. +] else if lang == "zh" [ + *注意*: 你可能需要另一个gdb版本而不是 + `gdb-multiarch`,取决于你在安装章节中安装了哪个。这个可能是 + `arm-none-eabi-gdb` 或者只是 `gdb`。 ] else { todo } #if lang == "en" [ @@ -992,6 +1126,9 @@ gdb-multiarch -q target/thumbv7m-none-eabi/debug/examples/hello ] else if lang == "de" [ Anschließend stellen wir innerhalb der GDB-Shell eine Verbindung zu QEMU her, das auf dem TCP-Port 3333 auf eine Verbindung wartet. +] else if lang == "zh" [ + 然后在GDB shell中,我们连接QEMU,QEMU正在等待一个在3333 + TCP端口上的连接。 ] else { todo } ```console @@ -1012,6 +1149,9 @@ Reset () at $REGISTRY/cortex-m-rt-0.6.1/src/lib.rs:473 Sie werden feststellen, dass der Prozess angehalten wurde und der Programmzähler auf eine Funktion namens `Reset` zeigt. Das ist der Reset-Handler: das, was Cortex-M-Kerne beim Booten ausführen. +] else if lang == "zh" [ + 你将看到,进程被挂起了,程序计数器正指向一个名为 `Reset` 的函数。那是 + reset 句柄:Cortex-M 内核在启动时执行的中断函数。 ] else { todo } #quote(block: true)[ @@ -1023,6 +1163,8 @@ Reset () at $REGISTRY/cortex-m-rt-0.6.1/src/lib.rs:473 Beachten Sie, dass gdb in manchen Konfigurationen anstelle der oben gezeigten Zeile `Reset () at $REGISTRY/cortex-m-rt-0.6.1/src/lib.rs:473` möglicherweise Warnungen wie die folgenden ausgibt: +] else if lang == "zh" [ + 注意在一些配置中,可能不会像上面一样,显示`Reset() at $REGISTRY/cortex-m-rt-0.6.1/src/lib.rs:473`,gdb可能打印一些警告,比如: ] else { todo } `core::num::bignum::Big32x40::mul_small () at src/libcore/num/bignum.rs:254` @@ -1034,6 +1176,8 @@ Reset () at $REGISTRY/cortex-m-rt-0.6.1/src/lib.rs:473 ] else if lang == "de" [ Das ist ein bekannter Fehler. Sie können diese Warnungen getrost ignorieren, da Sie sich höchstwahrscheinlich bei `Reset()` befinden. +] else if lang == "zh" [ + 那是一个已知的小bug,你可以安全地忽略这些警告,你非常大可能已经进入Reset()了。 ] else { todo } ] @@ -1048,6 +1192,8 @@ Reset () at $REGISTRY/cortex-m-rt-0.6.1/src/lib.rs:473 Befehls `continue` überspringen. Um den Haltepunkt zu setzen, schauen wir uns zunächst mit dem Befehl `list` an, an welcher Stelle in unserem Code wir anhalten möchten. +] else if lang == "zh" [ + 这个reset句柄最终将调用我们的主函数,让我们使用一个断点和`continue`命令跳过所有的步骤。为了设置断点,让我们首先看下我们想要在我们代码哪里打断点,使用`list`指令 ] else { todo } ```console @@ -1058,6 +1204,8 @@ list main This will show the source code, from the file examples/hello.rs. ] else if lang == "de" [ Dadurch wird der Quellcode aus der Datei „examples/hello.rs" angezeigt. +] else if lang == "zh" [ + 这将显示从examples/hello.rs文件来的源代码。 ] else { todo } ```text @@ -1079,6 +1227,9 @@ list main ] else if lang == "de" [ Wir möchten einen Haltepunkt direkt vor „Hello, world!" setzen, das sich in Zeile 13 befindet. Dazu verwenden wir den Befehl `break`: +] else if lang == "zh" [ + 我们想要在"Hello, + world!"之前添加一个断点,在13行那里。我们可以使用`break`命令 ] else { todo } ```console @@ -1091,6 +1242,8 @@ break 13 ] else if lang == "de" [ Wir können gdb nun mit dem Befehl `continue` anweisen, bis zu unserer Hauptfunktion weiterzulaufen: +] else if lang == "zh" [ + 我们现在能使用`continue`命令指示gdb运行到我们的主函数。 ] else { todo } ```console @@ -1110,6 +1263,8 @@ Breakpoint 1, hello::__cortex_m_rt_main () at examples\hello.rs:13 ] else if lang == "de" [ Wir sind nun fast bei dem Code angelangt, der „Hello, world!" ausgibt. Machen wir weiter mit dem Befehl `next`. +] else if lang == "zh" [ + 我们现在靠近打印"Hello, world!"的代码。让我们使用`next`命令继续前进。 ] else { todo } ```console @@ -1126,6 +1281,8 @@ next ] else if lang == "de" [ An dieser Stelle sollte auf dem Terminal, auf dem `qemu-system-arm` läuft, „Hello, world!" angezeigt werden. +] else if lang == "zh" [ + 在这里,你应该看到 "Hello, world!" 被打印到正在运行 `qemu-system-arm` 的终端上。 ] else { todo } ```text @@ -1137,6 +1294,8 @@ Hello, world! Calling `next` again will terminate the QEMU process. ] else if lang == "de" [ Ein erneuter Aufruf von `next` beendet den QEMU-Prozess. +] else if lang == "zh" [ + 再次调用`next`将会终止QEMU进程。 ] else { todo } ```console @@ -1151,6 +1310,8 @@ next You can now exit the GDB session. ] else if lang == "de" [ Sie können die GDB-Sitzung nun beenden. +] else if lang == "zh" [ + 你现在能退出GDB的会话了。 ] else { todo } ```console diff --git a/typ/start/registers.typ b/typ/start/registers.typ index 376c3bf1..ea127043 100644 --- a/typ/start/registers.typ +++ b/typ/start/registers.typ @@ -3,6 +3,7 @@ #h1(offset: whole, if lang == "en" [Memory Mapped Registers] else if lang == "de" [Im Speicher abgebildete Register] + else if lang == "zh" [存储映射的寄存器(Memory-Mapped Registers)] else { todo }) @@ -22,6 +23,8 @@ externen Peripheriekomponente über einen Bus --, müssen wir uns mit der Welt der Peripherieeinheiten und deren „im Speicher abgebildeten Registern" (memory-mapped registers) befassen. +] else if lang == "zh" [ + 嵌入式系统想要继续执行下去,只有通过执行常规的Rust代码并在RAM间移动数据才行。如果我们想要获取或者发出信息(点亮一个LED,发现一个按钮按下或者在总线上与芯片外设通信),我们不得不深入了解外设和它们的"存储映射的寄存器"。 ] else { todo } #let ln_cortex = link("https://crates.io/crates/cortex-m")[cortex-m] @@ -98,10 +101,27 @@ passend für das jeweils verwendete Entwickler-Kit oder Board vorkonfigurieren -- wie zum Beispiel #ln_f3 für das STM32F3DISCOVERY-Board. +] else if lang == "zh" [ + 你可能会发现,访问你的微控制器外设所需要的代码,已经存在于下面的某个抽象层中了。 + - Micro-architecture Crate(微架构库) - + 这个库拥有任何对于微控制器的处理器内核来说经常会用到的程序,也包括在这些微控制器中的通用外设。比如 + #ln_cortex + crate提供给你可以使能和关闭中断的函数,其对于所有的Cortex-M微控制器都是一样的。它也提供你访问'SysTick'外设的能力,在所有的Cortex-M微控制器中都包括了这个外设功能。 + - Peripheral Access Crate(PAC)(外设访问库) - + 这个库是对各种存储器封装的寄存器再进行的一次浅陋封装,特定于所使用的微控制器的产品号。比如,#ln_tm4c123x;针对TI的Tiva-C + TM4C123系列,#ln_f30x;针对ST的STM32F30x系列。这块,根据微控制器的技术手册写的每个外设操作指令,直接和寄存器交互。 + - HAL Crate - + 这些crates为你的处理器提供了一个更友好的API,通常是通过实现在#ln_hal;中定义的一些常用的traits来实现的。比如,这个crate可能提供一个`Serial`结构体,它的构造函数需要一组合适的GPIO端口和一个波特率,它为发送数据提供了 + `write_byte` 函数。查看 #link()[可移植性] + 可以看到更多关于 + #ln_hal 的信息。 + - Board Crate(开发板库) - + 这些Crate通过预配置不同的外设和GPIO管脚再进行了一层抽象以适配你正在使用的特定的开发者工具或者开发板,比如对于STM32F3DISCOVERY开发板来说,是#ln_f3 ] else { todo } = #(if lang == "en" [Board Crate] else if lang == "de" [Board Crate] + else if lang == "zh" [开发板Crate (Board Crate)] else { todo }) #if lang == "en" [ @@ -119,6 +139,10 @@ bereitgestellte Funktionsumfang variiert jedoch stark von Board zu Board. Da dieses Buch hardwareunabhängig bleiben soll, werden Board-Crates hier nicht behandelt. +] else if lang == "zh" [ + 如果你是嵌入式Rust新手,board + crate是一个完美的开始。它们很好地抽象出了,在开始学习这个项目时,需要耗费心力了解的硬件细节,使得标准工作,像是打开或者关闭LED,变得简单。不同的板子间,它们提供的功能变化很大。因为这本书是不假设我们使用的是何种板子,所以这本书不会提到board + crate。 ] else { todo } #let url_disco = "https://rust-embedded.github.io/discovery/" @@ -136,6 +160,9 @@ lassen sowie auf den Kompass, Bluetooth und mehr zuzugreifen. Das #link(url_disco)[Discovery]-Buch bietet eine hervorragende Einführung in die Verwendung eines Board-Crates. +] else if lang == "zh" [ + 如果你想要用STM32F3DISCOVERY开发板做实验,强烈建议看一下#ln_f3;开发板crate,它提供了闪烁LEDs,访问它的指南针,蓝牙和其它的功能。#link(url_disco)[Discovery]书对于一个board + crate的用法提供一个很好的介绍。 ] else { todo } #if lang == "en" [ @@ -147,10 +174,14 @@ dediziertes Board-Crate gibt, oder wenn Sie Funktionen benötigen, die von vorhandenen Crates nicht abgedeckt werden, lesen Sie weiter: Wir beginnen ganz unten, bei den Mikroarchitektur-Crates. +] else if lang == "zh" [ + 但是如果你正在使用一个还没有提供专用的board + crate的系统,或者你需要的一些功能,现存的crates不提供,那我们需要从底层的微架构crates开始。 ] else { todo } = #(if lang == "en" [Micro-architecture crate] else if lang == "de" [Mikroarchitektur-Crate] + else if lang == "zh" [Micro-architecture crate] else { todo }) #if lang == "en" [ @@ -161,6 +192,9 @@ Betrachten wir die SysTick-Peripherie, die allen Mikrocontrollern auf Cortex-M-Basis gemeinsam ist. Im #ln_cortex;-Crate finden wir eine recht hardwarenahe API, die sich folgendermaßen verwenden lässt: +] else if lang == "zh" [ + 让我们看一下SysTick外设,SysTick外设存在于所有的Cortex-M微控制器中。我们能在#ln_cortex + crate中找到一个相当底层的API,我们能像这样使用它: ] else { todo } ```rust @@ -207,10 +241,16 @@ fn main() -> ! { sicherstellt, dass im gesamten Programm nur eine einzige `SYST`-Instanz existiert. Weitere Informationen dazu finden Sie im Abschnitt "#link()[Peripherien]". +] else if lang == "zh" [ + `SYST`结构体上的功能,相当接近ARM技术手册为这个外设定义的功能。在这个API中没有关于 + '延迟X毫秒' 的功能 - 我们不得不通过使用一个 `while` + 循环来粗略地实现它。注意,我们调用了`Peripherals::take()`才能访问我们的`SYST`结构体 + - 这是一个特别的程序,保障了在我们的整个程序中只存在一个`SYST`结构体实例,更多的信息可以看#link()[外设]部分。 ] else { todo } = #(if lang == "en" [Using a Peripheral Access Crate (PAC)] else if lang == "de" [Verwendung eines Peripheral Access Crate (PAC)] + else if lang == "zh" [使用一个外设访问Crate (PAC)] else { todo }) #if lang == "en" [ @@ -230,6 +270,11 @@ fn main() -> ! { TM4C123 verwenden -- einen soliden 80-MHz-Cortex-M4 mit 256 KiB Flash-Speicher. Um diesen Chip nutzen zu können, binden wir das #ln_tm4c123x;-Crate ein. +] else if lang == "zh" [ + 如果我们把自己只局限于每个Cortex-M拥有的基本外设,那我们的嵌入式软件开发将不会走得太远。我们准备需要写一些特定于我们正在使用的微控制器的代码。在这个例子里,让我们假设我们有一个TI的TM4C123 + - 一个有256KiB + Flash的中等规模的80MHz的Cortex-M4。我们用#ln_tm4c123x + crate去使用这个芯片。 ] else { todo } ```rust @@ -278,7 +323,7 @@ pub fn init() -> (Delay, Leds) { Wir haben auf die `PWM0`-Peripherie auf genau dieselbe Weise zugegriffen wie zuvor auf die `SYST`-Peripherie, mit dem Unterschied, dass wir `tm4c123x::Peripherals::take()` aufgerufen haben. Da dieses Crate - mithilfe von #link("https://crates.io/crates/svd2rust")[svd2rust] + mithilfe von #ln_svd2rust automatisch generiert wurde, erwarten die Zugriffsfunktionen für unsere Registerfelder einen Closure anstelle eines numerischen Arguments. Auch wenn dies nach viel Code aussieht, kann der Rust-Compiler ihn nutzen, um @@ -291,9 +336,14 @@ pub fn init() -> (Delay, Leds) { besondere Bedeutung haben), wird die Funktion als `unsafe` markiert. Dies lässt sich im obigen Beispiel beim Setzen der Unterfelder `load` und `compa` mittels der Funktion `bits()` beobachten. +] else if lang == "zh" [ + 我们访问 `PWM0` 外设的方法和我们之前访问 `SYST` + 的方法一样,除了我们调用的是 `tm4c123x::Peripherals::take()` + 之外。因为这个crate是使用#ln_svd2rust;自动生成的,访问我们寄存器位段的函数的参数是一个闭包,而不是一个数值参数。虽然这看起来像是有了更多的代码,但是Rust编译器能使用这个闭包为我们执行一系列检查,且产生的机器码十分接近手写的汇编码!如果自动生成的代码不能确保某个访问函数其所有可能的参数都能发挥作用(比如,如果寄存器被SVD定义为32位,但是没有说明某些32位值是否有特殊作用),那么该函数需要被标记为 + `unsafe` 。我们能在上面看到这样的例子,我们使用 `bits()` 函数设置 `load` 和 `compa` 子域。 ] else { todo } -== #(if lang == "en" [Reading] +== #(if lang in ("en", "zh") [Reading] else if lang == "de" [Lesen] else { todo }) @@ -313,11 +363,17 @@ pub fn init() -> (Delay, Leds) { Registers (innerhalb der jeweiligen Peripherieeinheit auf diesem Chip) verfügbaren Funktionen finden Sie in der #link(url_struct_r)[tm4c123x-Dokumentation]. +] else if lang == "zh" [ + `read()` + 函数返回一个对象,这个对象提供了对这个寄存器中不同子域的只读访问,由厂商提供的这个芯片的SVD文件定义。在 + #link(url_struct_r)[tm4c123x documentation] + 中你能找到在这个特别的返回类型 `R` + 上所有可用的函数,其与特定芯片中的特定外设的特定寄存器有关。 ] else { todo } #raw(block: true, lang: "rust", "if pwm.ctl.read().globalsync0().is_set() { - // " + if lang == "en" { + // " + if lang in ("en", "zh") { "Do a thing" } else if lang == "de" { "Tu etwas" @@ -325,7 +381,7 @@ pub fn init() -> (Delay, Leds) { } ") -== #(if lang == "en" [Writing] +== #(if lang in ("en", "zh") [Writing] else if lang == "de" [Schreiben] else { todo }) @@ -352,13 +408,20 @@ pub fn init() -> (Delay, Leds) { Beachten Sie, dass alle Teilfelder, die wir nicht explizit setzen, automatisch auf einen Standardwert gesetzt werden; bereits vorhandene Inhalte des Registers gehen dabei verloren. +] else if lang == "zh" [ + `write()`函数使用一个只有一个参数的闭包。通常我们把这个参数叫做 + `w`。然后这个参数提供对这个寄存器中不同的子域的读写访问,由厂商关于这个芯片的SVD文件提供。再一次,在 + #link(url_struct_w)[tm4c123x documentation] + 中你能找到 `W` + 所有可用的函数,其与特定芯片中的特定外设的特定寄存器有关。注意,所有我们没有设置的子域将会被设置成一个默认值 + - 将会丢失任何在这个寄存器中的现存的内容。 ] else { todo } ```rust pwm.ctl.write(|w| w.globalsync0().clear_bit()); ``` -== #(if lang == "en" [Modifying] +== #(if lang in ("en", "zh") [Modifying] else if lang == "de" [Ändern] else { todo }) @@ -378,6 +441,10 @@ pwm.ctl.write(|w| w.globalsync0().clear_bit()); Argument `r` dient dazu, den aktuellen Inhalt des Registers zu betrachten, während das Argument `w` genutzt werden kann, um den Registerinhalt zu ändern. +] else if lang == "zh" [ + 如果我们希望只改变这个寄存器中某个特定的子域而让其它子域不变,我们能使用`modify`函数。这个函数使用一个具有两个参数的闭包 + - 一个用来读取,一个用来写入。通常我们分别称它们为 `r` 和 `w` 。 `r` + 参数能被用来查看这个寄存器现在的内容,`w` 参数能被用来修改寄存器的内容。 ] else { todo } ```rust @@ -394,19 +461,27 @@ pwm.ctl.modify(|r, w| w.globalsync0().clear_bit()); eine temporäre Variable einlesen, die entsprechenden Bits ändern und den Wert anschließend wieder zurückschreiben. Dies birgt ein erhebliches Fehlerpotenzial: +] else if lang == "zh" [ + `modify` + 函数在这里真正展示了闭包的能量。在C中,我们经常需要读取一些临时值,修改成正确的比特,然后再把值写回。这意味着出现错误的范围非常大。 ] else { todo } -```c -uint32_t temp = pwm0.ctl.read(); +#raw(block: true, lang: "c", +"uint32_t temp = pwm0.ctl.read(); temp |= PWM0_CTL_GLOBALSYNC0; pwm0.ctl.write(temp); uint32_t temp2 = pwm0.enable.read(); temp2 |= PWM0_ENABLE_PWM4EN; -pwm0.enable.write(temp); // Uh oh! Wrong variable! -``` +pwm0.enable.write(temp); // " + if lang in ("en", "de") { + "Uh oh! Wrong variable!" + } else if lang == "zh" { + "哦 不! 错误的变量!" + } else { todos } + " +") = #(if lang == "en" [Using a HAL crate] else if lang == "de" [Verwendung eines HAL-Crates] + else if lang == "zh" [使用一个HAL crate] else { todo }) #if lang == "en" [ @@ -446,12 +521,25 @@ pwm0.enable.write(temp); // Uh oh! Wrong variable! Pin dann in den korrekten Zustand versetzen (etwa durch Auswahl des passenden Modus für alternative Funktionen), bevor er ihn an die Peripherieeinheit übergibt. Und das alles ohne Laufzeitkosten! +] else if lang == "zh" [ + 一个芯片的HAL + crate是通过为PAC暴露的基础结构体们实现一个自定义Trait来发挥作用的。经常这个trait将会为某个外设定义一个被称作 + `constrain()` + 的函数,或者为像是有多个管脚的GPIO端口这类东西定义一个`split()`函数。这个函数将会使用基础的外设结构体,然后返回一个具有更高抽象的API的新对象。这个API还可以做一些事,比如让Serial + port的 `new` + 函数变成需要某个`Clock`结构体的函数,这个结构体只能通过调用配置PLLs并设置所有的时钟频率的函数来生成。在这时,生成一个Serial + port对象而不先配置时钟速率是不可能的,对于Serial + port对象来说错误地将波特率转换为时钟滴答数也是不会发生的。一些crates甚至为每个GPIO管脚的状态定义了特定的 + traits,在把管脚传递进外设前,要求用户去把一个管脚设置成正确的状态(通过选择Alternate + Function模式) 。所有这些都没有运行时开销的! ] else { todo } #if lang == "en" [ Let's see an example: ] else if lang == "de" [ Schauen wir uns ein Beispiel an: +] else if lang == "zh" [ + 让我们看一个例子: ] else { todo } #raw(block: true, lang: "rust", @@ -476,12 +564,16 @@ fn main() -> ! { } else if lang == "de" { "Kapseln Sie die SYSCTL-Struktur in einem Objekt mit einer API auf // hoeherer Ebene." + } else if lang == "zh" { + "将SYSCTL结构体封装成一个有更高抽象API的对象" } else { todos } + " let mut sc = p.SYSCTL.constrain(); // " + if lang == "en" { "Pick our oscillation settings" } else if lang == "de" { "Waehlen Sie unsere Oszillationseinstellungen aus." + } else if lang == "zh" { + "选择我们的晶振配置" } else { todos } + " sc.clock_setup.oscillator = sysctl::Oscillator::Main( sysctl::CrystalFrequency::_16mhz, @@ -491,6 +583,8 @@ fn main() -> ! { "Configure the PLL with those settings" } else if lang == "de" { "Konfigurieren Sie die PLL mit diesen Einstellungen." + } else if lang == "zh" { + "设置PLL" } else { todos } + " let clocks = sc.clock_setup.freeze(); @@ -503,6 +597,9 @@ fn main() -> ! { // hoeheren Ebene. // Beachten Sie, dass es `sc.power_control` nutzen muss, um die // Stromversorgung der GPIO-Peripherie automatisch zu aktivieren." + } else if lang == "zh" { + "把GPIO_PORTA结构体封装成一个有更高抽象API的对象 + // 注意它需要借用 `sc.power_control` 因此它能自动开启GPIO外设。" } else { todos } + " let mut porta = p.GPIO_PORTA.split(&sc.power_control); @@ -510,6 +607,8 @@ fn main() -> ! { "Activate the UART." } else if lang == "de" { "Aktivieren Sie den UART." + } else if lang == "zh" { + "激活UART" } else { todos } + " let uart = Serial::uart0( p.UART0, @@ -517,6 +616,8 @@ fn main() -> ! { "The transmit pin" } else if lang == "de" { "Der Sende-Pin" + } else if lang == "zh" { + "激活UART" } else { todos } + " porta .pa1 @@ -525,6 +626,8 @@ fn main() -> ! { "The receive pin" } else if lang == "de" { "Der Empfangs-Pin" + } else if lang == "zh" { + "接收管脚" } else { todos } + " porta .pa0 @@ -533,6 +636,8 @@ fn main() -> ! { "No RTS or CTS required" } else if lang == "de" { "Kein RTS oder CTS erforderlich" + } else if lang == "zh" { + "不需要RTS或者CTS" } else { todos } + " (), (), @@ -540,12 +645,16 @@ fn main() -> ! { "The baud rate" } else if lang == "de" { "Die Baudrate" + } else if lang == "zh" { + "波特率" } else { todos } + " 115200_u32.bps(), // " + if lang == "en" { "Output handling" } else if lang == "de" { "Ausgabeverarbeitung" + } else if lang == "zh" { + "输出处理" } else { todos } + " NewlineMode::SwapLFtoCRLF, // " + if lang == "en" { @@ -553,12 +662,16 @@ fn main() -> ! { } else if lang == "de" { "Wir benoetigen die Taktfrequenzen, um die Baudraten-Teiler zu // berechnen." + } else if lang == "zh" { + "我们需要时钟频率去计算波特率除法器(divisors)" } else { todos } + " &clocks, // " + if lang == "en" { "We need this to power up the UART peripheral" } else if lang == "de" { "Wir benoetigen dies, um die UART-Peripherie zu aktivieren." + } else if lang == "zh" { + "我们需要这个去启动UART外设" } else { todos } + " &sc.power_control, ); diff --git a/typ/start/semihosting.typ b/typ/start/semihosting.typ index 6b5a7eae..ae493e8f 100644 --- a/typ/start/semihosting.typ +++ b/typ/start/semihosting.typ @@ -3,6 +3,7 @@ #h1(offset: whole, if lang == "en" [Semihosting] else if lang == "de" [Semihosting] + else if lang == "zh" [半主机模式] else { todo }) #if lang == "en" [ @@ -22,6 +23,8 @@ jedoch die sehr geringe Geschwindigkeit: Je nach verwendetem Hardware-Debugger (z. B. ST-Link) kann jeder Schreibvorgang mehrere Millisekunden in Anspruch nehmen. +] else if lang == "zh" [ + 半主机模式是一种可以让嵌入式设备在主机上进行I/O操作的的机制,主要被用来记录信息到主机控制台上。半主机模式需要一个debug会话,除此之外几乎没有其它要求了,因此它非常易于使用。缺点是它非常慢:每个写操作需要几毫秒的时间,其取决于你的硬件调试器(e.g.~ST-LINK)。 ] else { todo } #let ln_sh = link("https://crates.io/crates/cortex-m-semihosting")[`cortex-m-semihosting`] @@ -33,6 +36,9 @@ Das #ln_sh;-Crate stellt eine API für Semihosting-Operationen auf Cortex-M-Geräten bereit. Das folgende Programm ist die Semihosting-Version von „Hello, world!": +] else if lang == "zh" [ + #ln_sh crate 提供了一个API去在Cortex-M设备上执行半主机操作。下面的程序是"Hello, + world!"的半主机版本。 ] else { todo } ```rust @@ -58,6 +64,9 @@ fn main() -> ! { ] else if lang == "de" [ Wenn Sie dieses Programm auf der Hardware ausführen, sehen Sie die Meldung „Hello, world!" in den OpenOCD-Protokollen. +] else if lang == "zh" [ + 如果你在硬件上运行这个程序,你将会在OpenOCD的logs中看到"Hello, + world!"信息。 ] else { todo } ```text @@ -71,6 +80,8 @@ Hello, world! You do need to enable semihosting in OpenOCD from GDB first: ] else if lang == "de" [ Sie müssen zunächst Semihosting in OpenOCD über GDB aktivieren: +] else if lang == "zh" [ + 你首先需要从GDB使能OpenOCD中的半主机模式。 ] else { todo } ```console @@ -91,10 +102,12 @@ semihosting is enabled `-semihosting-config` übergeben müssen, um die Semihosting-Unterstützung zu aktivieren; diese Optionen sind bereits in der Datei `.cargo/config.toml` der Vorlage enthalten. +] else if lang == "zh" [ + QEMU理解半主机操作,因此上面的程序不需要启动一个debug会话,也能在`qemu-system-arm`中工作。注意你需要传递`-semihosting-config`标志给QEMU去使能支持半主机模式;这些标识已经被包括在模板的`.cargo/config.toml`文件中了。 ] else { todo } #raw(block: true, lang: "text", -"$ # " + if lang == "en" { +"$ # " + if lang in ("en", "zh") { "this program will block the terminal" } else if lang == "de" { "Dieses Programm wird das Terminal blockieren." @@ -115,6 +128,8 @@ Hello, world! *nicht* auf echter Hardware; diese Funktion kann Ihre OpenOCD-Sitzung beschädigen, sodass Sie keine weiteren Programme mehr debuggen können, bis Sie die Sitzung neu starten. +] else if lang == "zh" [ + `exit`半主机操作也能被用于终止QEMU进程。重要:*不要*在硬件上使用`debug::exit`;这个函数会关闭你的OpenOCD对话,这样你就不能执行其它的程序调试操作了,除了重启它。 ] else { todo } ```rust @@ -157,6 +172,10 @@ $ echo $? `exit(EXIT_FAILURE)` einstellen. Dadurch lassen sich `no_std`-Tests schreiben, die erfolgreich durchlaufen und unter QEMU ausgeführt werden können. +] else if lang == "zh" [ + 最后一个提示:你可以将运行时恐慌(panicking)的行为设置成 + `exit(EXIT_FAILURE)`。这会允许你编写可以在QEMU上运行通过的 `no_std` + 测试。 ] else { todo } #if lang == "en" [ @@ -167,6 +186,10 @@ $ echo $? Praktischerweise bietet der `panic-semihosting`-Crate ein „exit"-Feature an; ist dieses aktiviert, wird `exit(EXIT_FAILURE)` aufgerufen, nachdem die Panic-Meldung auf dem `stderr` des Hosts ausgegeben wurde. +] else if lang == "zh" [ + 为了方便,`panic-semihosting` crate有一个 "exit" + 特性。当它使能的时候,在主机stderr上打印恐慌(painc)信息后会调用 + `exit(EXIT_FAILURE)` 。 ] else { todo } ```rust @@ -207,20 +230,27 @@ $ echo $? *HINWEIS*: Um diese Funktion für `panic-semihosting` zu aktivieren, bearbeiten Sie den Abschnitt „dependencies" in Ihrer `Cargo.toml`, in dem `panic-semihosting` wie folgt angegeben ist: +] else if lang == "zh" [ + *注意*: + 为了在`panic-semihosting`上使能这个特性,编辑你的`Cargo.toml`依赖,`panic-semihosting`改写成: ] else { todo } ```toml panic-semihosting = { version = "VERSION", features = ["exit"] } ``` +#let ln_spec_defs = link("https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html")[`specifying dependencies`] #if lang == "en" [ where `VERSION` is the version desired. For more information on dependencies features check the - #link("https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html")[`specifying dependencies`] + #ln_spec_defs section of the Cargo book. ] else if lang == "de" [ wobei `VERSION` für die gewünschte Version steht. Weitere Informationen zu Abhängigkeiten und Funktionen finden Sie im Abschnitt - "#link("https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html")[`specifying dependencies`]" + "#ln_spec_defs" des Cargo-Buchs. +] else if lang == "zh" [ + `VERSION`是想要的版本。关于依赖features的更多信息查看Cargo + book的#ln_spec_defs;部分。 ] else { todo } diff --git a/typ/static-guarantees/design-contracts.typ b/typ/static-guarantees/design-contracts.typ index 398edd85..3e4bbc73 100644 --- a/typ/static-guarantees/design-contracts.typ +++ b/typ/static-guarantees/design-contracts.typ @@ -3,6 +3,7 @@ #h1(offset: whole, if lang == "en" [Design Contracts] else if lang == "de" [Design-Verträge] + else if lang == "zh" [设计约定(design contracts)] else { todo }) #if lang == "en" [ @@ -13,6 +14,8 @@ Im letzten Kapitel haben wir eine Schnittstelle geschrieben, die _keine_ Design-Verträge (Design Contracts) durchsetzte. Werfen wir noch einmal einen Blick auf unser fiktives GPIO-Konfigurationsregister: +] else if lang == "zh" [ + 在我们的上个章节中,我们写了一个接口,但没有强制遵守设计约定。让我们再看下我们假想的GPIO配置寄存器: ] else { todo } #include "gpio-table.typ" @@ -26,6 +29,8 @@ zugrundeliegende Hardware nutzen, und dabei unsere Entwurfsverträge zur Laufzeit durchsetzen würden, könnten wir stattdessen Code schreiben, der wie folgt aussieht: +] else if lang == "zh" [ + 如果在使用底层硬件之前检查硬件的状态,在运行时强制用户遵守设计约定,代码可能像这一样: ] else { todo } #raw(block: true, lang: "rust", @@ -33,12 +38,16 @@ "GPIO interface" } else if lang == "de" { "GPIO-Schnittstelle" + } else if lang == "zh" { + "GPIO接口" } else { todos } + " struct GpioConfig { /// " + if lang == "en" { "GPIO Configuration structure generated by svd2rust" } else if lang == "de" { "Von svd2rust generierte GPIO-Konfigurationsstruktur" + } else if lang == "zh" { + "由svd2rust生成的GPIO配制结构体" } else { todos } + " periph: GPIO_CONFIG, } @@ -56,6 +65,8 @@ impl GpioConfig { "Must be enabled to set direction" } else if lang == "de" { "Muss aktiviert sein, um die Richtung festzulegen." + } else if lang == "zh" { + "必须被使能配置方向" } else { todos } + " return Err(()); } @@ -73,6 +84,8 @@ impl GpioConfig { "Must be enabled to set input mode" } else if lang == "de" { "Muss aktiviert sein, um den Eingabemodus festzulegen." + } else if lang == "zh" { + "必须被使能配置输入模式" } else { todos } + " return Err(()); } @@ -82,6 +95,8 @@ impl GpioConfig { "Direction must be input" } else if lang == "de" { "Die Richtung muss input sein." + } else if lang == "zh" { + "方向必须被设置成输入" } else { todos } + " return Err(()); } @@ -99,6 +114,8 @@ impl GpioConfig { "Must be enabled to set output status" } else if lang == "de" { "Muss aktiviert sein, um den Ausgangsstatus festzulegen." + } else if lang == "zh" { + "设置输出状态必须被使能" } else { todos } + " return Err(()); } @@ -108,6 +125,8 @@ impl GpioConfig { "Direction must be output" } else if lang == "de" { "Die Richtung muss output sein." + } else if lang == "zh" { + "方向必须是输出" } else { todos } + " return Err(()); } @@ -125,6 +144,8 @@ impl GpioConfig { "Must be enabled to get status" } else if lang == "de" { "Muss aktiviert sein, um den Status abzurufen." + } else if lang == "zh" { + "获取状态必须被使能" } else { todos } + " return Err(()); } @@ -134,6 +155,8 @@ impl GpioConfig { "Direction must be input" } else if lang == "de" { "Die Richtung muss input sein." + } else if lang == "zh" { + "方向必须是输入" } else { todos } + " return Err(()); } @@ -152,10 +175,13 @@ impl GpioConfig { letztlich zahlreiche Laufzeitüberprüfungen durch -- was Zeit und Ressourcen verschwendet --, und der Code ist für Entwickler deutlich weniger angenehm zu handhaben. +] else if lang == "zh" [ + 因为需要强制遵守硬件上的限制,所以最后做了很多运行时检查,它浪费了我们很多时间和资源,对于开发者来说,这个代码用起来就没那么愉快了。 ] else { todo } = #(if lang == "en" [Type States] else if lang == "de" [Typzustände] + else if lang == "zh" [类型状态(Type states)] else { todo }) #if lang == "en" [ @@ -165,6 +191,8 @@ impl GpioConfig { Was aber, wenn wir stattdessen das Typsystem von Rust nutzen würden, um die Regeln für Zustandsübergänge durchzusetzen? Betrachten wir dieses Beispiel: +] else if lang == "zh" [ + 但是,如果我们让Rust的类型系统去强制遵守状态转换的规则会怎样?看下这个例子: ] else { todo } #raw(block: true, lang: "rust", @@ -172,12 +200,16 @@ impl GpioConfig { "GPIO interface" } else if lang == "de" { "GPIO-Schnittstelle" + } else if lang == "zh" { + "GPIO接口" } else { todos } + " struct GpioConfig { /// " + if lang == "en" { "GPIO Configuration structure generated by svd2rust" } else if lang == "de" { "Von svd2rust generierte GPIO-Konfigurationsstruktur" + } else if lang == "zh" { + "由svd2rust产生的GPIO配置结构体" } else { todos } + " periph: GPIO_CONFIG, enabled: ENABLED, @@ -189,6 +221,8 @@ struct GpioConfig { "Type states for MODE in GpioConfig" } else if lang == "de" { "Typzustaende fuer MODE in GpioConfig" + } else if lang == "zh" { + "GpioConfig中MODE的类型状态" } else { todos } + " struct Disabled; struct Enabled; @@ -203,6 +237,8 @@ struct DontCare; "These functions may be used on any GPIO Pin" } else if lang == "de" { "Diese Funktionen koennen an jedem GPIO-Pin verwendet werden." + } else if lang == "zh" { + "这些函数可能被用于所有的GPIO管脚" } else { todos } + " impl GpioConfig { pub fn into_disabled(self) -> GpioConfig { @@ -248,6 +284,8 @@ impl GpioConfig { "This function may be used on an Output Pin" } else if lang == "de" { "Diese Funktion kann fuer einen Ausgangspin verwendet werden." + } else if lang == "zh" { + "这个函数可能被用于一个输出管脚" } else { todos } + " impl GpioConfig { pub fn set_bit(&mut self, set_high: bool) { @@ -259,6 +297,8 @@ impl GpioConfig { "These methods may be used on any enabled input GPIO" } else if lang == "de" { "Diese Methoden koennen auf jedem aktivierten Eingangs-GPIO verwendet werden." + } else if lang == "zh" { + "这些方法可能被用于任意一个使能的输入GPIO" } else { todos } + " impl GpioConfig { pub fn bit_is_set(&self) -> bool { @@ -301,6 +341,8 @@ impl GpioConfig { Now let's see what the code using this would look like: ] else if lang == "de" [ Schauen wir uns nun an, wie der Code aussehen würde, der dies verwendet: +] else if lang == "zh" [ + 现在让我们看下代码如何用这个API: ] else { todo } #raw(block: true, lang: "rust", @@ -309,6 +351,8 @@ impl GpioConfig { "Example 1: Unconfigured to High-Z input" } else if lang == "de" { "Beispiel 1: Nicht konfiguriert als High-Z-Eingang" + } else if lang == "zh" { + "案例 1: 从未配置到高阻输入" } else { todos } + " */ let pin: GpioConfig = get_gpio(); @@ -317,6 +361,8 @@ let pin: GpioConfig = get_gpio(); "Can't do this, pin isn't enabled!" } else if lang == "de" { "Das ist nicht moeglich, die PIN ist nicht aktiviert!" + } else if lang == "zh" { + "不能这么做,pin没有被使能" } else { todos } + " // pin.into_input_pull_down(); @@ -325,6 +371,8 @@ let pin: GpioConfig = get_gpio(); } else if lang == "de" { "Schalten Sie den Pin nun von „unkonfiguriert“ auf einen hochohmigen Eingang // (High-Z-Eingang) um." + } else if lang == "zh" { + "现在把管脚从未配置变为高阻输入" } else { todos } + " let input_pin = pin.into_enabled_input(); @@ -332,6 +380,8 @@ let input_pin = pin.into_enabled_input(); "Read from the pin" } else if lang == "de" { "Vom Pin lesen" + } else if lang == "zh" { + "从管脚读取" } else { todos } + " let pin_state = input_pin.bit_is_set(); @@ -340,6 +390,8 @@ let pin_state = input_pin.bit_is_set(); } else if lang == "de" { "Das ist nicht moeglich; Eingangs-Pins verfuegen nicht über diese // Schnittstelle!" + } else if lang == "zh" { + "不能这么做,输入管脚没有这个接口" } else { todos } + " // input_pin.set_bit(true); @@ -348,6 +400,8 @@ let pin_state = input_pin.bit_is_set(); "Example 2: High-Z input to Pulled Low input" } else if lang == "de" { "Beispiel 2: Hochohmiger Eingang (High-Z) an auf Low-Pegel gezogenen Eingang" + } else if lang == "zh" { + "案例 2: 高阻输入到下拉输入" } else { todos } + " */ let pulled_low = input_pin.into_input_pull_down(); @@ -358,6 +412,8 @@ let pin_state = pulled_low.bit_is_set(); "Example 3: Pulled Low input to Output, set high" } else if lang == "de" { "Beispiel 3: Eingang auf Low gezogen, Ausgang auf High gesetzt" + } else if lang == "zh" { + "案例 3: 下拉输入到输出, 拉高" } else { todos } + " */ let output_pin = pulled_low.into_enabled_output(); @@ -367,6 +423,8 @@ output_pin.set_bit(true); "Can't do this, output pins don't have this interface!" } else if lang == "de" { "Das geht nicht, Ausgangspins verfuegen nicht über diese Schnittstelle!" + } else if lang == "zh" { + "不能这么做,输出管脚没有这个接口" } else { todos } + " // output_pin.into_input_pull_down(); ") @@ -380,10 +438,13 @@ output_pin.set_bit(true); speichern, aber warum sollte man es auf diese Weise tun? Warum ist das besser, als den Zustand als `enum` innerhalb unserer `GpioConfig`-Struktur zu speichern? +] else if lang == "zh" [ + 这绝对是存储管脚状态的便捷方法,但是为什么这么做?为什么这比把状态当成一个`enum`存在我们的`GpioConfig`结构体中更好? ] else { todo } = #(if lang == "en" [Compile Time Functional Safety] else if lang == "de" [Funktionale Sicherheit zur Kompilierzeit] + else if lang == "zh" [编译时功能安全(Functional Safety)] else { todo }) #if lang == "en" [ @@ -401,6 +462,8 @@ output_pin.set_bit(true); Ausgangspin umwandelt und anschließend den Ausgabemodus festlegt. Dadurch entfällt der Laufzeitaufwand für die Überprüfung des aktuellen Zustands vor der Ausführung einer Funktion. +] else if lang == "zh" [ + 因为我们在编译时完全强制遵守设计约定,这造成了没有运行时开销。当管脚处于输入模式时时,是不可能设置输出模式的。必须先把它设置成一个输出管脚,然后再设置输出模式。因为在执行一个函数前会检查现在的状态,因此没有运行时消耗。 ] else { todo } #if lang == "en" [ @@ -412,4 +475,6 @@ output_pin.set_bit(true); Fehler bei der Verwendung dieser Schnittstelle ausgeschlossen. Versucht der Nutzer einen unzulässigen Zustandsübergang, lässt sich der Code nicht kompilieren! +] else if lang == "zh" [ + 也因为这些状态被类型系统强制遵守,因此没有为这个接口的使用者留太多的犯错余地。如果它们尝试执行一个非法的状态转换,代码将不会编译成功! ] else { todo } diff --git a/typ/static-guarantees/gpio-table.typ b/typ/static-guarantees/gpio-table.typ index 01311d0c..0dd0b313 100644 --- a/typ/static-guarantees/gpio-table.typ +++ b/typ/static-guarantees/gpio-table.typ @@ -13,77 +13,94 @@ }, table.header( if lang in ("en", "de") [Name] + else if lang == "zh" [名字] else { todo }, if lang == "en" [Bit Number(s)] else if lang == "de" [Bit-Nummer] + else if lang == "zh" [位数(s)] else { todo }, if lang == "en" [Value] else if lang == "de" [Wert] + else if lang == "zh" [值] else { todo }, if lang == "en" [Meaning] else if lang == "de" [Bedeutung] + else if lang == "zh" [含义] else { todo }, if lang == "en" [Notes] else if lang == "de" [Hinweise] + else if lang == "zh" [注释] else { todo }, ), table.cell(rowspan: 2)[enable], table.cell(rowspan: 2)[0], [0], [disabled], if lang == "en" [Disables the GPIO] else if lang == "de" [Deaktiviert den GPIO] + else if lang == "zh" [关闭GPIO] else { todo }, [1], [enabled], if lang == "en" [Enables the GPIO] else if lang == "de" [Aktiviert den GPIO] + else if lang == "zh" [使能GPIO] else { todo }, table.cell(rowspan: 2)[direction], table.cell(rowspan: 2)[1], [0], [input], if lang == "en" [Sets the direction to Input] else if lang == "de" [Legt die Richtung auf „Eingang" fest.] + else if lang == "zh" [方向设置成输入] else { todo }, [1], [output], if lang == "en" [Sets the direction to Output] else if lang == "de" [Legt die Richtung auf „Ausgang" fest.] + else if lang == "zh" [方向设置成输出] else { todo }, table.cell(rowspan: 4)[input_mode], table.cell(rowspan: 4)[2..3], [00], [hi-z], if lang == "en" [Sets the input as high resistance] else if lang == "de" [Setzt den Eingang auf hochohmig.] + else if lang == "zh" [输入设置为高阻态] else { todo }, [01], [pull-low], if lang == "en" [Input pin is pulled low] else if lang == "de" [Der Eingangspin wird auf Low-Pegel gezogen.] + else if lang == "zh" [下拉输入管脚] else { todo }, [10], [pull-high], if lang == "en" [Input pin is pulled high] else if lang == "de" [Der Eingangspin wird auf High-Pegel gezogen] + else if lang == "zh" [上拉输入管脚] else { todo }, [11], [n/a], if lang == "en" [Invalid state. Do not set] else if lang == "de" [Ungültiger Zustand. Nicht setzen.] + else if lang == "zh" [无效状态。不要设置] else { todo }, table.cell(rowspan: 2)[output_mode], table.cell(rowspan: 2)[4], [0], [set-low], if lang == "en" [Output pin is driven low] else if lang == "de" [Der Ausgangspin wird auf Low-Pegel gesteuert.] + else if lang == "zh" [把管脚设置成低电平] else { todo }, [1], [set-high], if lang == "en" [Output pin is driven high] else if lang == "de" [Der Ausgangspin wird auf High-Pegel gesteuert.] + else if lang == "zh" [把管脚设置成高电平] else { todo }, [input_status], [5], [x], [in-val], if lang == "en" [0 if input is < 1.5v, 1 if input >= 1.5v] else if lang == "de" [0, wenn der Eingang < 1,5 V ist; 1, wenn der Eingang ≥ 1,5 V ist.] + else if lang == "zh" [如果输入 < 1.5v 为0,如果输入 + >= 1.5v 为1] else { todo }, ) ) \ No newline at end of file diff --git a/typ/static-guarantees/index.typ b/typ/static-guarantees/index.typ index 9e4a76d7..608133c5 100644 --- a/typ/static-guarantees/index.typ +++ b/typ/static-guarantees/index.typ @@ -2,6 +2,7 @@ #h1(if lang == "en" [Static Guarantees] else if lang == "de" [Statische Garantien] + else if lang == "zh" [静态保障] else { todo }) #let ln_send = link("https://doc.rust-lang.org/core/marker/trait.Send.html")[`Send`] @@ -17,6 +18,8 @@ Zudem lässt sich das Typsystem nutzen, um weitere Eigenschaften bereits zur Kompilierzeit zu überprüfen, wodurch in einigen Fällen Laufzeitprüfungen überflüssig werden. +] else if lang == "zh" [ + Rust的类型系统可以在编译时防止数据竞争(看#ln_send;和#ln_sync;特性(traits))。也可以在编译时使用类型系统来完成一些检查工作;减少某些例子中对运行时检查的需要。 ] else { todo } #if lang == "en" [ @@ -32,6 +35,8 @@ entwerfen, bei der die Initialisierung einer seriellen Schnittstelle nur möglich ist, wenn zuvor die für diese Schnittstelle vorgesehenen Pins konfiguriert wurden. +] else if lang == "zh" [ + 当应用到嵌入式程序时,这些_静态检查_能被用来,比如,强制按需配置I/O接口。例如,可以设计一个初始化串行接口的API,这个API只有在配置好接口需要的管脚后才可以被正确地使用。 ] else { todo } #if lang == "en" [ @@ -46,6 +51,8 @@ Ausgangszustand eines Pins zu ändern, der als „Floating Input" (Eingang mit undefiniertem Pegel) konfiguriert ist, würde beispielsweise zu einem Kompilierfehler führen. +] else if lang == "zh" [ + 也可以静态检查,是否是在正确配置了的外设上执行的操作,像是拉低一个管脚这种操作。比如尝试修改一个被配置成浮空输入模式的管脚的输出状态时,会触发一个编译时错误。 ] else { todo } #if lang == "en" [ @@ -61,4 +68,6 @@ können. Diese _Zugriffskontrolle_ erleichtert das Verständnis und die Analyse der Software im Vergleich zu dem alternativen Ansatz, Peripheriekomponenten als globalen, veränderbaren Zustand zu behandeln. +] else if lang == "zh" [ + 并且,像是在前面章节看到的,所有权的概念能被应用到外设上确保一个程序只有某些部分可以修改一个外设。与将这个外设当做全局可变的状态相比,_访问控制_(assess control)使得软件更容易推理。 ] else { todo } diff --git a/typ/static-guarantees/state-machines.typ b/typ/static-guarantees/state-machines.typ index bf098935..008f92b0 100644 --- a/typ/static-guarantees/state-machines.typ +++ b/typ/static-guarantees/state-machines.typ @@ -3,6 +3,7 @@ #h1(offset: whole, if lang == "en" [Peripherals as State Machines] else if lang == "de" [Peripheriegeräte als Zustandsautomaten] + else if lang == "zh" [作为状态机的外设] else { todo }) #let url_pin = "https://en.wikipedia.org/wiki/General-purpose_input/output" @@ -34,6 +35,17 @@ - Eingang: Hoher Widerstand - Eingang: Auf Low-Pegel gezogen - Eingang: Auf High-Pegel gezogen +] else if lang == "zh" [ + 一个微控制器的外设可以被想成是一组状态机。比如,一个简化的#link(url_pin)[GPIO管脚]的配置可以被表达成下列的状态树: + - 关闭 + - 使能 + - 配置成输出 + - 输出: 高 + - 输出: 低 + - 配置成输入 + - 输入: 高阻态 + - 输入: 下拉 + - 输入: 上拉 ] else { todo } #if lang == "en" [ @@ -51,6 +63,12 @@ + Aktiviert + Als Eingang konfiguriert + Eingang: Hoher Widerstand +] else if lang == "zh" [ + 如果外设开始于`关闭`模式,切换到`输入: 高阻态`模式,我们必须执行下面的步骤: + + 关闭 + + 使能 + + 配置成输入 + + 输入: 高阻态 ] else { todo } #if lang == "en" [ @@ -62,6 +80,10 @@ Wenn wir von „Eingang: Hoher Widerstand" zu „Eingang: Auf Low-Pegel gezogen" wechseln möchten, müssen wir die folgenden Schritte ausführen: + Eingang: Hoher Widerstand 2. Eingang: Auf Low-Pegel gezogen +] else if lang == "zh" [ + 如果我们想要从`输入: 高阻态`切换到`输入: 下拉`,我们必须执行下列的步骤: + + 输入: 高阻抗 + + 输入: 下拉 ] else { todo } #if lang == "en" [ @@ -80,10 +102,17 @@ + Als Eingang konfiguriert + Als Ausgang konfiguriert + Ausgang: High +] else if lang == "zh" [ + 同样地,如果我们想要把一个GPIO管脚从`输入: 下拉`切换到`输出: 高`,我们必须执行下列的步骤: + + 输入: 下拉 + + 配置成输入 + + 配置成输出 + + 输出: 高 ] else { todo } = #(if lang == "en" [Hardware Representation] else if lang == "de" [Hardware-Darstellung] + else if lang == "zh" [硬件表征(Hardware Representation)] else { todo }) #if lang == "en" [ @@ -95,6 +124,8 @@ Werte in bestimmte Register geschrieben werden, die einer GPIO-Peripherieeinheit zugeordnet sind. Definieren wir zur Veranschaulichung ein fiktives GPIO-Konfigurationsregister: +] else if lang == "zh" [ + 通常,通过向映射到GPIO外设上的指定的寄存器中写入值可以配置上面列出的状态。让我们定义一个假想的GPIO配置寄存器来解释下它: ] else { todo } #include "gpio-table.typ" @@ -103,17 +134,23 @@ We _could_ expose the following structure in Rust to control this GPIO: ] else if lang == "de" [ Wir _könnten_ die folgende Struktur in Rust bereitstellen, um diesen GPIO zu steuern: +] else if lang == "zh" [ + _可以_ 在Rust中暴露下列的结构体来控制这个GPIO: ] else { todo } #raw(block: true, lang: "rust", "/// " + if lang in ("en", "de") { "GPIO interface" + } else if lang == "zh" { + "GPIO接口" } else { todos } + " struct GpioConfig { /// " + if lang == "en" { "GPIO Configuration structure generated by svd2rust" } else if lang == "de" { "Von svd2rust generierte GPIO-Konfigurationsstruktur" + } else if lang == "zh" { + "由svd2rust生成的GPIO配置结构体" } else { todos } + " periph: GPIO_CONFIG, } @@ -157,6 +194,8 @@ impl GpioConfig { Dies würde uns jedoch erlauben, bestimmte Register zu verändern, was keinen Sinn ergibt. Was passiert beispielsweise, wenn wir das Feld `output_mode` setzen, während unser GPIO als Eingang konfiguriert ist? +] else if lang == "zh" [ + 然而,这会允许我们修改某些没有意义的寄存器。比如,如果当我们的GPIO被配置为输入时我们设置`output_mode`字段,将会发生什么? ] else { todo } #if lang == "en" [ @@ -171,6 +210,8 @@ impl GpioConfig { einen Eingang, der auf HIGH gesetzt wird. Bei mancher Hardware mag dies keine Rolle spielen. Bei anderer Hardware könnte es jedoch zu unerwartetem oder undefiniertem Verhalten führen! +] else if lang == "zh" [ + 通常使用这个结构体会允许我们访问到上面的状态机没有定义的状态:比如,一个被上拉的输出,或者一个被拉高的输入。对于一些硬件,这并没有关系。对另外一些硬件来说,这将会导致不可预期或者没有定义的行为! ] else { todo } #if lang == "en" [ @@ -180,4 +221,6 @@ impl GpioConfig { Obwohl diese Schnittstelle bequem zu implementieren ist, erzwingt sie nicht die in unserer Hardware-Implementierung festgelegten Designvorgaben. +] else if lang == "zh" [ + 虽然这个接口很方便写入,但是它没有强制我们遵守硬件实现所设的设计约定。 ] else { todo } diff --git a/typ/static-guarantees/typestate-programming.typ b/typ/static-guarantees/typestate-programming.typ index 9351f480..7ad09fbb 100644 --- a/typ/static-guarantees/typestate-programming.typ +++ b/typ/static-guarantees/typestate-programming.typ @@ -3,6 +3,7 @@ #h1(offset: whole, if lang == "en" [Typestate Programming] else if lang == "de" [Typgestützte Programmierung] + else if lang == "zh" [类型状态编程(Typestate Programming)] else { todo }) #let url_ts = "https://en.wikipedia.org/wiki/Typestate_analysis" @@ -22,6 +23,8 @@ #link(url_builders)[Builder-Muster] verwendet haben, sind Sie schon mit der Typestate-Programmierung in Berührung gekommen! +] else if lang == "zh" [ + #link(url_ts)[typestates]的概念是指将有关对象当前状态的信息编码进该对象的类型中。虽然这听起来有点神秘,如果你在Rust中用过#link(url_builders))[建造者模式],你就已经开始使用类型状态编程了! ] else { todo } @@ -77,6 +80,8 @@ fn main() { In diesem Beispiel gibt es keine direkte Möglichkeit, ein `Foo`-Objekt zu erstellen. Wir müssen einen `FooBuilder` erstellen und ihn korrekt initialisieren, bevor wir das gewünschte `Foo`-Objekt erhalten können. +] else if lang == "zh" [ + 在这个例子里,不能直接生成一个`Foo`对象。必须先生成一个`FooBuilder`,并且恰当地初始化`FooBuilder`后,才能获取到需要的`Foo`对象。 ] else { todo } #if lang == "en" [ @@ -88,12 +93,16 @@ fn main() { Dieses Minimalbeispiel kodiert zwei Zustände: - `FooBuilder`, das einen „unkonfigurierten" Zustand oder einen Zustand der „laufenden Konfiguration" repräsentiert - - `Foo`, das einen „konfigurierten" oder „einsatzbereiten" Zustand - repräsentiert. + - `Foo`, das einen „konfigurierten" oder „einsatzbereiten" Zustand repräsentiert. +] else if lang == "zh" [ + 这个最小的例子编码了两个状态: + - `FooBuilder`,其表示一个"没有被配置",或者"正在配置"状态 + - `Foo`,其表示了一个"被配置",或者"可以使用"状态。 ] else { todo } = #(if lang == "en" [Strong Types] else if lang == "de" [Starke Typen] + else if lang == "zh" [强类型] else { todo }) #let url_strong = "https://en.wikipedia.org/wiki/Strong_and_weak_typing" @@ -113,6 +122,8 @@ fn main() { der Aufruf der Methode `into_foo()` die ursprüngliche `FooBuilder`-Struktur; das bedeutet, dass sie nicht ohne die Erstellung einer neuen Instanz wiederverwendet werden kann. +] else if lang == "zh" [ + 因为Rust有一个#link(url_strong)[强类型系统],没有什么简单的方法可以奇迹般地生成一个`Foo`实例,也没有简单的方法可以不用调用`into_foo()`方法而把一个`FooBuilder`变成一个`Foo`。另外,调用`into_foo()`方法消费了最初的`FooBuilder`结构体,意味着不生成一个新的实例就不能被再次使用它。 ] else { todo } #if lang == "en" [ @@ -128,4 +139,7 @@ fn main() { austauschen. Indem wir einen `FooBuilder` erstellen und diesen gegen ein `Foo`-Objekt austauschen, haben wir die Schritte einer einfachen Zustandsmaschine durchlaufen. +] else if lang == "zh" [ + 这允许我们可以将系统的状态表示成类型,把状态转换必须的动作包括进转换两个类型的方法中。通过生成一个 + `FooBuilder`,转换成一个 `Foo` 对象,我们已经使用了一个基本的状态机。 ] else { todo } diff --git a/typ/static-guarantees/zero-cost-abstractions.typ b/typ/static-guarantees/zero-cost-abstractions.typ index ae7218a0..1271f7ae 100644 --- a/typ/static-guarantees/zero-cost-abstractions.typ +++ b/typ/static-guarantees/zero-cost-abstractions.typ @@ -3,6 +3,7 @@ #h1(offset: whole, if lang == "en" [Zero Cost Abstractions] else if lang == "de" [Abstraktionen ohne Kosten] + else if lang == "zh" [零成本抽象] else { todo }) #if lang == "en" [ @@ -19,6 +20,9 @@ eigentlichen Daten, sondern dienen als Markierungen. Da sie keine Daten enthalten, besitzen sie zur Laufzeit keine tatsächliche Repräsentation im Speicher: +] else if lang == "zh" [ + 类型状态是一个零成本抽象的杰出案例 - + 把某些行为移到编译时执行或者分析的能力。这些类型状态不包含真实的数据,只用来作为标记。因为它们不包含数据,在运行时它们在内存中不存在实际的表示。 ] else { todo } ```rust @@ -32,6 +36,7 @@ let _ = size_of::>(); // == 0 = #(if lang == "en" [Zero Sized Types] else if lang == "de" [Typen der Größe Null] + else if lang == "zh" [零大小的类型(Zero Sized Types)] else { todo }) ```rust @@ -50,12 +55,16 @@ struct Enabled; „real" verhalten -- man kann sie kopieren, verschieben, Referenzen darauf erstellen usw. --, werden sie vom Optimierer vollständig entfernt. +] else if lang == "zh" [ + 像这样定义的结构体被称为零大小的类型,因为它们不包含实际数据。虽然这些类型在编译时像是"真实的"(real) - 你可以拷贝它们,移动它们,引用它们,等等,然而优化器将会完全跳过它们。 ] else { todo } #if lang == "en" [ In this snippet of code: ] else if lang == "de" [ In diesem Code-Ausschnitt: +] else if lang == "zh" [ + 在这个代码片段里: ] else { todo } ```rust @@ -87,10 +96,15 @@ pub fn into_input_high_z(self) -> GpioConfig { zusätzliche CPU- oder RAM-Ressourcen noch zusätzlichen Programmspeicher für die Zustandsverwaltung von `GpioConfig` und führt zu demselben Maschinencode wie ein direkter Registerzugriff. +] else if lang == "zh" [ + 我们返回的GpioConfig在运行时并不存在。对这个函数的调用通常会被归纳为一条汇编指令 + - 把一个常量寄存器值存进一个寄存器里。这意味着我们开发的类型状态接口是一个零成本抽象 + - 它不会用更多的CPU,RAM,或者代码空间去跟踪`GpioConfig`的状态,会被渲染成和直接访问寄存器一样的机器码。 ] else { todo } = #(if lang == "en" [Nesting] else if lang == "de" [Verschachtelung] + else if lang == "zh" [嵌套] else { todo }) #if lang == "en" [ @@ -102,6 +116,8 @@ pub fn into_input_high_z(self) -> GpioConfig { verschachteln. Solange es sich bei allen verwendeten Komponenten um Typen ohne Größe (Zero-Sized Types) handelt, existiert die gesamte Struktur zur Laufzeit nicht. +] else if lang == "zh" [ + 通常,这些抽象可能会被深深地嵌套起来。一旦结构体使用的所有的组件是零大小类型的,整个结构体将不会在运行时存在。 ] else { todo } #if lang == "en" [ @@ -113,4 +129,6 @@ pub fn into_input_high_z(self) -> GpioConfig { alle möglichen Zustandskombinationen zu definieren. In solchen Fällen lassen sich Makros verwenden, um sämtliche Implementierungen zu generieren. +] else if lang == "zh" [ + 对于复杂或者深度嵌套的结构体,定义所有可能的状态组合可能很乏味。在这些例子中,宏可能可以被用来生成所有的实现。 ] else { todo } diff --git a/typ/unsorted/index.typ b/typ/unsorted/index.typ index 3cd6e935..95ae4bbe 100644 --- a/typ/unsorted/index.typ +++ b/typ/unsorted/index.typ @@ -2,5 +2,6 @@ #h1(if lang == "en" [Unsorted topics] else if lang == "de" [Unsortierte Themen] + else if lang == "zh" [没有排序的主题] else { todo }) diff --git a/typ/unsorted/math.typ b/typ/unsorted/math.typ index c585f680..97ef118e 100644 --- a/typ/unsorted/math.typ +++ b/typ/unsorted/math.typ @@ -3,6 +3,7 @@ #h1(offset: whole, if lang == "en" [Performing math functionality with `#[no_std]`] else if lang == "de" [Mathematische Funktionen mit `#[no_std]` nutzen] + else if lang == "zh" [在`#[no_std]`下执行数学运算] else { todo }) #if lang == "en" [ @@ -14,6 +15,8 @@ Berechnung der Quadratwurzel aus der Exponentialfunktion einer Zahl -- und Ihnen die vollständige Standardbibliothek zur Verfügung steht, könnte Ihr Code folgendermaßen aussehen: +] else if lang == "zh" [ + 如果你想要执行数学相关的函数,像是计算平方根或者一个数的指数并有完整的标准库支持,代码可能看起来像这样: ] else { todo } #raw(block: true, lang: "rust", @@ -21,6 +24,8 @@ "Some mathematical functions with standard support available" } else if lang == "de" { "Einige mathematische Funktionen mit Standardunterstützung verfügbar" + } else if lang == "zh" { + "可用一些标准支持的数学函数" } else { todos } + " fn main() { @@ -50,8 +55,10 @@ fn main() { ] else if lang == "de" [ Ohne Unterstützung durch die Standardbibliothek stehen diese Funktionen nicht zur Verfügung. Stattdessen kann ein externes Crate wie - #link("https://crates.io/crates/libm")[`libm`] verwendet werden. Der + #ln_libm verwendet werden. Der Beispielcode sähe dann folgendermaßen aus: +] else if lang == "zh" [ + 没有标准库支持的时候,这些函数不可用。反而可以使用像是#ln_libm;这样一个外部库。示例的代码将会看起来像这样: ] else { todo } #raw(block: true, lang: "rust", @@ -89,6 +96,9 @@ fn main() -> ! { "exit QEMU // HINWEIS: Fuehren Sie dies nicht auf der Hardware aus; es kann den // OpenOCD-Zustand beschaedigen." + } else if lang == "zh" { + "退出QEMU + // 注意不要在硬件上使用这个; 它能破坏OpenOCD的状态" } else { todos } + " // debug::exit(debug::EXIT_SUCCESS); @@ -104,6 +114,8 @@ fn main() -> ! { Wenn Sie komplexere Operationen wie DSP-Signalverarbeitung oder fortgeschrittene lineare Algebra auf Ihrem Mikrocontroller durchführen müssen, könnten Ihnen die folgenden Crates weiterhelfen. +] else if lang == "zh" [ + 如果需要在MCU上执行更复杂的操作,像是DSP信号处理或者更高级的线性代数,下列的crates可能可以帮到你 ] else { todo } - #link("https://github.com/jacobrosenthal/cmsis-dsp-sys")[CMSIS DSP library binding] - #link("https://crates.io/crates/constgebra")[`constgebra`] diff --git a/typ/unsorted/speed-vs-size.typ b/typ/unsorted/speed-vs-size.typ index f02dc0f4..b16e37ff 100644 --- a/typ/unsorted/speed-vs-size.typ +++ b/typ/unsorted/speed-vs-size.typ @@ -3,6 +3,7 @@ #h1(offset: whole, if lang == "en" [Optimizations: the speed size tradeoff] else if lang == "de" [Optimierungen: Der Kompromiss zwischen Geschwindigkeit und Größe] + else if lang == "zh" [优化: 速度与大小之间的博弈] else { todo }) #if lang == "en" [ @@ -16,10 +17,13 @@ Dieser Abschnitt behandelt die verschiedenen Optimierungsstufen von `rustc` und deren Auswirkungen auf die Ausführungszeit sowie die Größe der Binärdatei eines Programms. +] else if lang == "zh" [ + 每个人都想要程序变得即快又小,但是同时满足这两个条件是不可能的。这部分讨论`rustc`提供的不同的优化等级,和它们是如何影响执行时间和一个程序的二进制项的大小。 ] else { todo } = #(if lang == "en" [No optimizations] else if lang == "de" [Keine Optimierungen] + else if lang == "zh" [无优化] else { todo }) #if lang == "en" [ @@ -33,6 +37,9 @@ dieses Profil für das Debugging optimiert ist, werden Debug-Informationen aktiviert, jedoch _keinerlei_ Optimierungen vorgenommen; es wird also `-C opt-level=0` verwendet. +] else if lang == "zh" [ + 这是默认的。当你调用`cargo build`时,你使用的是development(又叫`dev`)配置。这个配置优化的目的是为了调试,因此它使能了调试信息且_关闭_了所有优化,i.e.~它使用 + `-C opt-level = 0` 。 ] else { todo } #if lang == "en" [ @@ -47,6 +54,9 @@ Release-Profil zu aktivieren -- standardmäßig sind sie dort deaktiviert. Dies ermöglicht Ihnen die Verwendung von Breakpoints beim Debuggen von Release-Builds. +] else if lang == "zh" [ + 至少对于裸机开发来说,调试信息不会占用Flash/ROM中的空间,意味着在这种情况下,调试信息是零开销的,因此实际上我们推荐你在release配置中使能调试信息 + -- 默认它被关闭了。那会让你调试release版本的固件时可以使用断点。 ] else { todo } #raw(block: true, lang: "toml", @@ -55,6 +65,8 @@ "symbols are nice and they don't increase the size on Flash" } else if lang == "de" { "Symbole sind praktisch und vergroessern die Dateigroesse im Flash nicht" + } else if lang == "zh" { + "调试符号很好且它们不会增加Flash上的大小" } else { todos } + " debug = true ") @@ -72,6 +84,8 @@ debug = true Funktionsargumente in GDB per `print` ausgeben. Bei optimiertem Code führt der Versuch, Variablen auszugeben, hingegen zur Meldung `$0 = `. +] else if lang == "zh" [ + 无优化对于调试来说是最好的选择,因为单步调试代码感觉像是你正在逐条语句地执行程序,且你能在GDB中`print`栈变量和函数参数。当代码被优化了,尝试打印变量会导致`$0 = `被打印出来。 ] else { todo } #if lang == "en" [ @@ -87,6 +101,9 @@ debug = true Flash-Speicher belegen können -- Speicherplatz, über den das Zielgerät womöglich gar nicht verfügt. Die Folge: Die nicht optimierte Binärdatei passt nicht auf das Gerät! +] else if lang == "zh" [ + `dev`配置最大的缺点就是最终的二进制项将会变得巨大且缓慢。大小通常是一个更大的问题,因为未优化的二进制项会占据大量KiB的Flash,你的目标设备可能没这么多Flash + -- 结果: 你未优化的二进制项无法烧录进你的设备中! ] else { todo } #if lang == "en" [ @@ -94,10 +111,13 @@ debug = true ] else if lang == "de" [ Gibt es eine Möglichkeit, kleinere und dennoch für das Debugging geeignete Binärdateien zu erhalten? Ja, es gibt einen Trick. +] else if lang == "zh" [ + 我们可以有更小的,调试友好的二进制项吗?是的,这里有一个技巧。 ] else { todo } == #(if lang == "en" [Optimizing dependencies] else if lang == "de" [Optimierung der Abhängigkeiten] + else if lang == "zh" [优化依赖] else { todo }) #let url_overrides = "https://doc.rust-lang.org/cargo/reference/profiles.html#overrides" @@ -112,6 +132,9 @@ debug = true überschreiben können. So lassen sich alle Abhängigkeiten hinsichtlich ihrer Größe optimieren, während die oberste Crate unoptimiert und debuggfreundlich bleibt. +] else if lang == "zh" [ + 这里有个名为#link(url_overrides)[`profile-overrides`]的Cargo + feature,其可以让你覆盖依赖项的优化等级。你能使用这个feature去优化所有依赖的大小,而保持顶层的crate没有被优化以致调试起来友好。 ] else { todo } #if lang == "en" [ @@ -127,12 +150,17 @@ debug = true einer generischen Struktur erstellen und feststellen, dass diese Code mit großem Speicherbedarf einbindet, kann es sein, dass eine Erhöhung des Optimierungsgrads der relevanten Abhängigkeiten keine Wirkung zeigt. +] else if lang == "zh" [ + 需要知道,泛型代码有时是在它被实例化的库中被优化的,而不是它被定义的地方.如果你在你的应用中生成了一个泛型结构体的实例, + 并且发现它让代码体积变得更大,那可能是因为相关的依赖的优化等级的增加没有造成影响. ] else { todo } #if lang == "en" [ Here's an example: ] else if lang == "de" [ Hier ist ein Beispiel: +] else if lang == "zh" [ + 这是一个示例: ] else { todo } ```toml @@ -149,6 +177,8 @@ opt-level = "z" # + Without the override: ] else if lang == "de" [ Ohne die Überschreibung: +] else if lang == "zh" [ + 没有覆盖: ] else { todo } ```text @@ -166,6 +196,8 @@ section size addr With the override: ] else if lang == "de" [ Mit der Überschreibung +] else if lang == "zh" [ + 有覆盖: ] else { todo } ```text @@ -196,6 +228,10 @@ section size addr sein, eine Abhängigkeit zu debuggen, lässt sich die Funktion `profile-overrides` nutzen, um diese spezifische Abhängigkeit von der Optimierung auszunehmen. Siehe dazu das folgende Beispiel: +] else if lang == "zh" [ + 在Flash的使用上减少了6KiB,而不会损害顶层crate的可调试性。如果你步进一个依赖项,然后你将开始再次看到那些``信息,但是通常的情况下你只想调试顶层的crate而不是依赖项。如果你 + _需要_ 调试一个依赖项,那么你可以使用`profile-overrides` + feature去防止一个特定的依赖项被优化。看下面的例子: ] else { todo } #raw(block: true, lang: "toml", @@ -205,6 +241,8 @@ section size addr "don't optimize the `cortex-m-rt` crate" } else if lang == "de" { "Optimiere das `cortex-m-rt`-Crate nicht" + } else if lang == "zh" { + "不要优化`cortex-m-rt` crate" } else { todos } + " [profile.dev.package.cortex-m-rt] # + opt-level = 0 # + @@ -213,9 +251,11 @@ opt-level = 0 # + "but do optimize all the other dependencies" } else if lang == "de" { "optimieren Sie jedoch alle anderen Abhaengigkeiten" + } else if lang == "zh" { + "但是优化所有其它依赖项" } else { todos } + " [profile.dev.package.\"*\"] -codegen-units = 1 # " + if lang in ("en", "de") { +codegen-units = 1 # " + if lang in ("en", "de", "zh") { "better optimizations" } else { todos } + " opt-level = \"z\" @@ -225,10 +265,13 @@ opt-level = \"z\" Now the top crate and `cortex-m-rt` are debugger friendly! ] else if lang == "de" [ Jetzt sind das Top-Level-Crate und `cortex-m-rt` debuggerfreundlich! +] else if lang == "zh" [ + 现在顶层的crate和`cortex-m-rt`对调试器很友好! ] else { todo } = #(if lang == "en" [Optimize for speed] else if lang == "de" [Auf Geschwindigkeit optimieren] + else if lang == "zh" [优化速度] else { todo }) #if lang == "en" [ @@ -240,6 +283,10 @@ opt-level = \"z\" Geschwindigkeitsoptimierung: `opt-level = 1`, `2` und `3`. Beim Ausführen von `cargo build --release` wird das Release-Profil verwendet, das standardmäßig auf `opt-level = 3` eingestellt ist. +] else if lang == "zh" [ + 自2018-09-18开始 `rustc` 支持三个 "优化速度" 的等级: `opt-level = 1`, + `2` 和 `3` 。当你运行 `cargo build --release` + 时,你正在使用的是release配置,其默认是 `opt-level = 3` 。 ] else { todo } #if lang == "en" [ @@ -260,6 +307,11 @@ opt-level = \"z\" Bytes für eine Schleife zum Nullsetzen eines Arrays), kann aber unter geeigneten Bedingungen (etwa bei einer ausreichend hohen Anzahl von Iterationen) die Ausführungszeit halbieren. +] else if lang == "zh" [ + `opt-level = 2` 和 `3` + 都以二进制项大小为代价优化速度,但是等级`3`比等级`2`做了更多的向量化和内联。特别是,你将看到在`opt-level`等于或者大于`2`时LLVM将展开循环。循环展开在 + Flash / ROM 方面的成本相当高(e.g.~from 26 bytes to 194 for a zero this + array loop)但是如果条件合适(迭代次数足够大),也可以将执行时间减半。 ] else { todo } #if lang == "en" [ @@ -271,10 +323,13 @@ opt-level = \"z\" `opt-level = 2` und `3` zu deaktivieren; wenn Sie sich diesen Speicheraufwand also nicht leisten können, sollten Sie Ihr Programm stattdessen auf eine geringe Größe hin optimieren. +] else if lang == "zh" [ + 现在还没有办法在`opt-level = 2`和`3`的情况下关闭循环展开,因此如果你不能接受它的开销,你应该选择优化你的程序的大小。 ] else { todo } = #(if lang == "en" [Optimize for size] else if lang == "de" [Nach Größe optimieren] + else if lang == "zh" [优化大小] else { todo }) #if lang == "en" [ @@ -288,6 +343,10 @@ opt-level = \"z\" wurden von Clang/LLVM übernommen und sind nicht besonders aussagekräftig; allerdings soll das `"z"` signalisieren, dass damit kleinere Binärdateien erzeugt werden als mit `"s"`. +] else if lang == "zh" [ + 自2018-09-18开始`rustc`支持两个"优化大小"的等级: `opt-level = "s"` 和 + `"z"` 。这些名字传承自 clang / LLVM + 且不具有描述性,但是`"z"`意味着它产生的二进制文件比`"s"`更小。 ] else { todo } #if lang == "en" [ @@ -297,11 +356,13 @@ opt-level = \"z\" Wenn Ihre Release-Binärdateien hinsichtlich der Größe optimiert werden sollen, ändern Sie die Einstellung `profile.release.opt-level` in der Datei `Cargo.toml` wie unten dargestellt. +] else if lang == "zh" [ + 如果你想要发布一个优化了大小的二进制项,那么改变下面展示的`Cargo.toml`中的`profile.release.opt-level`配置。 ] else { todo } #raw(block: true, lang: "toml", "[profile.release] -# " + if lang == "en" { "or \"z\"" } +# " + if lang in ("en", "zh") { "or \"z\"" } else if lang == "de" { "oder \"z\"" } else { todos } + " opt-level = \"s\" @@ -326,6 +387,9 @@ opt-level = \"s\" Ein niedriger Inline-Schwellenwert kann daher dazu führen, dass LLVM Optimierungsmöglichkeiten verpasst (etwa das Entfernen von nicht erreichbaren Programmzweigen oder das Inlinen von Closure-Aufrufen). +] else if lang == "zh" [ + 这两个优化等级极大地减小了LLVM的内联阈值,一个用来决定是否内联或者不内联一个函数的度量。Rust其中一个概念是零成本抽象;这些抽象趋向于去使用许多新类型和小函数去保持不变量(e.g.~像是`deref`,`as_ref`这样借用内部值的函数)因此一个低内联阈值会使LLVM失去优化的机会(e.g.~去掉死分支(dead + branches),内联对闭包的调用)。 ] else { todo } #if lang == "en" [ @@ -340,6 +404,8 @@ recommended way to change the inline threshold is to append the Größe der Binärdatei auswirkt. Die empfohlene Methode zur Änderung dieses Schwellenwerts besteht darin, das Flag `-C inline-threshold` zu den übrigen `rustflags` in der Datei `.cargo/config.toml` hinzuzufügen. +] else if lang == "zh" [ + 当优化大小时,你可能想要尝试增加内联阈值去观察是否会对你的二进制项的大小有影响。推荐的改变内联阈值的方法是在`.cargo/config.toml`中往其它rustflags后插入`-C inline-threshold` ] else { todo } #raw(block: true, lang: "toml", @@ -348,6 +414,8 @@ recommended way to change the inline threshold is to append the "this assumes that you are using the cortex-m-quickstart template" } else if lang == "de" { "Dies setzt voraus, dass Sie die cortex-m-quickstart-Vorlage verwenden" + } else if lang == "zh" { + "这里假设你正在使用cortex-m-quickstart模板" } else { todos } + " [target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))'] rustflags = [ @@ -371,6 +439,12 @@ rustflags = [ - `opt-level = 2` verwendet 225 - `opt-level = "s"` verwendet 75 - `opt-level = "z"` verwendet 25 +] else if lang == "zh" [ + 用什么值?#link(url_opt_lvls)[从1.29.0开始,这些是不同优化级别使用的内联阈值]: + - `opt-level = 3` 使用 275 + - `opt-level = 2` 使用 225 + - `opt-level = "s"` 使用 75 + - `opt-level = "z"` 使用 25 ] else { todo } #if lang == "en" [ @@ -378,4 +452,6 @@ rustflags = [ ] else if lang == "de" [ Du solltest `225` und `275` ausprobieren, wenn du auf die Größe optimierst. +] else if lang == "zh" [ + 当优化大小时,你应该尝试`225`和`275` 。 ] else { todo } From 9317f9edebc0dfb63a6f48302e9a4c3eeff5f6ab Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Sat, 8 Aug 2026 20:48:53 +0300 Subject: [PATCH 07/11] japanese start & fix CI cache --- .github/workflows/ci-typ.yml | 15 ++- .github/workflows/ci.yml | 14 +-- typ/book.typ | 30 ++++++ typ/config.typ | 27 ++++-- typ/intro/hardware.typ | 26 +++++ typ/intro/index.typ | 54 +++++++++++ typ/intro/install.typ | 22 ++++- typ/intro/install/linux.typ | 49 ++++++++-- typ/intro/install/macos.typ | 6 ++ typ/intro/install/verify.typ | 28 ++++++ typ/intro/install/windows.typ | 16 +++- typ/intro/no-std.typ | 54 ++++++++++- typ/intro/tooling.typ | 46 +++++++-- typ/start/exceptions.typ | 90 +++++++++++++++++ typ/start/hardware.typ | 175 +++++++++++++++++++++++++++------- typ/start/index.typ | 5 + typ/start/interrupts.typ | 32 +++++++ typ/start/panicking.typ | 45 ++++++++- 18 files changed, 656 insertions(+), 78 deletions(-) diff --git a/.github/workflows/ci-typ.yml b/.github/workflows/ci-typ.yml index aa73791a..57142eb5 100644 --- a/.github/workflows/ci-typ.yml +++ b/.github/workflows/ci-typ.yml @@ -14,6 +14,9 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable + - name: Install PDF fonts + run: sudo apt install -y fonts-noto-cjk + - name: Install Python dependencies run: | pip3 install --user python-dateutil linkchecker @@ -25,15 +28,15 @@ jobs: id: cache-cargo-typst with: path: ~/cargo-bin - key: cache-cargo + key: cache-cargo-typst - name: Install Typst - if: steps.cache-cargo.outputs.cache-hit != 'true' + if: steps.cache-cargo-typst.outputs.cache-hit != 'true' uses: actions-rs/install@v0.1 with: crate: typst-cli version: latest - name: Copy typst to cache directory - if: steps.cache-cargo.outputs.cache-hit != 'true' + if: steps.cache-cargo-typst.outputs.cache-hit != 'true' run: | mkdir -p ~/cargo-bin cp ~/.cargo/bin/typst ~/cargo-bin @@ -47,12 +50,14 @@ jobs: - name: Build translated book run: | - typst compile typ/book.typ --input lang=zh --input target=html book --features bundle,html -f bundle - typst compile typ/book.typ --input lang=zh --input target=pdf book/book_zh.pdf typst compile typ/book.typ --input lang=de --input target=html book --features bundle,html -f bundle typst compile typ/book.typ --input lang=de --input target=pdf book/book_de.pdf + typst compile typ/book.typ --input lang=ja --input target=html book --features bundle,html -f bundle + typst compile typ/book.typ --input lang=ja --input target=pdf book/book_ja.pdf typst compile typ/book.typ --input lang=uk --input target=html book --features bundle,html -f bundle typst compile typ/book.typ --input lang=uk --input target=pdf book/book_uk.pdf + typst compile typ/book.typ --input lang=zh --input target=html book --features bundle,html -f bundle + typst compile typ/book.typ --input lang=zh --input target=pdf book/book_zh.pdf - name: Check links run: linkchecker book diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d3b98047..e8e929a9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,10 +47,10 @@ jobs: - name: Check links run: linkchecker book - - name: Deploy book - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: book - force_orphan: true + #- name: Deploy book + # if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + # uses: peaceiris/actions-gh-pages@v3 + # with: + # github_token: ${{ secrets.GITHUB_TOKEN }} + # publish_dir: book + # force_orphan: true diff --git a/typ/book.typ b/typ/book.typ index e58ba0b4..2551f2b6 100644 --- a/typ/book.typ +++ b/typ/book.typ @@ -6,6 +6,7 @@ content: include "intro/index.typ", title: if lang == "en" [Introduction] else if lang == "de" [Einleitung] + else if lang == "ja" [導入] else if lang == "uk" [Вступ] else if lang == "zh" [引言] else { todo }, @@ -13,6 +14,7 @@ "intro/hardware": ( content: include "intro/hardware.typ", title: if lang in ("en", "de") [Hardware] + else if lang == "ja" [ハードウェア] else if lang == "uk" [Залізо] else if lang == "zh" [硬件] else { todo } @@ -25,6 +27,7 @@ content: include "intro/tooling.typ", title: if lang == "en" [Tooling] else if lang == "de" [Werkzeuge] + else if lang == "ja" [ツール] else if lang == "uk" [Інструменти] else if lang == "zh" [工具] else { todo } @@ -32,6 +35,7 @@ "intro/install": ( content: include "intro/install.typ", title: if lang in ("en", "de") [Installation] + else if lang == "ja" [インストール] else if lang == "uk" [Встановлення] else if lang == "zh" [安装] else { todo }, @@ -52,6 +56,7 @@ content: include "intro/install/verify.typ", title: if lang == "en" [Verify Installation] else if lang == "de" [Die Installation überprüfen] + else if lang == "ja" [インストールの確認] else if lang == "uk" [Перевірка встановлення] else if lang == "zh" [验证工具链的安装] else { todo } @@ -64,6 +69,7 @@ content: include "start/index.typ", title: if lang == "en" [Getting started] else if lang == "de" [Erste Schritte] + else if lang == "ja" [入門] else if lang == "uk" [Початок роботи] else if lang == "zh" [开始] else { todo }, @@ -75,6 +81,7 @@ "start/hardware": ( content: include "start/hardware.typ", title: if lang in ("en", "de") [Hardware] + else if lang == "ja" [ハードウェア] else if lang == "uk" [Залізо] else if lang == "zh" [硬件] else { todo } @@ -83,6 +90,7 @@ content: include "start/registers.typ", title: if lang == "en" [Memory-mapped Registers] else if lang == "de" [Im Speicher abgebildete Register] + else if lang == "ja" [メモリマップドレジスタ] else if lang == "uk" [Відображені в пам'яті регістри] else if lang == "zh" [存储映射的寄存器] else { todo } @@ -90,6 +98,7 @@ "start/semihosting": ( content: include "start/semihosting.typ", title: if lang in ("en", "de", "uk") [Semihosting] + else if lang == "ja" [セミホスティング] else if lang == "zh" [半主机模式] else { todo } ), @@ -97,6 +106,7 @@ content: include "start/panicking.typ", title: if lang == "en" [Panicking] else if lang == "de" [In Panik geraten] + else if lang == "ja" [パニック] else if lang == "uk" [Паніка] else if lang == "zh" [运行时恐慌(Panicking)] else { todo } @@ -104,6 +114,7 @@ "start/exceptions": ( content: include "start/exceptions.typ", title: if lang in ("en", "de") [Exceptions] + else if lang == "ja" [例外] else if lang == "uk" [Виключення] else if lang == "zh" [异常] else { todo } @@ -111,6 +122,7 @@ "start/interrupts": ( content: include "start/interrupts.typ", title: if lang in ("en", "de") [Interrupts] + else if lang == "ja" [割り込み] else if lang == "uk" [Переривання] else if lang == "zh" [中断] else { todo } @@ -121,6 +133,7 @@ content: include "peripherals/index.typ", title: if lang == "en" [Peripherals] else if lang == "de" [Peripheriegeräte] + else if lang == "ja" [ペリフェラル] else if lang == "uk" [Периферійні пристрої] else if lang == "zh" [外设] else { todo }, @@ -129,6 +142,7 @@ content: include "peripherals/a-first-attempt.typ", title: if lang == "en" [A first attempt in Rust] else if lang == "de" [Ein erster Versuch in Rust] + else if lang == "ja" [Rustでの最初の試み] else if lang == "uk" [Перша спроба на Rust] else if lang == "zh" [Rust尝鲜] else { todo } @@ -137,6 +151,7 @@ content: include "peripherals/borrowck.typ", title: if lang == "en" [The Borrow Checker] else if lang == "de" [Der Borrow-Prüfer] + else if lang == "ja" [借用チェッカ] else if lang == "uk" [Перевірка запозичень] else if lang == "zh" [借用检查器] else { todo } @@ -144,6 +159,7 @@ "peripherals/singletons": ( content: include "peripherals/singletons.typ", title: if lang in ("en", "de") [Singletons] + else if lang == "ja" [シングルトン] else if lang == "uk" [Одинаки] else if lang == "zh" [单例] else { todo } @@ -154,6 +170,7 @@ content: include "static-guarantees/index.typ", title: if lang == "en" [Static Guarantees] else if lang == "de" [Statische Garantien] + else if lang == "ja" [静的な保証] else if lang == "uk" [Статичні гарантії] else if lang == "zh" [静态保障(static guarantees)] else { todo }, @@ -162,6 +179,7 @@ content: include "static-guarantees/typestate-programming.typ", title: if lang == "en" [Typestate Programming] else if lang == "de" [Typgestützte Programmierung] + else if lang == "ja" [型状態プログラミング] else if lang == "uk" [Програмування типів-станів] else if lang == "zh" [类型状态编程] else { todo } @@ -170,6 +188,7 @@ content: include "static-guarantees/state-machines.typ", title: if lang == "en" [Peripherals as State Machines] else if lang == "de" [Peripheriegeräte als Zustandsmaschinen] + else if lang == "ja" [ステートマシンとしてのペリフェラル] else if lang == "uk" [Периферія як кінцеві автомати] else if lang == "zh" [把外设当作状态机] else { todo } @@ -178,6 +197,7 @@ content: include "static-guarantees/design-contracts.typ", title: if lang == "en" [Design Contracts] else if lang == "de" [Designverträge] + else if lang == "ja" [設計契約] else if lang == "uk" [Угоди щодо дизайну] else if lang == "zh" [设计约定] else { todo } @@ -186,6 +206,7 @@ content: include "static-guarantees/zero-cost-abstractions.typ", title: if lang == "en" [Zero Cost Abstractions] else if lang == "de" [Abstraktionen ohne Kosten] + else if lang == "ja" [ゼロコスト抽象化] else if lang == "uk" [Безкоштовні абстракції] else if lang == "zh" [零成本抽象] else { todo } @@ -196,6 +217,7 @@ content: include "portability/index.typ", title: if lang == "en" [Portability] else if lang == "de" [Portabilität] + else if lang == "ja" [移植性] else if lang == "uk" [Переносимість] else if lang == "zh" [可移植性] else { todo } @@ -204,6 +226,7 @@ content: include "concurrency/index.typ", title: if lang == "en" [Concurrency] else if lang == "de" [Nebenläufigkeit] + else if lang == "ja" [並行性] else if lang == "uk" [Паралелізм] else if lang == "zh" [并发] else { todo } @@ -212,6 +235,7 @@ content: include "collections/index.typ", title: if lang == "en" [Collections] else if lang == "de" [Sammlungen] + else if lang == "ja" [コレクション] else if lang == "uk" [Колекції] else if lang == "zh" [容器] else { todo } @@ -274,6 +298,7 @@ content: include "c-tips/index.typ", title: if lang == "en" [Tips for embedded C developers] else if lang == "de" [Tipps für Entwickler im Bereich Embedded-C] + else if lang == "ja" [組込みC開発者へのヒント] else if lang == "uk" [Поради для розробників мовою C] else if lang == "zh" [给嵌入式C开发者的贴士] else { todo } @@ -282,6 +307,7 @@ content: include "interoperability/index.typ", title: if lang == "en" [Interoperability] else if lang == "de" [Interoperabilität] + else if lang == "ja" [相互運用性] else if lang == "uk" [Сумісність] else if lang == "zh" [互操性] else { todo }, @@ -290,6 +316,7 @@ content: include "interoperability/c-with-rust.typ", title: if lang == "en" [A little C with your Rust] else if lang == "de" [Ein bisschen C zu Ihrem Rust] + else if lang == "ja" [Rustと少しのC] else if lang == "uk" [Трошки C в вашому Rust] else if lang == "zh" [使用C的Rust] else { todo } @@ -298,6 +325,7 @@ content: include "interoperability/rust-with-c.typ", title: if lang == "en" [A little Rust with your C] else if lang == "de" [Ein bisschen Rust zu Ihrem C] + else if lang == "ja" [Cと少しのRust] else if lang == "uk" [Трошки Rust в вашому C] else if lang == "zh" [使用Rust的C] else { todo } @@ -308,6 +336,7 @@ content: include "unsorted/index.typ", title: if lang == "en" [Unsorted topics] else if lang == "de" [Unsortierte Themen] + else if lang == "ja" [未分類のトピック] else if lang == "uk" [Невідсортовані теми] else if lang == "zh" [没有排序的主题] else { todo }, @@ -316,6 +345,7 @@ content: include "unsorted/speed-vs-size.typ", title: if lang == "en" [Optimizations: The speed size tradeoff] else if lang == "de" [Optimierungen: Der Kompromiss zwischen Geschwindigkeit und Größe] + else if lang == "ja" [最適化: 速度とサイズのトレードオフ] else if lang == "uk" [Оптимізація: компроміс між швидкістю та розміром] else if lang == "zh" [优化: 速度与大小间的博弈] else { todo } diff --git a/typ/config.typ b/typ/config.typ index 0b85463e..8af09cc1 100644 --- a/typ/config.typ +++ b/typ/config.typ @@ -3,6 +3,7 @@ #let languages = ( "en": [English], "de": [German], + "ja": [Japanese], "uk": [Ukrainian], "zh": [Chinese] ) @@ -10,11 +11,15 @@ #let whole = int(tgt == "pdf") // str: use in code blocks -#let todos = (if lang == "de" { "unübersetzt" } - else if lang == "uk" { "не перекладено" } - else if lang == "zh" { "未翻译" } - else { "untranslated" } -) +#let todos = { + ( + en: "untranslated", + de: "unübersetzt", + ja: "未翻訳", + uk: "не перекладено", + zh: "未翻译", + ).at(lang) +} // content: use in other places #let todo = text( fill: red, @@ -22,11 +27,13 @@ ) // str: use in code blocks #let todoupds(l) = { - assert(l in languages, message: "add language name") - if lang == "de" { "die Übersetzung ist veraltet" } - else if lang == "uk" { "переклад застарів" } - else if lang == "zh" { "翻译已过时" } - else { "translation is outdated" } + ( + en: "translation is outdated", + de: "die Übersetzung ist veraltet", + ja: "翻訳が古くなっています", + uk: "переклад застарів", + zh: "翻译已过时", + ).at(lang) } // content: use in other places #let todoupd(l) = text( fill: orange, diff --git a/typ/intro/hardware.typ b/typ/intro/hardware.typ index 8ee8201a..19ab6d8d 100644 --- a/typ/intro/hardware.typ +++ b/typ/intro/hardware.typ @@ -3,6 +3,7 @@ #h1(offset: whole, if lang == "en" [Meet Your Hardware] else if lang == "de" [Lernen Sie Ihre Hardware kennen] + else if lang == "ja" [ハードウェアとの出会い] else if lang == "uk" [Зустрічайте залізо] else if lang == "zh" [熟悉你的硬件] else { todo }) @@ -11,6 +12,8 @@ Let's get familiar with the hardware we'll be working with. ] else if lang == "de" [ Machen wir uns mit der Hardware vertraut, mit der wir arbeiten werden. +] else if lang == "ja" [ + これから作業するハードウェアに詳しくなりましょう。 ] else if lang == "uk" [ Давайте ознайомимося з обладнанням, з яким ми будемо працювати. ] else if lang == "zh" [ @@ -23,6 +26,7 @@ = #(if lang in ("en", "zh") [STM32F3DISCOVERY (the "F3")] else if lang == "de" [STM32F3DISCOVERY (die "F3")] + else if lang == "ja" [STM32F3DISCOVERY ("F3")] else if lang == "uk" [STM32F3DISCOVERY (скорочено "F3")] else { todo }) @@ -30,6 +34,8 @@ What does this board contain? ] else if lang == "de" [ Was beinhaltet diese Platine? +] else if lang == "ja" [ + 私たちは、本書内でこのボードを"F3"と呼びます。 ] else if lang == "uk" [ Що міститься на цій платі? ] else if lang == "zh" [ @@ -88,6 +94,26 @@ Dieser Mikrocontroller ist eigentlich Teil eines On-Board-Programmers/Debuggers und mit dem Mini-USB-Anschluss namens „USB ST-LINK" verbunden. +] else if lang == "ja" [ + このボードには何が搭載されているか見てみましょう。 + - STM32F303VCT6マイクロコントローラが1つ。このマイクロコントローラは、次のものを搭載しています。 + - 単精度浮動小数点演算をハードウェアサポートし、最大72MHzのクロック周波数で動作するシングルコアのARM + Cortex-M4Fプロセッサ + - 256 KiBの"フラッシュ"メモリ (1 KiB = 10#[*24*] bytes) + - 48 KiBのRAM + - 多くの"ペリフェラル": タイマ、GPIO、I2C、SPI、USART、他 + - 両側面の"ヘッダ"に配置された多数の"ピン" + - *重要* このマイクロコントローラは、約3.3ボルトで動作します。 + - #link(wiki_accelerometer)[加速度センサ]と#link(wiki_magnetometer)[磁気センサ]が1つずつ + (1つのパッケージにまとめられています) + - #link(wiki_gyroscope)[ジャイロセンサ]が1つ + - 円形に配置された8個のユーザLED + - 第2のマイクロコントローラ: + STM32F103CBT。このマイクロコントローラは、実際には、ST-LINKというオンボードプログラマおよびデバッガの一部であり、"USB + ST-LINK"という名前のUSBポートに接続されています。 + - "USB + USER"というラベルが付いている第2のUSBポート。このUSBポートは、メインマイクロコントローラ + (STM32F303VCT6)に接続されており、アプリケーションで利用できます。 ] else if lang == "uk" [ - Мікроконтролер #link(url_f303)[STM32F303VCT6]. Цей мікроконтролер має: - Одноядерний процесор ARM Cortex-M4F з апаратною підтримкою операцій diff --git a/typ/intro/index.typ b/typ/intro/index.typ index 4c20b942..10c1c9d8 100644 --- a/typ/intro/index.typ +++ b/typ/intro/index.typ @@ -2,6 +2,7 @@ #h1(if lang == "en" [Introduction] else if lang == "de" [Einleitung] + else if lang == "ja" [導入] else if lang == "uk" [Вступ] else if lang == "zh" [引言] else { todo }) @@ -13,6 +14,9 @@ ] else if lang == "de" [ Willkommen bei „Das Embedded-Rust Buch“ – einem Einführungswerk zur Verwendung der Programmiersprache Rust auf „Bare-Metal“-Embedded-Systemen, wie etwa Mikrocontrollern. +] else if lang == "ja" [ + _The Embedded Rust + Book_へようこそ。Rustをマイクロコントローラのような、「ベアメタル」の組込みシステムで使うための入門書です。 ] else if lang == "uk" [ Ласкаво просимо до The Embedded Rust Book: ознайомчої книги про використання мови програмування Rust у вбудованих "Bare Metal" системах, таких як мікроконтролери. @@ -22,6 +26,7 @@ = #(if lang == "en" [Who Embedded Rust is For] else if lang == "de" [Für wen Embedded-Rust geeignet ist] + else if lang == "ja" [組込みRustは誰のためのもの] else if lang == "uk" [Для кого Вбудований Rust] else if lang == "zh" [嵌入式Rust是为谁准备的] else { todo }) @@ -39,6 +44,9 @@ (Siehe auch #link(url_rustbook)[Für wen ist Rust geeignet?]) +] else if lang == "ja" [ + 組込みRustは、Rustの高い抽象度と安全性のもと、組込みプログラミングをしたい人のためのものです。 + (#url_rustbook)[Rustは誰のためのもの]も合わせて見て下さい) ] else if lang == "uk" [ Вбудований Rust для кожного, хто бажає програмувати вбудовані системи використовуючи переваги концепцій вищого рівня та гарантій безпеки, які надає мова Rust. @@ -49,6 +57,7 @@ = #(if lang == "en" [Scope] else if lang == "de" [Umfang] + else if lang == "ja" [スコープ] else if lang == "uk" [Область застосування] else if lang == "zh" [本书范围] else { todo }) @@ -71,6 +80,11 @@ Rust optimal nutzt, um korrektere Embedded-Software zu schreiben. - Dient in einigen Fällen als Kochbuch -- zum Beispiel: Wie kombiniere ich C und Rust in einem einzigen Projekt? +] else if lang == "ja" [ + この本の目的は、以下の通りです。 + - 組込みRustをできる限り速く開始できるようにします。すなわち、開発環境のセットアップ方法です。 + - 組込み開発におけるRustの_現在_のベストプラクティスを共有します。つまり、より正しい組込みソフトウェアを書くための、Rustの最善な利用方法です。 + - いくつかのケースに対するマニュアルを提供します。例えば、1つのプロジェクト内で、C言語とRustとを混在する方法です。 ] else if lang == "uk" [ Цілі цієї книги: - Надати розробникам швидкий доступ до розробки вбудованих систем на Rust. @@ -99,6 +113,10 @@ setzt das Buch keine Vorkenntnisse zu dieser speziellen Architektur voraus und erläutert die entsprechenden Besonderheiten, wo immer dies erforderlich ist. +] else if lang == "ja" [ + 本書は出来る限り一般的な事項を取り扱います。ただし、説明を簡単にするために、全ての例で、ARM + Cortex-Mアーキテクチャを利用します。 + 読者は、このアーキテクチャに詳しい必要はありません。本書では、アーキテクチャ固有の詳細について、必要に応じて説明をします。 ] else if lang == "uk" [ Ця книга намагається бути якомога більш загальною, але щоб полегшити роботу як читачам, так і авторам, у ній використовується архітектура ARM Cortex-M у всіх її прикладах. @@ -111,6 +129,7 @@ = #(if lang == "en" [Who This Book is For] else if lang == "de" [Für wen dieses Buch gedacht ist] + else if lang == "ja" [この本は誰のためのもの] else if lang == "uk" [Для кого Ця Книга] else if lang == "zh" [这本书是为谁准备的] else { todo }) @@ -134,6 +153,8 @@ Buch zu ziehen und das Leseerlebnis zu verbessern. Im Abschnitt „Weitere Ressourcen" finden Sie Hinweise zu Themen, in die Sie sich bei Bedarf einarbeiten können. +] else if lang == "ja" [ + 本書は、組込み開発か、Rustかのバックグラウンドを持つ人々に向けたものです。しかし、組込みRustに興味がある人なら、誰でも、この本から何かを得られると思います。本書による学習効果を高めるために、事前知識が不足している読者は、「仮定と前提条件」のセクションを読み、不足している知識を補うことをお勧めします。不足知識を補うリソースを見つけるために、「その他のリソース」セクションをチェックすることができます。 ] else if lang == "uk" [ Ця книга розрахована на людей, які мають досвід роботи з вбудованими програмами або з Rust, але ми віримо, що всі, хто цікавиться програмуванням у вбудованому Rust, можуть щось отримати з цієї книги. @@ -146,6 +167,7 @@ == #(if lang == "en" [Assumptions and Prerequisites] else if lang == "de" [Annahmen und Voraussetzungen] + else if lang == "ja" [仮定と前提条件] else if lang == "uk" [Припущення та передумови] else if lang == "zh" [要求和预备知识] else { todo }) @@ -179,6 +201,16 @@ - Im Speicher abgebildete Peripheriegeräte - Interrupts - Gängige Schnittstellen wie I2C, SPI, seriell usw. +] else if lang == "ja" [ + - Rustでのプログラミングを楽しんでおり、デスクトップ環境でRustアプリケーションを書いたり、実行したり、デバッグしたりしたことがあることを前提とします。また、本書ではRust + 2018を対象とするため、2018 + editionのイディオムに慣れ親しんでいる必要があります。 + - C, C++, + Adaといった言語で組込みシステムを開発、デバッグすることに慣れており、次の概念になじみがあることを想定します。 + - クロスコンパイル + - メモリマップ方式のペリフェラル + - 割り込み + - I2C、SPI、シリアルといった一般的なインタフェース ] else if lang == "uk" [ - Вам зручно користуватися мовою програмування Rust і ви вже писали, запускали та налагоджували програми Rust у середовищі робочого столу. @@ -202,6 +234,7 @@ == #(if lang == "en" [Other Resources] else if lang == "de" [Weitere Ressourcen] + else if lang == "ja" [その他のリソース] else if lang == "uk" [Інші ресурси] else if lang == "zh" [其它资源] else { todo }) @@ -214,6 +247,8 @@ Falls Ihnen einer der oben genannten Punkte nicht vertraut ist oder Sie mehr über ein bestimmtes, in diesem Buch erwähntes Thema erfahren möchten, könnten einige dieser Ressourcen hilfreich für Sie sein. +] else if lang == "ja" [ + もしあなたが上述した何らかの事項をよく知らない場合、もしくは、本書内の特定トピックに関して、より詳細な情報を知りたい場合、これらのリソースが役に立つでしょう。 ] else if lang == "uk" [ Якщо ви не знайомі з чимось із згаданого вище або вам потрібна додаткова інформація про конкретну тему, згадану в цій книзі, деякі з цих ресурсів можуть стати в нагоді. ] else if lang == "zh" [ @@ -228,14 +263,17 @@ table.header( if lang == "en" [Topic] else if lang == "de" [Thema] + else if lang == "ja" [トピック] else if lang == "zh" [主题] else { todo }, if lang == "en" [Resource] else if lang == "de" [Ressource] + else if lang == "ja" [リソース] else if lang == "zh" [资源] else { todo }, if lang == "en" [Description] else if lang == "de" [Beschreibung] + else if lang == "ja" [説明] else if lang == "zh" [描述] else { todo } ), @@ -246,6 +284,8 @@ ] else if lang == "de" [ Wenn Sie sich mit Rust noch nicht sicher fühlen, empfehlen wir Ihnen dringend, dieses Buch zu lesen. + ] else if lang == "ja" [ + もしRustに親しんでいない場合、この本を読むことを強くお勧めします。 ] else if lang == "zh" [ 如果你还不熟悉Rust,我们强烈建议你读这本书. ] else { todo }, @@ -267,6 +307,8 @@ ] else if lang == "de" [ Hier finden Sie weitere Ressourcen, die von der Embedded Working Group von Rust bereitgestellt werden. + ] else if lang == "ja" [ + Rust組込みワーキンググループによるいくらかのリソースがあります。 ] else if lang == "zh" [ 在这里,你可以找到由Rust的嵌入式工作组提供的许多其它资源. ] else { todo }, @@ -277,6 +319,8 @@ The nitty gritty details when doing embedded programming in Rust. ] else if lang == "de" [ Die Feinheiten der Embedded-Programmierung mit Rust. + ] else if lang == "ja" [ + Rustで組込みプログラミングを行うときのより深い詳細が記載されています。 ] else if lang == "zh" [ 用Rust进行嵌入式编程的细节. ] else { todo }, @@ -287,6 +331,8 @@ Frequently asked questions about Rust in an embedded context. ] else if lang == "de" [ Häufig gestellte Fragen zu Rust im Embedded-Umfeld. + ] else if lang == "ja" [ + 組込みでRustを使う際のよくある質問と回答です。 ] else if lang == "zh" [ Rust在嵌入式上下文中遇到的常见问题. ] else { todo }, @@ -352,6 +398,7 @@ if lang == "en" [ == #(if lang == "en" [How to Use This Book] else if lang == "de" [Wie Sie dieses Buch nutzen] + else if lang == "ja" [この本はどう使う] else if lang == "uk" [Як читати цю книгу] else if lang == "zh" [如何使用这本书] else { todo }) @@ -367,6 +414,8 @@ if lang == "en" [ Kapitel auf; dabei werden bestimmte Themen in den früheren Kapiteln möglicherweise noch nicht bis ins Detail behandelt, sondern erst in einem späteren Kapitel erneut aufgegriffen. +] else if lang == "ja" [ + この本は、前から順番に読んでいくことを想定しています。後半の章は、前半の章で説明する概念に基づいて成り立っています。前半の章では、トピックの詳細に深入りせず、後半の章で再訪問します。 ] else if lang == "uk" [ Ця книга зазвичай передбачає, що ви читаєте її послідовно. Подальші розділи ґрунтуються на концепціях попередніх розділів, і попередні розділи можуть @@ -394,6 +443,9 @@ if lang == "en" [ Implementierungsdetails der Mikrocontroller je nach Hersteller -- und oft sogar zwischen verschiedenen Mikrocontroller-Familien desselben Herstellers. +] else if lang == "ja" [ + この本は、ほとんどの例で、STマイクロエレクトロニクスの#link(url_f3disco)[STM32F3DISCOVERY]開発ボードを使用します。このボードは、ARM + Cortex-Mアーキテクチャをベースとしています。基本機能はこのアーキテクチャベースのCPUでは共通です。一方、ペリフェラルとマイクロコントローラ実装の詳細は、他のベンダーと異なります。同じSTマイクロエレクトロニクスのマイクロコントローラファミリでも、違いがあります。 ] else if lang == "uk" [ Для більшості наведених у цій книзі прикладів використовуватиметься розробна плата #link(url_f3disco)[STM32F3DISCOVERY] від STMicroelectronics. Ця плата @@ -416,6 +468,8 @@ if lang == "en" [ Aus diesem Grund empfehlen wir die Anschaffung des Entwicklungsboards #link(url_f3disco)[STM32F3DISCOVERY], um die Beispiele in diesem Buch praktisch nachvollziehen zu können. +] else if lang == "ja" [ + 上記の理由から、本書内の例を理解するために、#link(url_f3disco)[STM32F3DISCOVERY]開発ボードを購入することをお勧めします。 ] else if lang == "uk" [ З цієї причини ми пропонуємо придбати плату розробки #link(url_f3disco)[STM32F3DISCOVERY] щоб повторювати приклади з цієї книги. diff --git a/typ/intro/install.typ b/typ/intro/install.typ index b83f4836..73b6d03f 100644 --- a/typ/intro/install.typ +++ b/typ/intro/install.typ @@ -3,6 +3,7 @@ #h1(offset: whole, if lang == "en" [Installing the tools] else if lang == "de" [Werkzeuge installieren] + else if lang == "ja" [ツールのインストール] else if lang == "zh" [安装工具] else { todo }) @@ -12,12 +13,15 @@ ] else if lang == "de" [ Diese Seite enthält betriebssystemunabhängige Installationsanleitungen für einige der Werkzeuge: +] else if lang == "ja" [ + このページには、いくつかのツールのOSに依存しないインストール手順を掲載します。 ] else if lang == "zh" [ 这一页包含的工具安装指令与操作系统无关: ] else { todo } == #(if lang == "en" [Rust Toolchain] else if lang == "de" [Rust-Werkzeuge] + else if lang == "ja" [Rustツールチェイン] else if lang == "zh" [Rust 工具链] else { todo }) @@ -26,6 +30,8 @@ Install rustup by following the instructions at #ln_rustup. ] else if lang == "de" [ Installieren Sie rustup, indem Sie die Anweisungen unter #ln_rustup. +] else if lang == "ja" [ + #ln_rustup;の手順に従って、rustupをインストールします。 ] else { todo } #if lang == "en" [ @@ -34,6 +40,9 @@ ] else if lang == "de" [ *HINWEIS* Stellen Sie sicher, dass Sie eine Compiler-Version verwenden, die mindestens `1.31` entspricht. Der Befehl `rustc -V` sollte ein Datum ausgeben, das neuer ist als das unten angegebene. +] else if lang == "ja" [ + *注意* + コンパイラのバージョンが`1.31`以上であることを確認して下さい。`rustc -v`は下記に示す日付より新しい日付を返すべきです。 ] else if lang == "zh" [ *注意* 确保你的编译器版本等于或者大于`1.31`版本。`rustc -V`应该返回一个比下列日期更新的日期。 @@ -60,6 +69,11 @@ rustc 1.31.1 (b6c32da9b 2018-12-18) STM32F3DISCOVERY-Board, das für die Beispiele in diesem Buch verwendet wird, verwenden Sie das Ziel `thumbv7em-none-eabihf`. #link(url_find)[Finden Sie den für Sie am besten geeigneten Cortex-M.] +] else if lang == "ja" [ + #todoupd("ja") + バンド幅とディスク使用量に関する懸念から、デフォルトインストールではネイティブコンパイルのみをサポートします。 + ARM + Cortex-Mアーキテクチャのクロスコンパイラを追加するために、下記のコンパイルターゲットをインストールします。 ] else if lang == "zh" [ #todoupd("zh") 考虑到带宽和磁盘的使用量,默认的安装只支持主机环境的编译。为了添加对ARM @@ -204,9 +218,11 @@ cargo install cargo-generate Now follow the instructions specific to the OS you are using: ] else if lang == "de" [ Befolgen Sie nun die Anweisungen für das von Ihnen verwendete Betriebssystem: +] else if lang == "ja" [ + 使用しているOSに特有の手順に従って下さい。 ] else if lang == "zh" [ 现在根据你使用的操作系统,来执行对应的指令: ] else { todo } -- #link("install/linux.html")[Linux] -- #link("install/windows.html")[Windows] -- #link("install/macos.html")[macOS] +- #link()[Linux] +- #link()[Windows] +- #link()[macOS] diff --git a/typ/intro/install/linux.typ b/typ/intro/install/linux.typ index cbf4e1a7..bf0aa9e0 100644 --- a/typ/intro/install/linux.typ +++ b/typ/intro/install/linux.typ @@ -2,16 +2,19 @@ #h1(offset: whole*2, [Linux]) + #if lang == "en" [ Here are the installation commands for a few Linux distributions. ] else if lang == "de" [ Hier sind die Installationsbefehle für einige Linux-Distributionen. +] else if lang == "ja" [ + いくつかのLinuxディストリビューションのインストールコマンドを示します。 ] else if lang == "zh" [ 这部分是在某些Linux发行版下的安装指令。 ] else { todo } -= #(if lang == "en" [Packages] += #(if lang in ("en", "ja") [Packages] else if lang == "de" [Pakete] else if lang == "zh" [依赖包] else { todo }) @@ -20,6 +23,8 @@ - Ubuntu 18.04 or newer / Debian stretch or newer ] else if lang == "de" [ - Ubuntu 18.04 oder neuer / Debian Stretch oder neuer +] else if lang == "ja" [ + - Ubuntu 18.04以上 / Debian stretch以降 ] else if lang == "zh" [ - Ubuntu 18.04 或者更新的版本 / Debian stretch 或者更新的版本 ] else { todo } @@ -31,6 +36,9 @@ ] else if lang == "de" [ *HINWEIS* `gdb-multiarch` ist der GDB-Befehl, den Sie zum Debuggen Ihrer ARM-Cortex-M-Programme verwenden werden. +] else if lang == "ja" [ + *注記* `gdb-multiarch`は、ARM + Cortex-Mプログラムをデバッグするために使用するGDBのコマンドです。 ] else if lang == "zh" [ * 注意* `gdb-multiarch` 是你将用来调试你的ARM ] else { todo } @@ -44,6 +52,8 @@ sudo apt install gdb-multiarch openocd qemu-system-arm - Ubuntu 14.04 and 16.04 ] else if lang == "de" [ - Ubuntu 14.04 und 16.04 +] else if lang == "ja" [ + - Ubuntu 14.04と16.04 ] else { todo } #quote(block: true)[ @@ -53,6 +63,9 @@ sudo apt install gdb-multiarch openocd qemu-system-arm ] else if lang == "de" [ *HINWEIS* `arm-none-eabi-gdb` ist der GDB-Befehl, den Sie zum Debuggen Ihrer ARM-Cortex-M-Programme verwenden werden. +] else if lang == "ja" [ + *注記* `arm-none-eabi-gdb`は、ARM + Cortex-Mプログラムをデバッグするために使用するGDBのコマンドです。 ] else if lang == "zh" [ *注意* `arm-none-eabi-gdb` 是你将用来调试你的ARM Cortex-M程序的GDB命令 @@ -67,6 +80,8 @@ sudo apt install gdb-arm-none-eabi openocd qemu-system-arm - Fedora 27 or newer ] else if lang == "de" [ - Fedora 27 oder neuer +] else if lang == "ja" [ + - Fedora 27以上 ] else if lang == "zh" [ - Fedora 27 或者更新的版本 ] else { todo } @@ -84,6 +99,9 @@ sudo dnf install gdb openocd qemu-system-arm ] else if lang == "de" [ *HINWEIS* `arm-none-eabi-gdb` ist der GDB-Befehl, den Sie zum Debuggen von ARM-Cortex-M-Programmen verwenden. +] else if lang == "ja" [ + *注記* `arm-none-eabi-gdb`は、ARM + Cortex-Mプログラムをデバッグするために使用するGDBのコマンドです。 ] else if lang == "zh" [ *注意* `arm-none-eabi-gdb` 是你将用来调试你的ARM Cortex-M程序的GDB命令 ] else { todo } @@ -95,6 +113,7 @@ sudo pacman -S arm-none-eabi-gdb qemu-system-arm openocd = #(if lang == "en" [udev rules] else if lang == "de" [udev-Regeln] + else if lang == "ja" [udevルール] else if lang == "zh" [udev 规则] else { todo }) @@ -102,20 +121,23 @@ sudo pacman -S arm-none-eabi-gdb qemu-system-arm openocd This rule lets you use OpenOCD with the Discovery board without root privilege. ] else if lang == "de" [ Diese Regel ermöglicht die Nutzung von OpenOCD mit dem Discovery Board ohne Root-Rechte. +] else if lang == "ja" [ + このルールにより、ルート権限なしで、OpenOCDをDiscoveryボードに対して使えるようにします。 ] else if lang == "zh" [ 这个规则可以让你在不使用超级用户权限的情况下,使用OpenOCD和Discovery开发板。 ] else { todo } -#{ -let f = `/etc/udev/rules.d/70-st-link.rules` -if lang == "en" [ - Create the file #f with the contents shown below. +#let file_stlink = `/etc/udev/rules.d/70-st-link.rules` +#if lang == "en" [ + Create the file #file_stlink with the contents shown below. ] else if lang == "de" [ - Erstellen Sie die Datei #f mit dem unten stehenden Inhalt. + Erstellen Sie die Datei #file_stlink mit dem unten stehenden Inhalt. +] else if lang == "ja" [ + #todoupd("ja") + 下記の内容で、`/etc/udev/rules.d`ディレクトリにファイルを作成します。 ] else if lang == "zh" [ - 生成包含下列内容的 `/etc/udev/rules.d/70-st-link.rules` 文件 + 生成包含下列内容的 #file_stlink 文件 ] else { todo } -} ```text # STM32F3DISCOVERY rev A/B - ST-LINK/V2 @@ -129,6 +151,8 @@ ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374b", TAG+="uaccess" Then reload all the udev rules with: ] else if lang == "de" [ Laden Sie anschließend alle udev-Regeln neu mit: +] else if lang == "ja" [ + その後、全てのudevルールをリロードします。 ] else if lang == "zh" [ 然后重新加载所有的udev规则 ] else { todo } @@ -142,6 +166,8 @@ sudo udevadm control --reload-rules ] else if lang == "de" [ Wenn Sie die Platine an Ihren Laptop angeschlossen hatten, ziehen Sie den Stecker heraus und schließen Sie ihn dann erneut an. +] else if lang == "ja" [ + 既にボードをノートPCに接続している場合、一度抜いてから、もう一度接続します。 ] else if lang == "zh" [ 如果你已经把开发板插入到笔记本中了,请拔下它然后再插上它。 ] else { todo } @@ -150,6 +176,8 @@ sudo udevadm control --reload-rules You can check the permissions by running this command: ] else if lang == "de" [ Sie können die Berechtigungen überprüfen, indem Sie diesen Befehl ausführen: +] else if lang == "ja" [ + これらのコマンド実行することで、パーミッションを確認できます。 ] else if lang == "zh" [ 你可以通过运行这个命令检查权限: ] else { todo } @@ -212,6 +240,9 @@ user:you:rw- Das angehängte „+" bei den Berechtigungen weist auf eine erweiterte Berechtigung hin. Der Befehl „getfacl" teilt dem Benutzer mit, dass er dieses Gerät verwenden darf. +] else if lang == "ja" [ + #todoupd("ja") + パーミッションに追加された`+`は、パーミッションが拡張されたことを意味しています。 ] else if lang == "zh" [ 权限后的 `+` 指出存在一个扩展权限。`getfacl` 命令显示,`user`也就是`你`,可以使用这个设备。 @@ -221,6 +252,8 @@ user:you:rw- Now, go to the #link()[next section]. ] else if lang == "de" [ Fahren Sie nun mit dem #link()[nächsten Abschnitt] fort. +] else if lang == "ja" [ + それでは、#link()[次のセクション]に進んで下さい。 ] else if lang == "zh" [ 现在,去往#link()[下个章节]. ] else { todo } diff --git a/typ/intro/install/macos.typ b/typ/intro/install/macos.typ index 895370e6..770a9a14 100644 --- a/typ/intro/install/macos.typ +++ b/typ/intro/install/macos.typ @@ -2,6 +2,7 @@ #h1(offset: whole*2, [macOS]) + #let brew_ln = link("http://brew.sh/")[Homebrew] #let ports_ln = link("https://www.macports.org/")[MacPorts] @@ -10,6 +11,9 @@ All the tools can be installed using #brew_ln or #ports_ln: ] else if lang == "de" [ Alle Werkzeuge können mit #brew_ln oder #ports_ln installiert werden: +] else if lang == "ja" [ + #todoupd("ja") + 全てのツールは、#brew_ln;を使ってインストールできます。 ] else if lang == "zh" [ 所有的工具都可以使用#brew_ln;或者#ports_ln;来安装: ] else { todo } @@ -66,6 +70,8 @@ $ sudo port install qemu That's all! Go to the #link()[next section]. ] else if lang == "de" [ Das war's! Gehen Sie zum #link()[nächsten Abschnitt]. +] else if lang == "ja" [ + 以上です!#link()[次のセクション]に進んで下さい。 ] else if lang == "zh" [ 这是全部内容,请转入#link()[下个章节]. ] else { todo } diff --git a/typ/intro/install/verify.typ b/typ/intro/install/verify.typ index 9fd3f0dd..5151b30d 100644 --- a/typ/intro/install/verify.typ +++ b/typ/intro/install/verify.typ @@ -3,6 +3,7 @@ #h1(offset: whole*2, if lang == "en" [Verify Installation] else if lang == "de" [Die Installation überprüfen] + else if lang == "ja" [インストールの確認] else if lang == "zh" [安装验证] else { todo }) @@ -13,6 +14,8 @@ ] else if lang == "de" [ In diesem Abschnitt überprüfen wir, ob einige der erforderlichen Werkzeuge bzw. Treiber korrekt installiert und konfiguriert wurden. +] else if lang == "ja" [ + このセクションでは、必要となるツールとドライバが正しくインストールされ、設定されていることを確認します。 ] else if lang == "zh" [ 在这个章节中我们将检查工具和驱动是否已经被正确地安装和配置了。 ] else { todo } @@ -26,6 +29,11 @@ Discovery-Board. Das Discovery-Board verfügt über zwei USB-Anschlüsse; verwenden Sie den mit „USB ST-LINK" beschrifteten Anschluss, der sich mittig an der Platinenkante befindet. +] else if lang == "ja" [ + マイクロUSBケーブルを使って、ノートPC / + PCをdiscoveryボードに接続して下さい。 + discoveryボードは2つのUSBコネクタを搭載しています。 + ボード端の中央にある"USB ST-LINK"とラベルが付いたものを使用して下さい。 ] else if lang == "zh" [ 使用一个micro USB线缆将你的笔记本/个人电脑连接到discovery开发板上。discovery开发板有两个USB连接器;使用标记着"USB ST-LINK"的那个,它位于开发板边缘的中间位置。 ] else { todo } @@ -36,6 +44,8 @@ ] else if lang == "de" [ Überprüfen Sie außerdem, ob die ST-LINK-Stiftleiste bestückt ist. Siehe dazu die Abbildung unten; die ST-LINK-Stiftleiste ist dort hervorgehoben. +] else if lang == "ja" [ + ST-LINKヘッダが装着されていることも確認します。下の写真の赤丸で囲った部分がST-LINKヘッダです。 ] else if lang == "zh" [ 也要检查下ST-LINK的短路帽是否被安装了。看下面的图;ST-LINK短路帽用红色圈起来了。 ] else { todo } @@ -44,6 +54,8 @@ Now run the following command: ] else if lang == "de" [ Führen Sie nun den folgenden Befehl aus: +] else if lang == "ja" [ + それでは、次のコマンドを実行して下さい。 ] else if lang == "zh" [ 现在运行下面的命令: ] else { todo } @@ -77,6 +89,8 @@ openocd -f interface/stlink.cfg -f target/stm32f3x.cfg ] else if lang == "de" [ Sie sollten die folgende Ausgabe erhalten und das Programm sollte die Konsole blockieren: +] else if lang == "ja" [ + 次の出力が得られ、プログラムはコンソールをブロックするはずです。 ] else if lang == "zh" [ 你应该看到了下面的输出,且程序应该阻塞住了控制台: ] else { todo } @@ -109,6 +123,9 @@ Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints Zeile bezüglich Breakpoints und Watchpoints sollten Sie sehen. Wenn dies der Fall ist, beenden Sie den OpenOCD-Prozess und fahren Sie mit dem #link()[nächsten Abschnitt] fort. +] else if lang == "ja" [ + 確認作業とは直接関係しませんが、ブレイクポイントとウォッチポイントに関する最後の行を取得したはずです。 + 取得できた場合、OpenOCDプロセスを停止し、#link()[次のセクション]へ進んで下さい。 ] else if lang == "zh" [ 内容可能并不是一模一样,但是在最后一行,你应该看到了breakpoints和watchpoints,如果你看到了,那就终止OpenOCD进程然后进入#link()[下个章节] ] else { todo } @@ -119,6 +136,8 @@ Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints ] else if lang == "de" [ Falls du die Zeile „breakpoints" nicht erhalten hast, versuche einen der folgenden Befehle. +] else if lang == "ja" [ + "breakpoints"の行が取得できなかった場合、次のコマンドを試してく下さい。 ] else if lang == "zh" [ 如果你没看到"breakpoints"这行,尝试下下列命令中的某一个命令。 ] else { todo } @@ -142,6 +161,10 @@ openocd -f interface/stlink-v2-1.cfg -f target/stm32f3x.cfg Problem dar, aber merken Sie sich diesen Umstand, da Sie die Konfiguration später etwas anders vornehmen müssen. Sie können zum #link()[nächsten Abschnitt] übergehen. +] else if lang == "ja" [ + このコマンドが機能した場合、古いハードウェアリビジョンのdiscoveryボードを入手したことを意味します。 + これは問題になりませんが、後で少し設定を変える必要があるので、そのことを覚えておいて下さい。 + #link()[次のセクション]に進むことができます。 ] else if lang == "zh" [ 如果这些命令的某条起作用了,那意味着你使用的discovery开发板是一个旧的版本。那也不成问题,但是你要记住这件事,因为随后你的配置可能有点不同。你可以移到#link()[下个章节]了。 ] else { todo } @@ -156,6 +179,9 @@ openocd -f interface/stlink-v2-1.cfg -f target/stm32f3x.cfg Sie, sie mit Root-Rechten auszuführen (z. B. `sudo openocd ..`). Sollten die Befehle mit Root-Rechten funktionieren, überprüfen Sie, ob die #link()[udev-Regeln] korrekt eingerichtet wurden. +] else if lang == "ja" [ + どちらのコマンドも通常ユーザとしてうまく動かなかった場合、rootパーミッションで実行してみて下さい(例えば、`sudo openocd ..`)。 + コマンドがrootパーミッションで機能した場合、#link()[udevルール]が正しく設定されているか確認して下さい。 ] else if lang == "zh" [ 如果这些命令在普通用户模式下都没用,尝试下使用root模式运行它们(e.g.~`sudo openocd ..`)。如果命令在root模式下起作用,需要检查下#link()[udev rules]是否被正确地设置了。 ] else { todo } @@ -170,6 +196,8 @@ openocd -f interface/stlink-v2-1.cfg -f target/stm32f3x.cfg eröffnen Sie bitte #link(url_issues)[eine Problemmeldung], und wir helfen Ihnen weiter! +] else if lang == "ja" [ + ここまで到着していまい、OpenOCDが動いていないならば、#link(url_issues)[issue]を作って下さい。私たちがあなたを支援します。 ] else if lang == "zh" [ 如果这些都试了,OpenOCD还不工作,请打开一个#link(url_issues)[issue],我们将帮助你! ] else { todo } diff --git a/typ/intro/install/windows.typ b/typ/intro/install/windows.typ index be191a4c..234c70bb 100644 --- a/typ/intro/install/windows.typ +++ b/typ/intro/install/windows.typ @@ -2,6 +2,7 @@ #h1(offset: whole*2, [Windows]) + = `arm-none-eabi-gdb` @@ -18,6 +19,10 @@ Abschluss des Installationsvorgangs die Option „Add path to environment variable". Überprüfen Sie anschließend, ob die Werkzeuge in Ihrem `%PATH%` enthalten sind: +] else if lang == "ja" [ + ARMはWindows向けに`.exe`インストーラを提供しています。#link(url_arm_gnu)[here]から1つを入手して、手順に従って下さい。 + インストールプロセスが終了する直前に"環境変数にパスを追加"オプションを選択します。 + その後、ツールが`%PATH%`にあることを確認します。 ] else if lang == "zh" [ ARM提供了用于Windows的`.exe`安装程序。从#link(url_arm_gnu)[这里]获取, 然后按照说明操作。 在完成安装之前,勾选/选择"Add path to environment @@ -76,11 +81,15 @@ Open On-Chip Debugger 0.10.0 Grab QEMU from #link(url_qemu)[the official website]. ] else if lang == "de" [ Lade QEMU von #link(url_qemu)[der offiziellen Website] herunter. +] else if lang == "ja" [ + #link(url_qemu)[QEMU公式サイト]からQEMUを入手します。 ] else if lang == "zh" [ 从#link(url_qemu)[官网]获取QEMU。 ] else { todo } -= ST-LINK USB driver += #(if lang in ("en", "de", "zh") [ST-LINK USB driver] + else if lang == "ja" [ST-LINK USBドライバ] + else { todo }) #let url_stlink = "http://www.st.com/en/embedded-software/stsw-link009.html" #if lang == "en" [ @@ -92,6 +101,9 @@ Open On-Chip Debugger 0.10.0 installieren, da OpenOCD sonst nicht funktioniert. Befolgen Sie die Anweisungen des Installationsprogramms und stellen Sie sicher, dass Sie die richtige Version des Treibers (32-Bit oder 64-Bit) installieren. +] else if lang == "ja" [ + #link(url_stlink)[USBドライバ]もインストールする必要があります。そうでなければOpenOCDは動きません。インストーラの手順に従って下さい。 + そして、正しいドライバのバージョン(32ビットか64ビット)をインストールすることを確認して下さい。 ] else if lang == "zh" [ 你还需要安装这个 #link(url_stlink)[USB驱动] @@ -102,6 +114,8 @@ Open On-Chip Debugger 0.10.0 That's all! Go to the #link()[next section]. ] else if lang == "de" [ Das war's! Gehen Sie zum #link()[nächsten Abschnitt]. +] else if lang == "ja" [ + 以上です!#link()[次のセクション]に進んで下さい。 ] else if lang == "zh" [ 以上是全部内容!转到 #link()[下个章节]。 ] else { todo } diff --git a/typ/intro/no-std.typ b/typ/intro/no-std.typ index d25f1981..0c1c581e 100644 --- a/typ/intro/no-std.typ +++ b/typ/intro/no-std.typ @@ -3,6 +3,7 @@ #h1(offset: whole, if lang == "en" [A `no_std` Rust Environment] else if lang == "de" [Eine `no_std`-Rust-Umgebung] + else if lang == "ja" [Rustの`no_std`環境] else if lang == "zh" [一个 `no_std` Rust环境] else { todo }) @@ -27,6 +28,13 @@ und 1 GB RAM verfügt. Je nach Zielsystem und Anwendungsfall gelten bei der Programmierung unterschiedliche Einschränkungen und Rahmenbedingungen. +] else if lang == "ja" [ + 組込みプログラミングという用語は、様々な分野のプログラミングに使用されます。 + たった数キロバイトのRAMかROMが付随する8ビットMCU + (例えば、#ln_st72325)から、32/64ビットの4コア + Cortex-A53 \@ 1.4GHzと1GBのRAMが搭載されたRaspberry + Pi(#ln_rpi)のようなシステムまで、幅広いです。 + どのような種類の目的とユースケースがあるか、によって、コードを書くときに異なる制限/限界が課されます。 ] else if lang == "zh" [ 嵌入式编程这个词被广泛用于许多不同的编程场景中。小到RAM和ROM只有KB的8位机(像是#ln_st72325),大到一个具有32/64位4核Cortex-A53和1GB RAM的系统,比如树莓派(#ln_rpi)。当编写代码时,取决于你的目标环境和用例,将会有不同的限制和局限。 @@ -36,12 +44,15 @@ There are two general Embedded Programming classifications: ] else if lang == "de" [ Es gibt zwei grundlegende Kategorien der Programmierung eingebetteter Systeme: +] else if lang == "ja" [ + 2つの一般的な組込みプログラミングの分類があります。 ] else if lang == "zh" [ 通常嵌入式编程有两类: ] else { todo } = #(if lang == "en" [Hosted Environments] else if lang == "de" [Hosted Environments] + else if lang == "ja" [ホストされた環境] else if lang == "zh" [主机环境下] else { todo }) @@ -70,12 +81,19 @@ Ein-/Ausgabeschnittstellen (I/Os) können vorhanden sein. Insgesamt vermittelt die Programmierung den Eindruck, in einer auf einen speziellen Einsatzzweck ausgerichteten PC-Umgebung zu arbeiten. +] else if lang == "ja" [ + この分類の環境は、普通のPCの環境に近いです。 + これの意味するところは、#link("https://en.wikipedia.org/wiki/POSIX")[POSIX]のようなシステムインタフェースが提供されている、ということです。システムインタフェースは、ファイルシステムやネットワーク、メモリ管理、スレッドといった多様なシステムとやりとりするための基本要素を提供します。 + 通常、標準ライブラリは、その機能を実装するために、これらの基本要素に依存します。 + また、sysrootや、RAM/ROM利用の制限、そしておそらく特別なハードウェアやIOがあるかもしれません。 + 全体としては、特殊な用途のPC環境でコーディングをするようなものです。 ] else if lang == "zh" [ 这类环境与一个常见的PC环境类似。意味着向你提供了一个系统接口#link(ln_posix)[比如 POSIX],使你能和不同的系统进行交互,比如文件系统,网络,内存管理,进程,等等。标准库相应地依赖这些接口去实现了它们的功能。可能有某种sysroot并限制了对RAM/ROM的使用,可能还有一些特别的硬件或者I/O。总之感觉像是在专用的PC环境上编程一样。 ] else { todo } = #(if lang == "en" [Bare Metal Environments] else if lang == "de" [Rein physische Umgebung] + else if lang == "ja" [ベアメタル環境] else if lang == "zh" [裸机环境下] else { todo }) @@ -103,12 +121,21 @@ immer erwünscht sind. Eine dieser Funktionen ist ein Speicherallokator für die dynamische Speicherverwaltung. Falls Sie diese oder andere Funktionalitäten benötigen, stehen Ihnen häufig entsprechende Crates zur Verfügung. +] else if lang == "ja" [ + #todoupd("ja") + ベアメタル環境では、高機能なOSが動作していて、私たちのコードをホスティングしてくれる、ということはありません。 + これは、基本要素がないことを意味しており、それ故に、デフォルトでは標準ライブラリもありません。 + コードに`no_std`のマーキングをすることで、そのコードが、ベアメタル環境で実行できることを示します。 + no_stdなコードからは、Rustの#link("https://doc.rust-lang.org/std/")[libstd]とメモリの動的確保が使えません。 + しかしながら、no_stdなコードでも#ln_core;を使うことができます。libcoreは、ほんの数種類のシンボルを提供することで、いかなる環境でも容易に利用することができます + (詳細は、#ln_core;を参照して下さい)。 ] else if lang == "zh" [ 在一个裸机环境中,程序被加载前,环境中不存在代码。没有系统提供的软件,我们不能加载标准库。相反地,程序和它使用的crates只能使用硬件(裸机)去运行。使用`no-std`可以防止rust读取标准库。标准库中与平台无关的部分在#link("https://doc.rust-lang.org/core/")[libcore]中。libcore剔除了那些在一个嵌入式环境中非必要的东西。比如用于动态分配的内存分配器。如果你需要这些或者其它的某些功能,通常会有提供这些功能的crates。 ] else { todo } == #(if lang == "en" [The libstd Runtime] else if lang == "de" [Die libstd-Laufzeitumgebung] + else if lang == "ja" [libstdランタイム] else if lang == "zh" [libstd运行时] else { todo }) @@ -135,12 +162,17 @@ sowie das Starten des Haupt-Threads, bevor die `main`-Funktion des Programms aufgerufen wird. In einer `no_std`-Umgebung steht diese Laufzeitumgebung nicht zur Verfügung. +] else if lang == "ja" [ + 上述の通り、#ln_std;の利用には、いくらかのシステムインテグレーションが必要です。しかし、これは#ln_std;がOSの抽象にアクセスするための共通の方法を提供しているだけでなく、ランタイムも提供しているためです。 + ランタイムは、とりわけ、スタックオーバーフロープロテクションの準備、コマンドライン引数の処理、メインスレッドの生成、をプログラムのメイン関数が呼び出される前に処理します。 + このランタイムも、`no_std`環境では利用できません。 ] else if lang == "zh" [ - 就像之前提到的,使用#link("https://doc.rust-lang.org/std/")[libstd]需要一些系统集成,这不仅仅是因为#link("https://doc.rust-lang.org/std/")[libstd]使用了一个公共的方法访问操作系统,它也提供了一个运行时环境。这个运行时环境,负责设置堆栈溢出保护,处理命令行参数,并在一个程序的主函数被激活前启动一个主线程。在一个`no_std`环境中,这个运行时环境也是不可用的。 + 就像之前提到的,使用#ln_std;需要一些系统集成,这不仅仅是因为#ln_std;使用了一个公共的方法访问操作系统,它也提供了一个运行时环境。这个运行时环境,负责设置堆栈溢出保护,处理命令行参数,并在一个程序的主函数被激活前启动一个主线程。在一个`no_std`环境中,这个运行时环境也是不可用的。 ] else { todo } = #(if lang == "en" [Summary] else if lang == "de" [Zusammenfassung] + else if lang == "ja" [まとめ] else if lang == "zh" [总结] else { todo }) @@ -170,6 +202,12 @@ dieser Eigenschaften eignen sich `no_std`- und #ln_core;-Code für jegliche Art von Bootstrapping-Code (Stufe 0), wie etwa Bootloader, Firmware oder Kernel. +] else if lang == "ja" [ + `#![no_std]`は、クレートレベルの属性で、そのクレートがstdクレートの代わりにcoreクレートとリンクすることを意味します。 + #ln_core;クレートは、プラットフォームに依存しないstdクレートのサブセットです。libcoreクレートは、プログラムが動作するシステムについて前提を置きません。 + libcoreクレートは、浮動小数点、文字列やスライスといった言語の基本要素となるAPIと、アトミック操作やSIMD命令といったプロセッサの機能を公開するAPIとを、提供します。一方、プラットフォームインテグレーションを伴うようなAPIは欠如しています。 + これらの特性のため、no\_stdと#ln_core;のコードは、ブートローダー、ファームウェア、カーネルといったあらゆるブートストラップ + (ステージ0)のコードにも利用できます。 ] else if lang == "zh" [ `#![no_std]`是一个crate层级的属性,它说明crate将连接至core-crate而不是std-crate。#ln_core crate是std crate的一个的子集,其与平台无关,它对程序将要运行的系统没有做要求。比如,它提供了像是floats,strings和切片的APIs,暴露了像是与原子操作和SIMD指令相关的处理器功能的APIs。然而,它缺少涉及到平台集成的那些APIs。由于这些特性,no_std和#ln_core;代码可以用于任何引导程序(stage @@ -178,6 +216,7 @@ == #(if lang == "en" [Overview] else if lang == "de" [Überblick] + else if lang == "ja" [概略] else if lang == "zh" [概述] else { todo }) @@ -189,6 +228,7 @@ table.header( if lang == "en" [feature] else if lang == "de" [Eigenschaft] + else if lang == "ja" [機能] else if lang == "zh" [特性] else { todo }, [no_std], @@ -196,34 +236,41 @@ ), if lang == "en" [heap (dynamic memory)] else if lang == "de" [heap (dynamischer Speicher)] + else if lang == "ja" [ヒープ (動的メモリ)] else if lang == "zh" [堆 (dynamic memory)] else { todo }, [\*], [✓], if lang == "en" [collections (Vec, BTreeMap, etc)] else if lang == "de" [collections (Vec, BTreeMap, usw.)] + else if lang == "ja" [コレクション (Vec, HashMap, など)] else if lang == "zh" [容器 (Vec, BTreeMap, etc)] else { todo }, [\*\*], [✓], if lang == "en" [stack overflow protection] else if lang == "de" [Schutz vor Stack-Überlauf] + else if lang == "ja" [スタックオーバーフロープロテクション] else if lang == "zh" [栈溢出保护] else { todo }, [✘], [✓], if lang == "en" [runs init code before main] else if lang == "de" [führt Initialisierungscode vor der main-Funktion aus] + else if lang == "ja" [main関数前の初期化コード実行] else if lang == "zh" [在进入main之前运行的初始化代码] else { todo }, [✘], [✓], if lang in ("en", "zh") [libstd available] else if lang == "de" [libstd verfügbar] + else if lang == "ja" [libstdの利用] else { todo }, [✘], [✓], if lang in ("en", "zh") [libcore available] else if lang == "de" [libcore verfügbar] + else if lang == "ja" [libcoreの利用] else { todo }, [✓], [✓], if lang == "en" [writing firmware, kernel, or bootloader code] else if lang == "de" [schreibt firmware, kernel, oder bootloader code] + else if lang == "ja" [ファームウェア、カーネル、ブートローダーのコードを書く] else if lang == "zh" [编写固件,内核,或者引导程序] else { todo }, [✓], [✘], @@ -235,6 +282,8 @@ \* Only if you use the `alloc` crate and use a suitable allocator like #ln_cortex. ] else if lang == "de" [ \* Nur wenn Sie das `alloc`-Crate und einen geeigneten Allocator wie #ln_cortex verwenden. +] else if lang == "ja" [ + \* `alloc`クレートを使い、\[alloc-cortex-m\]のような適切なアロケータを使った場合のみ ] else if lang == "zh" [ \* 只有在你使用了 `alloc` crate 并设置了一个适合的分配器后,比如#ln_cortex;后可用. @@ -244,6 +293,8 @@ \*\* Only if you use the `collections` crate and configure a global default allocator. ] else if lang == "de" [ \*\* Nur wenn Sie das `collections`-Crate verwenden und einen globalen Standard-Allocator konfigurieren. +] else if lang == "ja" [ + \*\* `collections`クレートを使い、グローバルなデフォルトアロケータを設定した場合のみ ] else if lang == "zh" [ \*\* 只有在你使用了 `collections` crate 并配置了一个全局默认的分配器后可用. @@ -260,6 +311,7 @@ = #(if lang == "en" [See Also] else if lang == "de" [Siehe auch] + else if lang == "ja" [参照] else if lang == "zh" [参见] else { todo }) diff --git a/typ/intro/tooling.typ b/typ/intro/tooling.typ index ddc2ac60..c686e342 100644 --- a/typ/intro/tooling.typ +++ b/typ/intro/tooling.typ @@ -3,6 +3,7 @@ #h1(offset: whole, if lang == "en" [Tooling] else if lang == "de" [Werkzeuge] + else if lang == "ja" [ツール] else if lang == "zh" [工具] else { todo }) @@ -15,15 +16,16 @@ Werkzeuge, da wir es mit einer Architektur zu tun haben, die sich von der Ihres Laptops unterscheidet, und Programme auf einem _entfernten_ Gerät ausführen sowie debuggen müssen. +] else if lang == "ja" [ + マイクロコントローラを扱う際は、いくつかの異なるツールを利用することになります。あなたのノートPCとは異なるアーキテクチャを扱うことになるでしょうし、_リモート_デバイス上でプログラムを実行しデバッグする必要があります。 ] else if lang == "zh" [ 与微控制器打交道需要使用几种不同的工具,因为我们要处理的架构与笔记本电脑不同,我们必须在 _远程_ ] else { todo } -#{ -let ln_binutils = link("https://github.com/rust-embedded/cargo-binutils")[`cargo-binutils`] -let ln_qemu = link("https://www.qemu.org/")[`qemu-system-arm`] -let ln_generate = link("https://github.com/ashleygwilliams/cargo-generate")[`cargo-generate`] -if lang == "en" [ +#let ln_binutils = link("https://github.com/rust-embedded/cargo-binutils")[`cargo-binutils`] +#let ln_qemu = link("https://www.qemu.org/")[`qemu-system-arm`] +#let ln_generate = link("https://github.com/ashleygwilliams/cargo-generate")[`cargo-generate`] +#if lang == "en" [ We'll use all the tools listed below. Any recent version should work when a minimum version is not specified, but we have listed the versions we have tested. @@ -50,6 +52,16 @@ if lang == "en" [ empfohlen. Getestete Versionen: 7.10, 7.11, 7.12 und 8.1. - #ln_generate oder `git`. Diese Werkzeuge sind optional, erleichtern es jedoch, dem Buch zu folgen. +] else if lang == "ja" [ + 下記リストのツールを利用します。最小バージョンが指定されていない場合、新しいバージョンであれば機能するはずです。私たちがテストしたバージョンをリストに示しています。 + - ARM Cortex-Mコンパイルサポートを追加したRust 1.31、1.31-beta以上のツールチェイン。 + - #ln_binutils\~0.1.4 + - #ln_qemu。テストしたバージョン: 3.0.0 + - OpenOCD \>=0.8.テストしたバージョン: v0.9.0とv0.10.0 + - ARMサポートのGDB。バージョン7.12以上を強く推奨。テストしたバージョン: + 7.10、7.11、7.12、8.1 + - \[任意\] + `git`または#ln_generate。どちらもインストールしていないのであれば、どちらをインストールしてもかまいません。 ] else if lang == "zh" [ 设备上运行和调试程序。我们将使用下面列举出来的工具。当没有指定一个最小版本时,最新的版本应该也可以用,但是我们还是列出了我们已经测过的那些版本。 - Rust 1.31, 1.31-beta, 或者一个更新的,支持ARM Cortex-M编译的工具链。 @@ -60,7 +72,6 @@ if lang == "en" [ - #ln_generate 或者 `git`。 这些工具都是可选的,但是跟着这本书来使用它们,会更容易。 ] else { todo } -} #if lang == "en" [ The text below explains why we are using these tools. Installation @@ -68,12 +79,15 @@ if lang == "en" [ ] else if lang == "de" [ Der folgende Text erläutert, warum wir diese Werkzeuge verwenden. Installationsanweisungen finden Sie auf der nächsten Seite. +] else if lang == "ja" [ + 下記に、なぜこれらのツールを利用するのか、を説明します。インストール方法は、次のページにあります。 ] else if lang == "zh" [ 下面的文档将解释我们为什么使用这些工具。安装指令可以在下一页找到。 ] else { todo } = #(if lang == "en" [`cargo-generate` OR `git`] else if lang == "de" [`cargo-generate` ODER `git`] + else if lang == "ja" [`cargo-generate`か`git`] else if lang == "zh" [`cargo-generate` 或者 `git`] else { todo }) @@ -93,6 +107,11 @@ if lang == "en" [ erforderlich. Wir haben diese Elemente in einer Vorlage zusammengefasst, sodass Sie lediglich die noch fehlenden Angaben ergänzen müssen (etwa den Projektnamen und die Spezifikationen Ihrer Zielhardware). +] else if lang == "ja" [ + ベアメタルプログラムは、非標準 + (`no_std`)なRustプログラムであり、プログラムのメモリレイアウトを正しくするために、リンクプロセスをいじる必要があります。 + これには、独特なファイル(リンカスクリプトなど)と設定(リンカフラグ)が必要です。 + プロジェクト名やターゲットハードウェアの特徴などを、空白に入力するだけで済むように、テンプレートを用意しています。 ] else if lang == "zh" [ 裸机编程是非标准Rust编程,为了得到正确的程序的内存布局,需要对链接过程进行一些调整,这要求添加一些额外的文件(比如linker scripts)和配置(比如linker @@ -108,6 +127,9 @@ if lang == "en" [ Cargo-Unterbefehl zum Erstellen neuer Cargo-Projekte auf Basis von Vorlagen. Alternativ können Sie die Vorlage auch mithilfe von `git`, `curl`, `wget` oder Ihrem Webbrowser herunterladen. +] else if lang == "ja" [ + このテンプレートは、`cargo-generate`との互換性があります。`cargo-generate`は、テンプレートからCargoプロジェクトを作成するためのCargoのサブコマンドです。 + このテンプレートは、`git`や`curl`、`wget`、ウェブブラウザを使ってダウンロードできます。 ] else if lang == "zh" [ 我们的模板兼容`cargo-generate`:一个用来从模板生成新的Cargo项目的Cargo子命令。你也能使用`git`,`curl`,`wget`,或者你的网页浏览器下载模板。 ] else { todo } @@ -124,6 +146,9 @@ if lang == "en" [ Verwendung der mit der Rust-Toolchain ausgelieferten LLVM-Werkzeuge vereinfachen. Zu diesen Werkzeugen gehören die LLVM-Varianten von `objdump`, `nm` und `size`, die zur Untersuchung von Binärdateien dienen. +] else if lang == "ja" [ + `cargo-binutils`は、Rustツールチェインとともに配布されているLLVMツールを簡単に使うためのCargoサブコマンド一式です。 + これらのツールは、LLVMの`objdump`や`nm`、`size`を含んでおり、バイナリを調査するために使われます。 ] else if lang == "zh" [ `cargo-binutils`是一个Cargo命令的子集,它让我们能轻松使用Rust工具链带来的LLVM工具。这些工具包括LLVM版本的`objdump`,`nm`和`size`,用来查看二进制文件。 在GNU @@ -144,6 +169,11 @@ if lang == "en" [ (#cmd) erfolgt und (b) Werkzeuge wie `objdump` alle von `rustc` unterstützten Architekturen abdecken -- von ARM bis x86_64 --, da sie auf demselben LLVM-Backend basieren. +] else if lang == "ja" [ + GNU binutilsではなく、これらのツールを利用する利点は次の通りです。 (a) + LLVMツールのインストールは、OSに関わらず、共通のコマンド1つ(cmd)で済みます。 + (b) + `objdump`などのツールは、`rustc`がサポートする全てのアーキテクチャ(ARMからx86\_64まで)をサポートします。これは、同じLLVMバックエンドを共有しているためです。 ] else if lang == "zh" [ binutils之上使用这些工具的好处是,(a)无论你的操作系统是什么,安装这些LLVM工具都可以用同一条命令(#cmd)。(b)像是`objdump`这样的工具,支持所有`rustc`支持的架构--从ARM到x86\_64--因为它们都有一样的LLVM后端。 ] else { todo } @@ -162,6 +192,10 @@ if lang == "en" [ Embedded-Programme auf dem Host-System auszuführen. Auf diese Weise können Sie einige Teile dieses Buches auch dann nachvollziehen, wenn Sie keine Hardware zur Hand haben! +] else if lang == "ja" [ + QEMUはエミュレータです。今回は、ARMシステムを完全にエミュレートできるものを使います。 + 私たちは、ホスト上で組込みプログラムを実行するためにQEMUを利用します。 + このおかげで、ハードウェアを持っていなくても、この本のいくつかの部分を試すことができます。 ] else if lang == "zh" [ QEMU是一个仿真器。在这个例子里,我们使用能完全仿真ARM系统的改良版QEMU。我们使用QEMU在主机上运行嵌入式程序。多亏了它,你可以在没有任何硬件的情况下,尝试这本书的部分示例。 ] else { todo } diff --git a/typ/start/exceptions.typ b/typ/start/exceptions.typ index 56ce9cee..f2dc0cac 100644 --- a/typ/start/exceptions.typ +++ b/typ/start/exceptions.typ @@ -3,6 +3,7 @@ #h1(offset: whole, if lang == "en" [Exceptions] else if lang == "de" [Ausnahmen (Exceptions)] + else if lang == "ja" [例外] else if lang == "zh" [异常] else { todo }) @@ -19,6 +20,9 @@ Ausführung einer ungültigen Anweisung) behandelt. Ausnahmen implizieren Präemption und erfordern Ausnahmebehandlungsroutinen, die als Reaktion auf das auslösende Signal ausgeführt werden. +] else if lang == "ja" [ + 例外と割り込みは、プロセッサが非同期イベントと致命的なエラー(例えば、不正な命令の実行)を扱うためのハードウェアの仕組みです。 + 例外はプリエンプションを意味し、例外ハンドラを呼び出します。例外ハンドラは、イベントを引き起こした信号に応答して実行されるサブルーチンです。 ] else if lang == "zh" [ 异常和中断,是处理器用来处理异步事件和致命错误(e.g.~执行一个无效的指令)的一种硬件机制。异常意味着抢占并涉及到异常处理程序,即响应触发事件的信号的子程序。 ] else { todo } @@ -28,6 +32,8 @@ The `cortex-m-rt` crate provides an #ln_ex attribute to declare exception handlers. ] else if lang == "de" [ Die `cortex-m-rt`-Crate bietet das Attribut #ln_ex zur Deklaration von Ausnahmebehandlungsroutinen. +] else if lang == "ja" [ + `cortex-m-rt`クレートは、例外ハンドラを宣言するために、#ln_ex;アトリビュートを提供しています。 ] else if lang == "zh" [ `cortex-m-rt` crate提供了一个 #ln_ex 属性去声明异常处理程序。 ] else { todo } @@ -37,6 +43,8 @@ "Exception handler for the SysTick (System Timer) exception" } else if lang == "de" { "Ausnahmebehandlungsroutine fuer die SysTick-Ausnahme (System-Timer)" + } else if lang == "ja" { + "SysTick(システムタイマ)例外のための例外ハンドラ" } else if lang == "zh" { "SysTick (System计时器)异常的异常处理函数" } else { todos } + " @@ -57,6 +65,10 @@ fn SysTick() { Unterschied: `exception`-Handler können _nicht_ per Software aufgerufen werden. Bezogen auf das vorherige Beispiel würde die Anweisung `SysTick();` zu einem Kompilierfehler führen. +] else if lang == "ja" [ + `exception`属性の他は、例外ハンドラは普通の関数のように見えます。しかし、もう1つ違いがあります。 + `exception`ハンドラはソフトウェアから呼び出すことが_できません_。前述の例では、`SysTick();`というステートメントは、 + コンパイルエラーになります。 ] else if lang == "zh" [ 除了 `exception` 属性,异常处理函数看起来和普通函数一样,但是有一个很大的不同: @@ -73,6 +85,9 @@ fn SysTick() { bestimmte Eigenschaft zu gewährleisten: `static mut`-Variablen, die _innerhalb_ von `exception`-Handlern deklariert werden, sind _sicher_ in der Verwendung. +] else if lang == "ja" [ + この動作は、非常に意図的なものです。 + これは`exception`ハンドラ_内_で宣言された`static mut`変数の利用を_安全_にする、という機能を提供するためのものです。 ] else if lang == "zh" [ 这么做是有目的的,因为异常处理函数必须具有一个特性: 在异常处理函数中被声明为`static mut`的变量能被安全(safe)地使用。 @@ -87,6 +102,8 @@ fn SysTick() { } else if lang == "de" { "`COUNT` wurde in den Typ `&mut u32` umgewandelt und ist sicher in der // Verwendung." + } else if lang == "ja" { + "`COUNT`は`&mut u32`の型をもっており、その利用は安全です" } else if lang == "zh" { "`COUNT` 被转换到了 `&mut u32` 类型且它用起来是安全的" } else { todos } + " @@ -108,6 +125,10 @@ fn SysTick() { wiedereintrittsfähige Funktion direkt oder indirekt aus mehr als einem Ausnahme- oder Interrupt-Handler oder aus `main` und einem oder mehreren Ausnahme- oder Interrupt-Handlern heraus aufzurufen. +] else if lang == "ja" [ + ご存知かもしれませんが、`static mut`変数を関数内で使うことは、その関数を_再入不可能_にします。 + 直接的または間接的に、複数の例外・割り込みハンドラから、もしくは、`main`と1つ以上の例外・割り込みハンドラから、 + 再進入不可能な関数を呼び出すことは、未定義動作です。 ] else if lang == "zh" [ 就像你可能已经知道的那样,在一个函数里使用`static mut`变量,会让函数变成#link(url_re)[_非可重入函数(non-reentrancy)_]。从多个异常/中断处理函数,或者从`main`函数和多个异常/中断处理函数中,直接或者间接地调用一个非可重入(non-reentrancy)函数是未定义的行为。 ] else { todo } @@ -129,6 +150,11 @@ fn SysTick() { (Reentrancy) stattfinden kann. Diese Handler werden von der Hardware selbst aufgerufen, bei der davon ausgegangen wird, dass sie physisch keine Nebenläufigkeit aufweist. +] else if lang == "ja" [ + #todoupd("ja") + 安全なRustは、決して未定義動作になりません。そのため、再入不可能な関数は、`unsafe`とマークされなければなりません。 + それでも、`exception`ハンドラは`static mut`な変数を安全に使える、と述べました。これが可能なのは、どうしてでしょうか。 + `exception`ハンドラはソフトウェアから呼び出すことが_できない_ため、再入する可能性はありません。だから、安全に使えるのです。 ] else if lang == "zh" [ #todoupd("zh") 安全的Rust不能导致未定义的行为出现,所以非可重入函数必须被标记为 @@ -199,6 +225,7 @@ fn SysTick() { = #(if lang == "en" [A complete example] else if lang == "de" [Ein vollständiges Beispiel] + else if lang == "ja" [完全な例] else if lang == "zh" [一个完整的例子] else { todo }) @@ -214,6 +241,10 @@ fn SysTick() { `SysTick`-Exception-Handler protokolliert in der Variablen `COUNT`, wie oft er aufgerufen wurde, und gibt anschließend den Wert von `COUNT` mittels Semihosting auf der Host-Konsole aus. +] else if lang == "ja" [ + `SysTick`例外を大体1秒毎に発生させるシステムタイマの例を使います。 + `SysTick`例外ハンドラは、呼び出された回数を`COUNT`変数に記録し、 + セミホスティングを使ってホストコンソールに`COUNT`の値を出力します。 ] else if lang == "zh" [ 这里有个例子,使用系统计时器大概每秒抛出一个 `SysTick` 异常。异常处理函数使用 `COUNT` @@ -228,6 +259,8 @@ fn SysTick() { ] else if lang == "de" [ *HINWEIS*: Sie können dieses Beispiel auf jedem Cortex-M-Gerät ausführen; es lässt sich auch unter QEMU ausführen. +] else if lang == "ja" [ + *注記*:この例は、どのCortex-Mデバイスでも実行できます。QEMU上でも実行可能です。 ] else if lang == "zh" [ *注意*: 你能在任何Cortex-M设备上运行这个例子;你也能在QEMU运行它。 ] else { todo } @@ -259,6 +292,8 @@ fn main() -> ! { } else if lang == "de" { "konfiguriert den System-Timer so, dass er jede Sekunde eine SysTick- // Exception ausloest" + } else if lang == "ja" { + "毎秒SysTick例外を起こすためのシステムタイマを設定します" } else if lang == "zh" { "配置系统的计时器每秒去触发一个SysTick异常" } else { todos } + " @@ -268,6 +303,8 @@ fn main() -> ! { } else if lang == "de" { "Dies ist für den LM3S6965 konfiguriert, der einen Standard-CPU-Takt von // 12 MHz hat" + } else if lang == "ja" { + "デフォルトのCPUクロックが12MHzのLM3S6965向けの設定です" } else if lang == "zh" { "这是关于LM3S6965的配置,其有一个12MHz的默认CPU时钟" } else { todos } + " @@ -290,6 +327,8 @@ fn SysTick() { "Lazy initialization" } else if lang == "de" { "Verzoegerte Initialisierung" + } else if lang == "ja" { + "遅延初期化" } else if lang == "zh" { "惰性初始化(Lazy initialization)" } else { todos } + " @@ -308,6 +347,9 @@ fn SysTick() { "WICHTIG: Lassen Sie diesen `if`-Block weg, wenn Sie das Programm auf // echter Hardware ausfuehren, da sich Ihr Debugger sonst in // einem inkonsistenten Zustand befinden wird." + } else if lang == "ja" { + "重要。実際のハードウェアで実行するときは`if`ブロックを削除して下さい。そうでなければ、 + // デバッガが不整合な状態に陥るでしょう。" } else if lang == "zh" { "重要信息 如果运行在真正的硬件上,去掉这个 `if` 块, // 否则你的调试器将会以一种不一致的状态结束" @@ -317,6 +359,8 @@ fn SysTick() { "This will terminate the QEMU process" } else if lang == "de" { "Dies beendet den QEMU-Prozess." + } else if lang == "ja" { + "QEMUプロセスを終了します" } else if lang == "zh" { "这将终结QEMU进程" } else { todos } + " @@ -351,12 +395,16 @@ $ cargo run --release Wenn Sie dies auf dem Discovery-Board ausführen, sehen Sie die Ausgabe in der OpenOCD-Konsole. Außerdem hält das Programm _nicht_ an, wenn der Zählerstand 9 erreicht. +] else if lang == "ja" [ + Discoveryボードでこのコードを実行すると、OpenOCDコンソールに出力を確認できるでしょう。 + プログラムは、カウントが9に到達しても停止_しません_。 ] else if lang == "zh" [ 如果你在Discovery开发板上运行这个例子,你将会在OpenOCD控制台上看到输出。还有,当计数到达9的时候,程序将 _会_ 停止。 ] else { todo } = #(if lang == "en" [The default exception handler] else if lang == "de" [Der Standard-Exception-Handler] + else if lang == "ja" [デフォルト例外ハンドラ] else if lang == "zh" [默认异常处理函数] else { todo }) @@ -371,6 +419,10 @@ $ cargo run --release _überschrieben_ wird. Wenn Sie den Handler für eine bestimmte Exception nicht überschreiben, wird sie von der Funktion `DefaultHandler` behandelt, die standardmäßig Folgendes tut: +] else if lang == "ja" [ + `exception`アトリビュートが実際に行っていることは、特定の例外を処理するデフォルト例外ハンドラの_オーバーライド_です。 + 特定の例外について、ハンドラをオーバーライドしない場合、`DefaultHandler`関数がその例外を処理します。 + DefaultHandler関数は下記の通りです。 ] else if lang == "zh" [ `exception` 属性真正做的是,_覆盖_ 了一个特定异常的默认异常处理函数。如果你不覆盖一个特定异常的处理函数,它将会被 @@ -392,6 +444,9 @@ fn DefaultHandler() { `#[no_mangle]` markiert, sodass Sie einen Breakpoint auf „DefaultHandler" setzen und _unbehandelte_ Exceptions abfangen können. +] else if lang == "ja" [ + この関数は、`cortex-m-rt`クレートによって提供されており、`#[no_mangle]`とマークされています。 + そのため、「DefaultHandler」にブレイクポイントを設定することができ、_未処理の_例外を捕捉することができます。 ] else if lang == "zh" [ 这个函数是 `cortex-m-rt` crate提供的,且被标记为 `#[no_mangle]` 因此你能在 "DefaultHandler" 上放置一个断点并捕获 _unhandled_ 异常。 @@ -403,6 +458,8 @@ fn DefaultHandler() { ] else if lang == "de" [ Es ist möglich, diesen `DefaultHandler` mithilfe des `exception`-Attributs zu überschreiben: +] else if lang == "ja" [ + `exception`アトリビュートを使うことで、`DefaultHandler`をオーバーライドできます。 ] else if lang == "zh" [ 可以使用 `exception` 属性覆盖这个 `DefaultHandler`: ] else { todo } @@ -414,6 +471,8 @@ fn DefaultHandler(irqn: i16) { "custom default handler" } else if lang == "de" { "benutzerdefinierter Standard-Handler" + } else if lang == "ja" { + "カスタムデフォルトハンドラ" } else if lang == "zh" { "自定义默认处理函数" } else { todos } + " @@ -431,12 +490,16 @@ fn DefaultHandler(irqn: i16) { wird, während ein Wert von null oder ein positiver Wert darauf hinweist, dass eine gerätespezifische Exception -- auch als Interrupt bezeichnet -- bearbeitet wird. +] else if lang == "ja" [ + `irqn`引数は、どの例外が処理されているかを示します。負の値は、Cortex-Mの例外が処理されていることを意味します。 + ゼロまたは正の値は、デバイス固有の例外、すなわち、割り込みが処理されていること、を示しています。 ] else if lang == "zh" [ `irqn` 参数指出了被服务的是哪个异常。一个负数值指出了被服务的是一个Cortex-M异常;0或者一个正数值指出了被服务的是一个设备特定的异常,也就是中断。 ] else { todo } = #(if lang == "en" [The hard fault handler] else if lang == "de" [Der "Schwere Fehler"-Handler] + else if lang == "ja" [ハードフォールトハンドラ] else if lang == "zh" [硬错误(Hard Fault)处理函数] else { todo }) @@ -453,6 +516,10 @@ fn DefaultHandler(irqn: i16) { führen könnte. Zudem führt die Runtime-Crate einige Vorarbeiten durch, bevor der benutzerdefinierte `HardFault`-Handler aufgerufen wird, um die Fehlersuche (Debugging) zu erleichtern. +] else if lang == "ja" [ + `HardFault`例外は、少し特別です。この例外は、プログラムが不正な状態になった場合に発生します。 + そのため、このハンドラはリターンすることができず、未定義動作を引き起こす可能性があります。 + ランタイムクレートは、デバッグ性を向上するために、ユーザ定義の`HardFault`ハンドラが呼び出される前に、少し仕事をします。 ] else if lang == "zh" [ `HardFault`异常有点特别。当程序进入一个无法工作的状态时,这个异常被触发,因此它的处理函数 _不能_ 返回,因为这么做可能导致一个未定义的行为。在用户定义的 @@ -473,6 +540,10 @@ fn DefaultHandler(irqn: i16) { Stack gesichert wurden. Diese Register stellen eine Momentaufnahme des Prozessorzustands zum Zeitpunkt der Auslösung der Exception dar und sind für die Diagnose eines Hard Faults hilfreich. +] else if lang == "ja" [ + その結果、`HardFault`ハンドラは、`fn(&ExceptionFrame) -> !`のシグネチャを持つ必要があります。 + ハンドラの引数は、例外によってスタックにプッシュされたレジスタへのポインタです。 + これらのレジスタは、例外が発生した瞬間のプロセッサステートのスナップショットで、ハードフォールトの原因を突き止めるのに便利です。 ] else if lang == "zh" [ 结果是,`HardFault`处理函数必须有下列的签名: `fn(&ExceptionFrame) -> !` 。处理函数的参数是一个指针,它指向被异常推入栈中的寄存器。这些寄存器是异常被触发那刻,处理器状态的一个记录,能被用来分析一个硬错误。 @@ -484,6 +555,8 @@ fn DefaultHandler(irqn: i16) { ] else if lang == "de" [ Hier ist ein Beispiel für eine unzulässige Operation: der Lesezugriff auf eine nicht existierende Speicheradresse. +] else if lang == "ja" [ + 不正な操作を行う例を示します。存在しないメモリ位置への読み込みです。 ] else if lang == "zh" [ 这里有个执行不合法操作的案例: 读取一个不存在的存储位置。 ] else { todo } @@ -498,6 +571,10 @@ fn DefaultHandler(irqn: i16) { (d.~h. es stürzt nicht ab), da `qemu-system-arm -machine lm3s6965evb` keine Überprüfung von Speicherzugriffen durchführt und beim Lesen von ungültigem Speicher problemlos den Wert `0` zurückgibt. +] else if lang == "ja" [ + *注記*:このプログラムは、QEMU上ではうまく動きません。つまり、クラッシュしません。 + `qemu-system-arm -machine lm3s6965evb`はメモリの読み込みをチェックしないため、 + 無効なメモリを読み込むと、幸いにも、`0`を返します。 ] else if lang == "zh" [ *注意*: 这个程序在QEMU上不能起作用,i.e.~它不会崩溃,因为 `qemu-system-arm -machine lm3s6965evb` @@ -524,6 +601,8 @@ fn main() -> ! { "read a nonexistent memory location" } else if lang == "de" { "liest einen nicht existierenden Speicherbereich" + } else if lang == "ja" { + "存在しないメモリ位置を読み込みます" } else if lang == "zh" { "读取一个无效的存储位置" } else { todos } + " @@ -550,6 +629,9 @@ fn HardFault(ef: &ExceptionFrame) -> ! { ] else if lang == "de" [ Der `HardFault`-Handler gibt den Wert des `ExceptionFrame` aus. Wenn Sie dies ausführen, sehen Sie auf der OpenOCD-Konsole eine Ausgabe wie diese. +] else if lang == "ja" [ + `HardFault`ハンドラは、`ExceptionFrame`の値を表示します。実行すると、 + OpenOCDコンソールに次のような表示が見えるでしょう。 ] else if lang == "zh" [ `HardFault`处理函数打印了`ExceptionFrame`值。如果你运行这个,你将会看到下面的东西打印到OpenOCD控制台上。 ] else { todo } @@ -575,6 +657,8 @@ ExceptionFrame { ] else if lang == "de" [ Der Wert `pc` ist der Wert des Programmzählers zum Zeitpunkt der Ausnahme und verweist auf die Anweisung, die die Ausnahme ausgelöst hat. +] else if lang == "ja" [ + `pc`の値は、例外発生時のプログラムカウンタの値で、例外を引き起こした命令を指しています。 ] else if lang == "zh" [ `pc`值是异常时程序计数器(Program Counter)的值,它指向触发了异常的指令。 ] else { todo } @@ -583,6 +667,8 @@ ExceptionFrame { If you look at the disassembly of the program: ] else if lang == "de" [ Wenn Sie sich den Disassembler des Programms ansehen: +] else if lang == "ja" [ + プログラムのディスアセンブル結果を見ます。 ] else if lang == "zh" [ 如果你看向程序的反汇编: ] else { todo } @@ -611,6 +697,10 @@ ResetTrampoline: Das Feld `r0` von `ExceptionFrame` gibt an, dass der Wert des Registers `r0` zu diesem Zeitpunkt `0x3fff_fffe` war. +] else if lang == "ja" [ + #todoupd("ja") + ロード命令(`ldr r0, [r0]`)が例外を発生させたことがわかります。そして、この時の`r0`レジスタの値は、 + `0x3fff_fffe`です。この値は、`ExceptionFrame`の`r0`フィールドと一致します。 ] else if lang == "zh" [ 你可以在反汇编中搜索程序计数器`0x0800094a`的值。你将会看到一个读取操作(`ldr r0, [r0]`)导致了异常。`ExceptionFrame`的`r0`字段将告诉你,那时寄存器`r0`的值是`0x3fff_fffe` 。 diff --git a/typ/start/hardware.typ b/typ/start/hardware.typ index 2971a339..ab861bfa 100644 --- a/typ/start/hardware.typ +++ b/typ/start/hardware.typ @@ -2,6 +2,7 @@ #h1(offset: whole, if lang in ("en", "de") [Hardware] + else if lang == "ja" [ハードウェア] else if lang == "zh" [硬件] else { todo }) @@ -14,12 +15,16 @@ Mittlerweile sollten Sie mit den Werkzeugen und dem Entwicklungsprozess einigermaßen vertraut sein. In diesem Abschnitt wechseln wir zur echten Hardware; der Prozess bleibt im Großen und Ganzen gleich. Tauchen wir ein. +] else if lang == "ja" [ + ここまでで、ツールと開発プロセスにある程度慣れたはずです。このセクションでは、実際のハードウェアに切り替えます。 + 開発プロセスは、ほとんど同じままです。飛び込みましょう。 ] else if lang == "zh" [ 现在你应该有点熟悉工具和开发过程了。在这部分我们将切换到真正的硬件上;步骤非常相似。让我们深入下去。 ] else { todo } = #(if lang == "en" [Know your hardware] else if lang == "de" [Kennen Sie Ihre Hardware] + else if lang == "ja" [ハードウェアを知る] else if lang == "zh" [认识你的硬件] else { todo }) @@ -43,6 +48,14 @@ Flash und 32 KiB RAM. - Wo werden Flash-Speicher und RAM im Adressraum abgebildet? zB RAM ist befindet sich üblicherweise an der Adresse `0x2000_0000`. +] else if lang == "ja" [ + 始める前に、プロジェクトの設定に利用するターゲットデバイスのいくつかの特徴を確認する必要があります。 + - ARMコア、例えばCortex-M3です。 + - そのARMコアはFPUを搭載していますか?Cortex-M4#[*F*]とCortex-M7#[*F*]は、搭載しています。 + - ターゲットデバイスに搭載されているフラッシュメモリとRAMの容量はいくらですか? + 例えば、フラッシュは256KiBでRAMは32KiBです。 + - フラッシュメモリとRAMは、アドレス空間のどこにマッピングされていますか? + 例えば、RAMは、通常`0x2000_0000`番地に位置します。 ] else if lang == "zh" [ 在我们开始之前,你需要了解下你的目标设备的一些特性,因为你将用它们来配置项目: - ARM 内核。比如 Cortex-M3 。 @@ -57,6 +70,8 @@ ] else if lang == "de" [ Diese Informationen finden Sie im Datenblatt oder im Referenzhandbuch Ihres/Ihrer Microcontrollers/Microcontrollerplatine. +] else if lang == "ja" [ + これらの情報は、デバイスのデータシートかリファレンスマニュアルに掲載されています。 ] else if lang == "zh" [ 你可以在你的设备的数据手册和参考手册上找到这些信息。 ] else { todo } @@ -77,6 +92,12 @@ - 256 KiB Flash befinden sich an der Adresse 0x0800_0000. - 440 KiB RAM an der Adresse 0x2000_0000. (Es gibt noch eine weitere RAM-Region, aber der Einfachheit halber ignorieren wir das). +] else if lang == "ja" [ + このセクションでは、私たちのリファレンスハードウェアであるSTM32F3DISCOVERYを使用します。 + このボードは、STM32F303VCT6マイクロコントローラを1つ搭載しています。このマイクロコントローラは以下のものを持っています。 + - 単精度FPUを含むCortex-M4Fコアが1つ + - 0x0800_0000番地に配置された256KiBのフラッシュメモリ + - 0x2000_0000番地に配置された40KiBのRAM。(別のRAM領域もありますが、説明の簡単化のため、取り扱いません) ] else if lang == "zh" [ 这部分,要使用我们的参考硬件,STM32F3DISCOVERY。这个板子包含一个STM32F303VCT6微控制器。这个微控制器拥有: - 一个Cortex-M4F核心,它包含一个单精度FPU。 @@ -86,6 +107,7 @@ = #(if lang == "en" [Configuring] else if lang == "de" [Konfigurieren] + else if lang == "ja" [設定] else if lang == "zh" [配置] else { todo }) @@ -95,6 +117,9 @@ ] else if lang == "de" [ Wir beginnen bei Null mit einer neuen Vorlageninstanz. Zur Auffrischung siehe #link()[vorheriger Abschnitt zu QEMU], wie man das ohne `cargo-generate` macht. +] else if lang == "ja" [ + テンプレートの新しいインスタンスを使って、スクラッチから書いていきましょう。 + `cargo-generate`を使用しない方法については、#link()[前セクションのQEMU]を参照して下さい。 ] else if lang == "zh" [ 我们将使用一个新的模板实例从零开始。对于新手,请参考#link()[先前的QEMU]章节,了解如何在没有`cargo-generate`的情况下完成配置。 ] else { todo } @@ -113,6 +138,8 @@ $ cd app ] else if lang == "de" [ Schritt Nummer eins besteht darin, ein Standardkompilierungsziel in `.cargo/config.toml` festzulegen. +] else if lang == "ja" [ + 第一ステップは、`.cargo/config`にデフォルトコンパイルターゲットを設定することです。 ] else if lang == "zh" [ 第一步是在`.cargo/config.toml`中设置一个默认编译目标。 ] else { todo } @@ -137,12 +164,23 @@ tail -n5 .cargo/config.toml # target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU) target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) ``` +] else if lang == "ja" [ + ```toml + [build] + # 以下のコンパイルターゲットから1つを選びます + # target = "thumbv6m-none-eabi" # Cortex-M0およびCortex-M0+ + # target = "thumbv7m-none-eabi" # Cortex-M3 + # target = "thumbv7em-none-eabi" # Cortex-M4およびCortex-M7 (no FPU) + target = "thumbv7em-none-eabihf" # Cortex-M4FおよびCortex-M7F (with FPU) + ``` ] else { todo } #if lang == "en" [ We'll use `thumbv7em-none-eabihf` as that covers the Cortex-M4F core. ] else if lang == "de" [ Wir verwenden `thumbv7em-none-eabihf`, da es den Cortex-M4F-Kern abdeckt. +] else if lang == "ja" [ + Cortex-M4Fコアを対象とするものとして、`thumbv7em-none-eabihf`を使います。 ] else if lang == "zh" [ 我们将使用 `thumbv7em-none-eabihf`,因为它包括了Cortex-M4F内核. ] else { todo } @@ -169,6 +207,8 @@ tail -n5 .cargo/config.toml The second step is to enter the memory region information into the `memory.x` file. ] else if lang == "de" [ Der zweite Schritt besteht darin, die Speicherbereichsinformationen in die Datei `memory.x` einzugeben +] else if lang == "ja" [ + 第二ステップは、`memory.x`ファイルにメモリ領域の情報を入力することです。 ] else if lang == "zh" [ 第二步是将存储区域信息(memory region information)输入`memory.x`。 ] else { todo } @@ -216,6 +256,9 @@ MEMORY ] else if lang == "de" [ Stellen Sie in `examples/hello.rs`sicher, daß `debug::exit()` auskommentiert oder entfernt wurde. Es wird nur zum Ausführen in QEMU verwendet. +] else if lang == "ja" [ + `debug::exit()`の呼び出しが、コメントアウトされているか削除されていることを確認して下さい。 + これは、QEMUで実行する時のみ、使用します。 ] else if lang == "zh" [ 在`examples/hello.rs`中,确保`debug::exit()`调用被注释掉了或者移除掉了。它只能用于在QEMU中运行的情况。 ] else { todo } @@ -233,6 +276,10 @@ fn main() -> ! { "exit QEMU // HINWEIS: Fuehren Sie dies nicht auf der Hardware aus; es kann den // OpenOCD-Zustand beschaedigen. + // debug::exit(debug::EXIT_SUCCESS);" + } else if lang == "ja" { + "QEMUを終了する + // 注記、ハードウェア上で実行しないで下さい。OpenOCDの状態を破壊する可能性があります。 // debug::exit(debug::EXIT_SUCCESS);" } else if lang == "zh" { "退出 QEMU @@ -254,6 +301,11 @@ fn main() -> ! { `cortex-m-rt`-Crate übernimmt die gesamte für den Betrieb des Chips erforderliche „Magie", da erfreulicherweise so gut wie alle Cortex-M-CPUs auf die gleiche Weise booten. +] else if lang == "ja" [ + これまでやってきた通り、`cargo build`でプログラムをクロスコンパイルし、 + `cargo-binutils`でバイナリを調べることができます。 + `cortex-m-rt`クレートは、チップを動作させるために必要な、全てのおまじないを処理します。 + 便利なことに、ほとんど全てのCortex-M CPUが同じ方法で起動します。 ] else if lang == "zh" [ 你可以像你之前做的一样,使用`cargo build`检查编译程序,使用`cargo-binutils`观察二进制项。`cortex-m-rt`库可以处理所有让芯片运行起来所需的魔法,几乎所有的Cortex-M CPUs都按同样的方式启动。 @@ -265,6 +317,7 @@ cargo build --example hello = #(if lang == "en" [Debugging] else if lang == "de" [Fehlerbehebung (Debugging)] + else if lang == "ja" [デバッグ] else if lang == "zh" [调试] else { todo }) @@ -282,6 +335,11 @@ cargo build --example hello STM32F3DISCOVERY laufenden Programms erforderlich sind. Dies soll als Referenz dienen; für gerätespezifische Informationen zum Debugging konsultieren Sie bitte #link(url_dn)[das Debugonomicon]. +] else if lang == "ja" [ + デバッグ方法は少し違います。実際、最初のステップは、ターゲットデバイスによって異なります。 + このセクションでは、STM32F3DISCOVERY上で実行しているプログラムをデバッグするために必要となる手順を説明します。 + これは、参考の役目を果たします。デバイス固有のデバッグ情報は、 + #link(url_dn)[the Debugonomicon]を参照して下さい。 ] else if lang == "zh" [ 调试会看起来有点不一样。事实上,取决于不同的目标设备,第一步可能看起来不一样。在这个章节里,我们将展示,调试一个在STM32F3DISCOVERY上运行的程序,所需要的步骤。这作为一个参考。关于调试有关的设备特定的信息,可以看#link(url_dn)[the Debugonomicon]。 ] else { todo } @@ -292,6 +350,9 @@ cargo build --example hello ] else if lang == "de" [ Wie zuvor führen wir Remote-Debugging durch, wobei der Client ein GDB-Prozess ist. Diesmal ist der Server jedoch OpenOCD. +] else if lang == "ja" [ + 以前と同様に、リモートデバッグを行います。クライアントがGDBプロセスであることも同様です。 + しかし、今回、サーバはOpenOCDになります。 ] else if lang == "zh" [ 像之前一样,我们将进行远程调试,客户端将是一个GDB进程。不同的是,OpenOCD将是服务器。 ] else { todo } @@ -305,6 +366,9 @@ cargo build --example hello "#link()[Die Installation überprüfen]" beschrieben, das Discovery Board mit Ihrem Laptop/PC und prüfen Sie, ob der ST-LINK-Header ausgefüllt ist. +] else if lang == "ja" [ + #link()[インストールの確認]セクションでやったように、ノートPCまたはPCをdiscoveryボードに接続し、 + ST-LINKヘッダが設定されていることを確認して下さい。 ] else if lang == "zh" [ 像是在#link()[安装验证]中做的那样,把你的笔记本/个人电脑和discovery开发板连接起来,检查ST-LINK的短路帽是否被安装了。 ] else { todo } @@ -320,6 +384,10 @@ cargo build --example hello im Stammverzeichnis der Vorlage aus; `openocd` greift dabei auf die Datei `openocd.cfg` zu, in der festgelegt ist, welche Schnittstellen- und Zieldateien verwendet werden sollen. +] else if lang == "ja" [ + discoveryボードのST-LINKに接続するために、端末で`openocd`を実行して下さい。 + このコマンドは、テンプレートプロジェクトのルートディレクトリから実行して下さい。 + `openocd`は、どのインタフェースファイルとターゲットファイルを使うか、が記述されている`openocd.cfg`ファイルを見つけます。 ] else if lang == "zh" [ 在一个终端上运行 `openocd` 连接到你的开发板上的 ST-LINK 。从模板的根目录运行这个命令;`openocd` 将会选择 `openocd.cfg` @@ -359,6 +427,21 @@ cat openocd.cfg # Revision A and B (aeltere Revisionen) # source [find interface/stlink-v2.cfg] + source [find target/stm32f3x.cfg] + ``` +] else if lang == "ja" [ + ```text + # STM32F3DISCOVERY開発ボード用のOpenOCD設定サンプル + + # 持っているハードウェアのリビジョンに応じて、これらのインタフェースのうち、1つを選んで下さい。 + # 常に、1つのインタフェースがコメントアウトされているべきです。 + + # リビジョンC (新しいリビジョン) + source [find interface/stlink-v2-1.cfg] + + # リビジョンAとB(古いリビジョン) + # source [find interface/stlink-v2.cfg] + source [find target/stm32f3x.cfg] ``` ] else { todo } @@ -375,6 +458,10 @@ cat openocd.cfg feststellen, dass Sie eine ältere Revision des Discovery-Boards besitzen, sollten Sie an dieser Stelle die Datei `openocd.cfg` so anpassen, dass `interface/stlink-v2.cfg` verwendet wird. +] else if lang == "ja" [ + *注記* + #link()[インストールの確認]セクションで、古いバージョンのdiscoveryボードを持っていることが判明している場合、 + `interface/stlink-v2.cfg`を使うように`openocd.cfg`ファイルを修正する必要があります。 ] else if lang == "zh" [ *注意* 如果你在#link()[安装验证]章节中,发现你的discovery开发板是一个更旧的版本,那么你应该修改你的 @@ -408,6 +495,8 @@ Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints ] else if lang == "de" [ Starten Sie in einem weiteren Terminal GDB, ebenfalls vom Stammverzeichnis des Templates aus. +] else if lang == "ja" [ + 別の端末で、GDBを実行します。こちらも、テンプレートプロジェクトのルートディレクトから実行して下さい。 ] else { todo } ```text @@ -432,6 +521,8 @@ gdb-multiarch -q target/thumbv7em-none-eabihf/debug/examples/hello Next connect GDB to OpenOCD, which is waiting for a TCP connection on port 3333. ] else if lang == "de" [ Verbinden Sie nun GDB mit OpenOCD, das auf eine TCP-Verbindung an Port 3333 wartet. +] else if lang == "ja" [ + 次に、TCP 3333ポートで接続待ちしているOpenOCDに、GDBを接続します。 ] else if lang == "zh" [ 接下来把GDB连接到OpenOCD,它正在等待一个在端口3333上的TCP链接。 ] else { todo } @@ -448,6 +539,8 @@ Remote debugging using :3333 ] else if lang == "de" [ Fahren Sie nun damit fort, das Programm mithilfe des Befehls `load` auf den Mikrocontroller zu _flashen_ (zu laden). +] else if lang == "ja" [ + それでは、`load`コマンドを使って、マイクロコントローラにプログラムを_書き込んで_下さい。 ] else if lang == "zh" [ 接下来使用`load`命令,继续 _flash_(加载) 程序到微控制器上。 ] else { todo } @@ -470,6 +563,10 @@ Transfer rate: 13 KB/sec, 2489 bytes/write. müssen wir OpenOCD anweisen, Semihosting zu aktivieren, bevor wir einen entsprechenden Aufruf tätigen. Befehle können mithilfe des Befehls `monitor` an OpenOCD gesendet werden. +] else if lang == "ja" [ + プログラムがロードされました。このプログラムはセミホスティングを使用します。そこで、 + セミホスティングを呼び出して何かを行う前に、OpenOCDにセミホスティングを有効にするように、 + 指示する必要があります。 ] else if lang == "zh" [ 程序现在被加载了。这个程序使用半主机模式,因此在我们调用半主机模式之前,我们必须告诉OpenOCD使能半主机。你可以使用 `monitor` 命令,发送命令给OpenOCD 。 @@ -486,6 +583,8 @@ semihosting is enabled ] else if lang == "de" [ Sie können sich alle OpenOCD-Befehle anzeigen lassen, indem Sie den Befehl `monitor help` aufrufen. +] else if lang == "ja" [ + `monitor help`コマンドを実行することで、全てのOpenOCDコマンドを見ることができます。 ] else if lang == "zh" [ 通过调用 `monitor help` 命令,你能看到所有的OpenOCD命令。 ] else { todo } @@ -497,6 +596,8 @@ semihosting is enabled ] else if lang == "de" [ Wie zuvor können wir mithilfe eines Breakpoints und des Befehls `continue` direkt zu `main` springen. +] else if lang == "ja" [ + 以前のように、ブレイクポイントと`continue`コマンドを使用することで、`main`までスキップすることができます。 ] else if lang == "zh" [ 像我们之前一样,使用一个断点和 `continue` 命令我们可以跳过所有的步骤到 `main` 。 @@ -554,6 +655,10 @@ hello::__cortex_m_rt_main () at examples/hello.rs:13 ] else if lang == "de" [ Nachdem Sie das Programm mit `next` weitergeführt haben, sollten Sie unter anderem „Hello, world!" auf der OpenOCD-Konsole ausgegeben sehen. +] else if lang == "ja" [ + #todoupd("ja") + この時点で、OpenOCDコンソールに、他のものと入り混じって「Hello, + world!」と表示されるはずです。 ] else if lang == "zh" [ 在使用了`next`让函数继续执行之后,你应该看到 "Hello, world!" 被打印到了OpenOCD控制台上。 @@ -637,19 +742,20 @@ load # start the process but immediately halt the processor stepi ``` - +#let cmd_gdb_hello = ` -x openocd.gdb target/thumbv7em-none-eabihf/debug/examples/hello` #if lang == "en" [ - Now running ` -x openocd.gdb target/thumbv7em-none-eabihf/debug/examples/hello` + Now running #cmd_gdb_hello will immediately connect GDB to OpenOCD, enable semihosting, load the program and start the process. ] else if lang == "de" [ - Wenn Sie nun den Befehl - ` -x openocd.gdb target/thumbv7em-none-eabihf/debug/examples/hello` + Wenn Sie nun den Befehl #cmd_gdb_hello ausführen, verbindet sich GDB sofort mit OpenOCD, aktiviert Semihosting, lädt das Programm und startet den Prozess. +] else if lang == "ja" [ + #cmd_gdb_hello;を実行することで、GDBはすぐにOpenOCDに接続し、 + セミホスティングを有効化し、プログラムをロードした上で、プロセスを開始します。 ] else if lang == "zh" [ - 现在运行 - ` -x openocd.gdb target/thumbv7em-none-eabihf/debug/examples/hello` + 现在运行 #cmd_gdb_hello 将会立即把GDB和OpenOCD连接起来,使能半主机,加载程序和启动进程。 ] else { todo } @@ -662,6 +768,10 @@ stepi Runner einrichten, sodass `cargo run` das Programm baut _und_ eine GDB-Sitzung startet. Dieser Runner ist bereits in der Datei `.cargo/config.toml` enthalten, jedoch auskommentiert. +] else if lang == "ja" [ + 別の方法として、` -x openocd.gdb`をカスタムランナーにして、`cargo run`でプログラムをビルドし、 + _さらに_GDBセッションを開始することもできます。このランナーは、`.cargo/config`に含まれていますが、 + コメントアウトされています。 ] else if lang == "zh" [ 另外,你能将 ` -x openocd.gdb` 放进一个自定义的 runner 中,使 `cargo run` 能编译程序并启动一个GDB会话。这个 runner 在 @@ -672,34 +782,33 @@ stepi head -n10 .cargo/config.toml ``` -#if lang in ("en", "zh") [ - ```toml - [target.thumbv7m-none-eabi] - # uncomment this to make `cargo run` execute programs on QEMU - # runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" - - [target.'cfg(all(target_arch = "arm", target_os = "none"))'] - # uncomment ONE of these three option to make `cargo run` start a GDB session - # which option to pick depends on your system - runner = "arm-none-eabi-gdb -x openocd.gdb" - # runner = "gdb-multiarch -x openocd.gdb" - # runner = "gdb -x openocd.gdb" - ``` -] else if lang == "de" [ - ```toml - [target.thumbv7m-none-eabi] - # Dies auskommentieren, damit `cargo run` Programme auf QEMU ausfuehrt. - # runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" - - [target.'cfg(all(target_arch = "arm", target_os = "none"))'] - # Heben Sie die Auskommentierung einer dieser drei Optionen auf, damit +#raw(block: true, lang: "toml", +"[target.thumbv7m-none-eabi] +# " + if lang in ("en", "zh") { + "uncomment this to make `cargo run` execute programs on QEMU" + } else if lang == "de" { + "Dies auskommentieren, damit `cargo run` Programme auf QEMU ausfuehrt." + } else if lang == "ja" { + "ここのコメントアウトを外すと、`cargo run`はQEMUでプログラムを実行します" + } else { todos } + " +# runner = \"qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel\" + +[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))'] +# " + if lang in ("en", "zh") { + "uncomment ONE of these three option to make `cargo run` start a GDB session +# which option to pick depends on your system" + } else if lang == "de" { + "Heben Sie die Auskommentierung einer dieser drei Optionen auf, damit # `cargo run` eine GDB-Sitzung startet. - # Welche Option Sie waehlen sollten, haengt von Ihrem System ab. - runner = "arm-none-eabi-gdb -x openocd.gdb" - # runner = "gdb-multiarch -x openocd.gdb" - # runner = "gdb -x openocd.gdb" - ``` -] else { todo } +# Welche Option Sie waehlen sollten, haengt von Ihrem System ab." + } else if lang == "ja" { + "3つの選択肢のうち、1つのコメントアウトを外すと、`cargo run`はGDBセッションを開始します。 +# どの選択肢を使うか、は対象システムによって異なります。" + } else { todos } + " +runner = \"arm-none-eabi-gdb -x openocd.gdb\" +# runner = \"gdb-multiarch -x openocd.gdb\" +# runner = \"gdb -x openocd.gdb\" +") ```text $ cargo run --example hello diff --git a/typ/start/index.typ b/typ/start/index.typ index 314b37ea..450c33af 100644 --- a/typ/start/index.typ +++ b/typ/start/index.typ @@ -2,6 +2,7 @@ #h1(if lang == "en" [Getting Started] else if lang == "de" [Erste Schritte] + else if lang == "ja" [入門] else if lang == "zh" [开始] else { todo }) @@ -24,6 +25,10 @@ Abschnitt, in dem Hardware erforderlich ist, ist natürlich der Abschnitt #link()[Hardware], wo wir OpenOCD verwenden, um ein #ln_f3 zu programmieren. +] else if lang == "ja" [ + このセクションでは、組込みプログラムを書いて、ビルドして、フラッシュに書き込み、デバッグする、という一連のプロセスを説明します。 + ほとんどの例を特別なハードウェアなしで試すことができます。有名なオープンソースハードウェアエミュレータであるQEMUを使うからです。 + ハードウェアが必要となる唯一のセクションは、当然ながら、OpenOCDを使って#ln_f3;にプログラムする#link("./hardware.md")[Hardware]セクションです。 ] else if lang == "zh" [ 在这部分里,你将会经历编写,编译,烧录和调试嵌入式程序。大多数的例子都不需要特定的硬件就可以试试,因为我们将要向你展示一个开源硬件仿真器,QEMU的基本使用。唯一需要硬件的部分,那就是,#link()[硬件]那一章,我们会使用OpenOCD去编程一个#ln_f3。 ] else { todo } \ No newline at end of file diff --git a/typ/start/interrupts.typ b/typ/start/interrupts.typ index 021a2e29..e484966f 100644 --- a/typ/start/interrupts.typ +++ b/typ/start/interrupts.typ @@ -2,6 +2,7 @@ #h1(offset: whole, if lang in ("en", "de") [Interrupts] + else if lang == "ja" [割り込み] else if lang == "zh" [中断] else { todo }) @@ -19,6 +20,9 @@ sich bei Interrupts -- sowohl hinsichtlich der Benennung als auch der Funktionalität -- stets um herstellerspezifische (und oft sogar chip-spezifische) Implementierungen. +] else if lang == "ja" [ + 割り込みは様々な点で例外と違いますが、その動作と使用方法は、ほとんど同じで、同じ割り込みコントローラによって処理されます。 + 例外がCortex-Mアーキテクチャで定義されているのに対し、割り込みは、命名と機能との両方において、常にベンダ(もっと言うとチップ)固有の実装です。 ] else if lang == "zh" [ 虽然中断和异常在很多方面都不一样,但是它们的操作和使用几乎是一样的,且它们也能被同一个中断控制器处理。然而异常是由Cortex-M微架构定义的,中断在命名和功能上总是由特定厂商(经常甚至是芯片)实现的。 ] else { todo } @@ -47,6 +51,12 @@ - Im Allgemeinen muss die Ursache für die Auslösung des Interrupts beseitigt werden, um zu verhindern, dass der Interrupt-Handler endlos erneut aufgerufen wird. +] else if lang == "ja" [ + 割り込みは、高度な使い方をしようとする時に必要とされる様々な柔軟性を考慮に入れています。 + 本書では、そのような高度な使い方は対象外です。しかし、次の点に留意することをお勧めします。 + - 割り込みは、ハンドラの実行順序を決めるプログラム可能な優先度を持ちます。 + - 割り込みは、ネストとプリエンプションが可能です。つまり、割り込みハンドラの実行は、より優先度の高い割り込みに割り込まれる場合があります。 + - 通常、割り込み要因は、割り込みハンドラが無限に再呼び出しされないようにするため、クリアされる必要があります。 ] else if lang == "zh" [ 中断提供了更多的灵活性,当尝试用一种高级的方法使用它们时,我们需要对这种灵活性进行解释。但我们将不会在这本书里涵盖这些内容,最好把下面的东西记在心里: - 中断有可以编程的优先级,其决定了它们的处理函数的执行顺序。 @@ -66,6 +76,11 @@ - Legen Sie die gewünschte Priorität des Interrupt-Handlers im Interrupt-Controller fest. - Aktivieren Sie den Interrupt-Handler im Interrupt-Controller. +] else if lang == "ja" [ + ランタイムでの一般的な初期化手順は、常に同じです。 + - 必要な時に割り込み要求を起こすように、ペリフェラルを設定します + - 割り込みコントローラで割り込みハンドラの優先度をセットします + - 割り込みコントローラで割り込みハンドラを有効化します ] else if lang == "zh" [ 运行时的初始化步骤总是相同的: - 设置外设在想要的事件发生时产生中断请求 @@ -87,6 +102,9 @@ wenn das Device-Feature aktiviert ist. Es ist allerdings nicht für die direkte Verwendung vorgesehen; eine solche würde zu einem Kompilierfehler führen. +] else if lang == "ja" [ + 例外と同様に、例外ハンドラを宣言するために、`cortex-m-rt`クレートは、#ln_int;属性を提供しています。 + 利用可能な割り込み(そして割り込みハンドラテーブルでの配置)は、通常、`svd2rust`を使ってSVDから自動生成されます。 ] else if lang == "zh" [ #todoupd("zh") 与异常相似,`cortex-m-rt` @@ -122,6 +140,8 @@ use lm3s6965::interrupt; // " + if lang == "en" { "Interrupt handler for the Timer2 interrupt" } else if lang == "de" { "Interrupt-Handler fuer den Timer2-Interrupt" + } else if lang == "ja" { + "タイマ2割り込みの割り込みハンドラ" } else if lang == "zh" { "Timer2中断的中断处理函数" } else { todos } + " @@ -132,6 +152,8 @@ fn TIMER2A() { "Clear reason for the generated interrupt request" } else if lang == "de" { "Eindeutiger Grund fuer die generierte Interrupt-Anforderung" + } else if lang == "ja" { + "発生した割り込み要求の原因をクリアします" } else if lang == "zh" { "清除生成中断请求的原因" } else { todos } + " @@ -151,6 +173,10 @@ fn TIMER2A() { Firmware aufgerufen werden. Es ist allerdings möglich, Interrupt-Anforderungen per Software zu erzeugen, um einen Sprung zum Interrupt-Handler auszulösen. +] else if lang == "ja" [ + 割り込みハンドラは、通常の関数のように見え、例外ハンドラに似ています(引数がないことを除いて)。 + しかし、割り込みハンドラは、特別な呼び出し規約のため、ファームウェアの他の部分から直接呼び出すことができません。 + ソフトウェアで割り込み要求を起こし、割り込みハンドラへの転送を発生させることは可能です。 ] else if lang == "zh" [ 中断处理函数和异常处理函数一样看起来像是普通的函数(除了没有入参)。然而由于特殊的调用规定,它不能被固件的其它部分直接调用。然而,可以在软件中生成中断请求,转移到中断处理函数中。 ] else { todo } @@ -162,6 +188,8 @@ fn TIMER2A() { Ähnlich wie bei Exception-Handlern ist es auch hier möglich, `static mut`-Variablen innerhalb der Interrupt-Handler zu deklarieren, um den Zustand auf _sichere_ Weise zu speichern. +] else if lang == "ja" [ + 例外ハンドラと同様に、割り込みハンドラ内で`static mut`変数を宣言し、状態を_安全_に保持することができます。 ] else if lang == "zh" [ 与异常处理函数一样,也能在中断处理函数中声明`static mut`变量且保持 _safe_ 状态。 @@ -176,6 +204,8 @@ fn TIMER2A() { "`COUNT` has type `&mut u32` and it's safe to use" } else if lang == "de" { "`COUNT` hat den Typ `&mut u32` und ist sicher zu verwenden." + } else if lang == "ja" { + "`COUNT`は`&mut u32`の型を持っており、その使用は安全です" } else if lang == "zh" { "`COUNT` 的类型是 `&mut u32` 且它用起来安全" } else { todos } + " @@ -190,6 +220,8 @@ fn TIMER2A() { Für eine detailliertere Beschreibung der hier veranschaulichten Mechanismen konsultieren Sie bitte den #link()[Abschnitt zu Ausnahmen]. +] else if lang == "ja" [ + ここで示した仕組みの詳細については、#link()[例外セクション]を参照して下さい。 ] else if lang == "zh" [ 关于这里所说的机制的更多细节描述,请参考#link()[异常章节]。 ] else { todo } diff --git a/typ/start/panicking.typ b/typ/start/panicking.typ index 3a67b053..fd814aeb 100644 --- a/typ/start/panicking.typ +++ b/typ/start/panicking.typ @@ -3,6 +3,7 @@ #h1(offset: whole, if lang == "en" [Panicking] else if lang == "de" [In Panik geraten] + else if lang == "ja" [パニック] else if lang == "zh" [运行时恐慌(Panicking)] else { todo }) @@ -16,6 +17,9 @@ Rust. Eingebaute Operationen wie der Zugriff per Index werden zur Laufzeit auf Speichersicherheit überprüft. Erfolgt ein Zugriff außerhalb der zulässigen Grenzen, führt dies zu einer Panik. +] else if lang == "ja" [ + パニックはRustのコア部分です。インデックス操作のような言語組込みの操作は、メモリ安全性をランタイム時に検査されます。 + 範囲外のインデックスにアクセスしようとすると、パニックが発生します。 ] else if lang == "zh" [ 运行时恐慌是Rust语言的一个核心部分。像是索引这样的内建的操作为了存储安全性是运行时检查的。当尝试越界索引时,这会导致运行时恐慌(panic)。 ] else { todo } @@ -29,6 +33,9 @@ Der Stack des betroffenen Threads wird abgewickelt (Stack Unwinding), es sei denn, der Benutzer hat sich für einen Programmabbruch im Falle einer Panik entschieden. +] else if lang == "ja" [ + 標準ライブラリでは、パニックは定義された動作です。ユーザがパニック発生時にプログラムをアボートする選択をしない限り、 + パニックを起こしたスレッドのスタックを巻き戻します。 ] else if lang == "zh" [ 在标准库中,运行时恐慌的行为被定义成:展开(unwinds)恐慌的线程的栈,除非用户自己选择在恐慌时终止程序。 ] else { todo } @@ -48,6 +55,11 @@ Abhängigkeitsgraphen des Programms genau _einmal_ vorkommen und folgende Signatur aufweisen: `fn(&PanicInfo) -> !`, wobei #ln_info eine Struktur ist, die Informationen über den Ort des Panic enthält. +] else if lang == "ja" [ + しかし、非標準のプログラムでは、パニック時の挙動は、未定義のままです。`#[panic_handler]`関数を宣言することにより、 + 挙動を選択することができます。この関数は、プログラムの依存関係グラフに、*1回だけ*現れる必要があります。 + そして、 `fn(&PanicInfo) -> !`のシグネチャを持つ必要があります。 + ここで、#ln_info;は、パニックした位置情報を含む構造体です。 ] else if lang == "zh" [ 然而在没有标准库的程序中,运行时恐慌的行为是未被定义了的。通过声明一个 `#[panic_handler]` 函数可以选择一个运行时恐慌的行为。 @@ -85,6 +97,16 @@ Cortex-M spezifischen Peripheriekomponente. - #ln_sh. Die Panik-Meldung wird mithilfe der Semihosting-Technik auf dem Host protokolliert. +] else if lang == "ja" [ + 組込みシステムは、ユーザとやり取りするものから、安全性が重要な(クラッシュできない)ものまであります。 + そのため、全てのパニック時動作に対応できる唯一のものはありませんが、よく利用される挙動がたくさんあります。 + これらの一般的な挙動が、`#[panic_handler]`関数を定義するクレートにまとめられています。 + いくつか、例を挙げます。 + - #ln_abort。パニックが発生すると、アボート命令を実行します。 + - #ln_halt。パニックが発生すると、プログラム、または、現在のスレッドは、無限ループに入ることで停止します。 + - #ln_itm。パニック発生時のメッセージは、ARM + Cortex-M固有のペリフェラルであるITMを使ってログ出力されます。 + - #ln_sh。パニック発生時のメッセージは、セミホスティングを使ってログ出力されます。 ] else if lang == "zh" [ 鉴于嵌入式系统的范围从面向用户的系统到安全关键系统,没有一个运行时恐慌行为能满足所有场景,但是有许多常用的行为。这些常用的行为已经被打包进了一些crates中,这些crates中定义了 `#[panic_handler]`函数。比如: @@ -106,6 +128,8 @@ ] else if lang == "de" [ Möglicherweise findest du noch mehr Crates, wenn du auf crates.io nach dem Schlüsselwort #ln_handler suchst. +] else if lang == "ja" [ + crates.ioで#ln_handler;をキーワードに検索することで、さらにクレートを見つけることができます。 ] else if lang == "zh" [ 在crates.io上搜索 #ln_handler,你甚至可以找到更多的crates。 @@ -124,6 +148,11 @@ ausgedrückt wird, ist nicht nur als Dokumentation nützlich, sondern ermöglicht es auch, dieses Verhalten je nach Kompilierungsprofil anzupassen. Zum Beispiel: +] else if lang == "ja" [ + プログラムは、対応するクレートとリンクすることで、これらの挙動の中から1つを選びます。 + パニック時の挙動がアプリケーションソースコードの中で単一行で表現されていることは、ドキュメントとして有用なだけでなく、 + パニック時の挙動をコンパイル時のプロファイルで変更にする時にも利用できます。 + 例えば ] else if lang == "zh" [ 仅仅通过链接到相关的crate中,一个程序就可以简单地从这些行为中选择一个运行时恐慌行为。将运行时恐慌的行为作为一行代码放进一个应用的源码中,不仅仅是因为可以作为文档使用,而且能根据编译配置改变运行时恐慌的行为。比如: ] else { todo } @@ -137,6 +166,8 @@ } else if lang == "de" { "dev-Profil: einfacheres Debuggen von Panics; man kann einen Breakpoint bei // `rust_begin_unwind` setzen." + } else if lang == "ja" { + "開発プロファイル:パニックのデバッグを容易にします。`rust_begin_unwind`にブレイクポイントを置くことを可能にします。" } else if lang == "zh" { "dev配置: 更容易调试运行时恐慌; 可以在 `rust_begin_unwind` 上放一个断点" } else { todos } + " @@ -147,6 +178,8 @@ use panic_halt as _; "release profile: minimize the binary size of the application" } else if lang == "de" { "Release-Profil: Minimierung der Binaergroesse der Anwendung" + } else if lang == "ja" { + "リリースプロファイル:アプリケーションのバイナリサイズを最小化します。" } else if lang == "zh" { "release配置: 最小化应用的二进制文件的大小" } else { todos } + " @@ -161,10 +194,14 @@ use panic_abort as _; with the dev profile (`cargo build`), but links to the `panic-abort` crate when built with the release profile (`cargo build --release`). ] else if lang == "de" [ -In diesem Beispiel verlinkt der Crate beim Bauen mit dem Dev-Profil -(`cargo build`) auf den `panic-halt`-Crate, beim Bauen mit dem -Release-Profil (`cargo build --release`) hingegen auf den -`panic-abort`-Crate. + In diesem Beispiel verlinkt der Crate beim Bauen mit dem Dev-Profil + (`cargo build`) auf den `panic-halt`-Crate, beim Bauen mit dem + Release-Profil (`cargo build --release`) hingegen auf den + `panic-abort`-Crate. +] else if lang == "ja" [ + #todoupd("ja") + この例では、開発プロファイルでビルド(`cargo build`)した時は、`panic-halt`クレートとリンクします。 + しかし、リリースプロファイルでビルド(`cargo build --release`)した時は、`panic-abort`クレートとリンクします。 ] else if lang == "zh" [ 在这个例子里,当使用dev配置编译的时候(`cargo build`),crate链接到 `panic-halt` From 0ea91324864121aad25908d90d4abd8b54e3c974 Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Sun, 9 Aug 2026 14:22:35 +0300 Subject: [PATCH 08/11] dict syntax --- .gitignore | 1 + .vscode/settings.json | 3 +- typ/appendix/glossary.typ | 121 ++- typ/book.typ | 465 +++++---- typ/c-tips/index.typ | 781 ++++++++------ typ/collections/index.typ | 472 +++++---- typ/concurrency/index.typ | 987 ++++++++++-------- typ/config.typ | 19 + typ/design-patterns/hal/checklist.typ | 21 +- typ/design-patterns/hal/gpio.typ | 223 ++-- typ/design-patterns/hal/index.typ | 21 +- typ/design-patterns/hal/interoperability.typ | 125 ++- typ/design-patterns/hal/naming.typ | 30 +- typ/design-patterns/hal/predictability.typ | 63 +- typ/design-patterns/index.typ | 20 +- typ/interoperability/c-with-rust.typ | 337 +++--- typ/interoperability/index.typ | 164 +-- typ/interoperability/rust-with-c.typ | 267 +++-- typ/intro/hardware.typ | 105 +- typ/intro/index.typ | 587 ++++++----- typ/intro/install.typ | 211 ++-- typ/intro/install/linux.typ | 240 +++-- typ/intro/install/macos.typ | 57 +- typ/intro/install/verify.typ | 163 +-- typ/intro/install/windows.typ | 87 +- typ/intro/no-std.typ | 287 ++--- typ/intro/tooling.typ | 327 +++--- typ/peripherals/a-first-attempt.typ | 235 +++-- typ/peripherals/borrowck.typ | 83 +- typ/peripherals/index.typ | 157 +-- typ/peripherals/singletons.typ | 335 +++--- typ/portability/index.typ | 237 +++-- typ/start/exceptions.typ | 513 +++++---- typ/start/hardware.typ | 655 +++++++----- typ/start/index.typ | 27 +- typ/start/interrupts.typ | 171 +-- typ/start/panicking.typ | 207 ++-- typ/start/qemu.typ | 858 +++++++++------ typ/start/registers.typ | 407 ++++---- typ/start/semihosting.typ | 131 ++- typ/static-guarantees/design-contracts.typ | 398 ++++--- typ/static-guarantees/gpio-table.typ | 144 +-- typ/static-guarantees/index.typ | 53 +- typ/static-guarantees/state-machines.typ | 140 ++- .../typestate-programming.typ | 74 +- .../zero-cost-abstractions.typ | 94 +- typ/unsorted/index.typ | 9 +- typ/unsorted/math.typ | 73 +- typ/unsorted/speed-vs-size.typ | 351 ++++--- 49 files changed, 6734 insertions(+), 4802 deletions(-) diff --git a/.gitignore b/.gitignore index 7585238e..122c0943 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ book +.vscode diff --git a/.vscode/settings.json b/.vscode/settings.json index 7f993f80..5fffd65e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,6 +3,7 @@ "tinymist.typstExtraArgs": [ "--features=html,bundle", "--input=lang=en", - "--input=target=pdf" + "--input=target=pdf", + "--input=goal=translation" ] } diff --git a/typ/appendix/glossary.typ b/typ/appendix/glossary.typ index a0da7097..20ba00b1 100644 --- a/typ/appendix/glossary.typ +++ b/typ/appendix/glossary.typ @@ -1,34 +1,40 @@ #import "../config.typ": * -#h1(if lang == "en" [Appendix A: Glossary] - else if lang == "de" [Anhang A: Glossar] - else if lang == "zh" [附录A: 词汇表] - else { todo }) - -#if lang == "en" [ +#h1(tr(( + en: [Appendix A: Glossary], + de: [Anhang A: Glossar], + zh: [附录A: 词汇表], +))) + +#tr(( +en: [ The embedded ecosystem is full of different protocols, hardware components and vendor-specific things that use their own terms and abbreviations. This Glossary attempts to list them with pointers for understanding them better. -] else if lang == "de" [ +], +de: [ Das Embedded-Ökosystem ist geprägt von einer Vielzahl unterschiedlicher Protokolle, Hardwarekomponenten und herstellerspezifischer Elemente, für die jeweils eigene Begriffe und Abkürzungen verwendet werden. Dieses Glossar soll diese auflisten und Hinweise zum besseren Verständnis geben. -] else if lang == "zh" [ +], +zh: [ 嵌入式生态系统中充满了不同的协议,硬件组件,还有许多与生产商相关的东西,它们都使用自己的缩写和项目名。这个词汇表尝试列出它们以便更好理解它们。 -] else { todo } +])) *BSP* #let url_bspvideo = "https://youtu.be/vLYit_HHPaY" -#if lang == "en" [ +#tr(( +en: [ A Board Support Crate provides a high level interface configured for a specific board. It usually depends on a #link()[HAL] crate. There is a more detailed description on the #link()[memory-mapped registers page] or for a broader overview see #link(url_bspvideo)[this video]. -] else if lang == "de" [ +], +de: [ Ein „Board Support Crate" stellt eine auf eine bestimmte Platine zugeschnittene Schnittstelle auf hoher Ebene bereit. Es hängt in der Regel von einem #link()[HAL]-Crate ab. Eine ausführlichere @@ -36,29 +42,34 @@ #link()[Seite zu den im Speicher abgebildeten Registern] oder für einen umfassenderen Überblick sehen Sie sich #link(url_bspvideo)[dieses Video] an. -] else if lang == "zh" [ +], +zh: [ 板级支持的Crate(Board Support Crate)提供为某个特定板子配置的高级接口。它通常依赖一个#link()[HAL] crate 。在#link()[存储映射的寄存器那页]有更多细节的描述或者看#link(url_bspvideo)[这个视频]来获取一个更广泛的概述。 -] else { todo } +])) *FPU* -#if lang == "en" [ +#tr(( +en: [ Floating-point Unit. A 'math processor' running only operations on floating-point numbers. -] else if lang == "de" [ +], +de: [ Gleitkommaeinheit. Ein „Mathematikprozessor", der ausschließlich Operationen mit Gleitkommazahlen ausführt. -] else if lang == "zh" [ +], +zh: [ 浮点单元(Floating-Point Unit)。一个只运行在浮点数上的'数学处理器'。 -] else { todo } +])) *HAL* #let ln_hal = link("https://crates.io/crates/embedded-hal")[`embedded-hal`] #let url_hal_video = "https://youtu.be/vLYit_HHPaY" -#if lang == "en" [ +#tr(( +en: [ A Hardware Abstraction Layer crate provides a developer friendly interface to a microcontroller's features and peripherals. It is usually implemented on top of a #link()[Peripheral Access Crate (PAC)]. It @@ -66,7 +77,8 @@ There is a more detailed description on the #link()[memory-mapped registers page] or for a broader overview see #link(url_hal_video)[this video]. -] else if lang == "de" [ +], +de: [ Ein „Hardware Abstraction Layer"-Crate bietet eine entwicklerfreundliche Schnittstelle zu den Funktionen und Peripheriegeräten eines Mikrocontrollers. Es wird in der Regel auf Basis eines @@ -75,35 +87,40 @@ Eine ausführlichere Beschreibung finden Sie auf der #link()[Seite zu den im Speicher abgebildeten Registern] oder für einen umfassenderen Überblick in #link(url_hal_video)[diesem Video]. -] else if lang == "zh" [ +], +zh: [ 硬件抽象层(Hardware Abstraction Layer) crate为微控制器的功能和外设提供一个开发者友好的接口。它通常在#link()[Peripheral Access Crate (PAC)]之上被实现。它可能也会实现来自#ln_hal crate的traits 。在#link()[存储映射的寄存器那页]上有更多的细节或者看#link(url_hal_video)[这个视频]获取一个更广泛的概述。 -] else { todo } +])) *I2C* #let wiki_i2c = "https://en.wikipedia.org/wiki/I2c" -#if lang == "en" [ +#tr(( +en: [ Sometimes referred to as `I²C` or Inter-IC. It is a protocol meant for hardware communication within a single integrated circuit. See #link(wiki_i2c)[here] for more details -] else if lang == "de" [ +], +de: [ Wird manchmal auch als „I²C" oder „Inter-IC" bezeichnet. Es handelt sich um ein Protokoll für die Hardware-Kommunikation innerhalb eines einzelnen integrierten Schaltkreises. Weitere Informationen finden Sie #link(wiki_i2c)[hier]. -] else if lang == "zh" [ +], +zh: [ 有时又被称为 `I²C` 或者 Intere-IC 。它是一种用于在单个集成电路中进行硬件通信的协议。看#link(wiki_i2c)[这里]来获取更多细节。 -] else { todo } +])) *PAC* #let ln_svd2rust = link("https://github.com/rust-embedded/svd2rust/")[svd2rust] #let url_pacvideo = "https://youtu.be/vLYit_HHPaY" -#if lang == "en" [ +#tr(( +en: [ A Peripheral Access Crate provides access to a microcontroller's peripherals. It is one of the lower level crates and is usually generated directly from the provided #link()[SVD], @@ -112,7 +129,8 @@ depend on this crate. There is a more detailed description on the #link()[memory-mapped registers page] or for a broader overview see #link(url_pacvideo)[this video]. -] else if lang == "de" [ +], +de: [ Ein „Peripheral Access Crate" ermöglicht den Zugriff auf die Peripheriegeräte eines Mikrocontrollers. Es handelt sich um eines der Crates auf niedrigerer Ebene, das in der Regel direkt aus der @@ -124,63 +142,76 @@ #link()[Seite zu den im Speicher abgebildeten Registern] oder für einen umfassenderen Überblick in #link(url_pacvideo)[diesem Video]. -] else if lang == "zh" [ +], +zh: [ 一个外设访问 Crate (Peripheral Access Crate)提供了对一个微控制器的外设的访问。它是一个底层的crates且通常从提供的#link()[SVD]被直接生成,经常使用#ln_svd2rust。#link()[硬件抽象层]应该依赖这个crate。在#link()[存储映射的寄存器那页]有更细节的描述或者看#link(url_pacvideo)[这个视频]获取一个更广泛的概述。 -] else { todo } +])) *SPI* #let wiki_spi = "https://en.wikipedia.org/wiki/Serial_peripheral_interface" -#if lang == "en" [ +#tr(( +en: [ Serial Peripheral Interface. See #link(wiki_spi)[here] for more information. -] else if lang == "de" [ +], +de: [ Serial Peripheral Interface. Weitere Informationen finden Sie #link(wiki_spi)[hier]. -] else if lang == "zh" [ +], +zh: [ 串行外设接口。看#link(wiki_spi)[这里]获取更多信息。 -] else { todo } +])) *SVD* #let url_svd = "https://www.keil.com/pack/doc/CMSIS/SVD/html/index.html" -#if lang == "en" [ +#tr(( +en: [ System View Description is an XML file format used to describe the programmers view of a microcontroller device. You can read more about it on #link(url_svd)[the ARM CMSIS documentation site]. -] else if lang == "de" [ +], +de: [ „System View Description" ist ein XML-Dateiformat, das dazu dient, die Programmierersicht eines Mikrocontrollers zu beschreiben. Weitere Informationen hierzu finden Sie auf #link(url_svd)[der ARM CMSIS-Dokumentationsseite]. -] else if lang == "zh" [ +], +zh: [ 系统视图描述文件(System View Description)是一个XML文件格式,以程序员视角来描述一个微控制器设备。你能在#link(url_svd)[the ARM CMSIS documentation site]上获取更多信息。 -] else { todo } +])) *UART* #let wiki_uart = "https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter" -#if lang == "en" [ +#tr(( +en: [ Universal asynchronous receiver-transmitter. See #link(wiki_uart)[here] for more information. -] else if lang == "de" [ +], +de: [ Universeller asynchroner Empfänger-Sender. Weitere Informationen finden Sie #link(wiki_uart)[hier]. -] else if lang == "zh" [ +], +zh: [ 通用异步收发器。看#link(wiki_uart)[这里]获取更多信息。 -] else { todo } +])) *USART* #let wiki_usart = "https://en.wikipedia.org/wiki/Universal_synchronous_and_asynchronous_receiver-transmitter" -#if lang == "en" [ +#tr(( +en: [ Universal synchronous and asynchronous receiver-transmitter. See #link(wiki_usart)[here] for more information. -] else if lang == "de" [ +], +de: [ Universeller synchroner und asynchroner Empfänger-Sender. Weitere Informationen finden Sie #link(wiki_usart)[hier]. -] else if lang == "zh" [ +], +zh: [ 通用同步异步收发器。看#link(wiki_usart)[这里]获取更多信息。 -] else { todo } +])) diff --git a/typ/book.typ b/typ/book.typ index 2551f2b6..b97fdd49 100644 --- a/typ/book.typ +++ b/typ/book.typ @@ -4,20 +4,23 @@ #let sources = ( "intro/index": ( content: include "intro/index.typ", - title: if lang == "en" [Introduction] - else if lang == "de" [Einleitung] - else if lang == "ja" [導入] - else if lang == "uk" [Вступ] - else if lang == "zh" [引言] - else { todo }, + title: tr(( + en: [Introduction], + de: [Einleitung], + ja: [導入], + uk: [Вступ], + zh: [引言], + )), sub: ( "intro/hardware": ( content: include "intro/hardware.typ", - title: if lang in ("en", "de") [Hardware] - else if lang == "ja" [ハードウェア] - else if lang == "uk" [Залізо] - else if lang == "zh" [硬件] - else { todo } + title: tr(( + en: [Hardware], + de: [Hardware], + ja: [ハードウェア], + uk: [Залізо], + zh: [硬件], + )), ), "intro/no-std": ( content: include "intro/no-std.typ", @@ -25,20 +28,23 @@ ), "intro/tooling": ( content: include "intro/tooling.typ", - title: if lang == "en" [Tooling] - else if lang == "de" [Werkzeuge] - else if lang == "ja" [ツール] - else if lang == "uk" [Інструменти] - else if lang == "zh" [工具] - else { todo } + title: tr(( + en: [Tooling], + de: [Werkzeuge], + ja: [ツール], + uk: [Інструменти], + zh: [工具], + )), ), "intro/install": ( content: include "intro/install.typ", - title: if lang in ("en", "de") [Installation] - else if lang == "ja" [インストール] - else if lang == "uk" [Встановлення] - else if lang == "zh" [安装] - else { todo }, + title: tr(( + en: [Installation], + de: [Installation], + ja: [インストール], + uk: [Встановлення], + zh: [安装], + )), sub: ( "intro/install/linux": ( content: include "intro/install/linux.typ", @@ -54,12 +60,13 @@ ), "intro/install/verify": ( content: include "intro/install/verify.typ", - title: if lang == "en" [Verify Installation] - else if lang == "de" [Die Installation überprüfen] - else if lang == "ja" [インストールの確認] - else if lang == "uk" [Перевірка встановлення] - else if lang == "zh" [验证工具链的安装] - else { todo } + title: tr(( + en: [Verify Installation], + de: [Die Installation überprüfen], + ja: [インストールの確認], + uk: [Перевірка встановлення], + zh: [验证工具链的安装], + )), ) ) ), @@ -67,12 +74,13 @@ ), "start/index": ( content: include "start/index.typ", - title: if lang == "en" [Getting started] - else if lang == "de" [Erste Schritte] - else if lang == "ja" [入門] - else if lang == "uk" [Початок роботи] - else if lang == "zh" [开始] - else { todo }, + title: tr(( + en: [Getting started], + de: [Erste Schritte], + ja: [入門], + uk: [Початок роботи], + zh: [开始], + )), sub: ( "start/qemu": ( content: include "start/qemu.typ", @@ -80,211 +88,240 @@ ), "start/hardware": ( content: include "start/hardware.typ", - title: if lang in ("en", "de") [Hardware] - else if lang == "ja" [ハードウェア] - else if lang == "uk" [Залізо] - else if lang == "zh" [硬件] - else { todo } + title: tr(( + en: [Hardware], + de: [Hardware], + ja: [ハードウェア], + uk: [Залізо], + zh: [硬件], + )), ), "start/registers": ( content: include "start/registers.typ", - title: if lang == "en" [Memory-mapped Registers] - else if lang == "de" [Im Speicher abgebildete Register] - else if lang == "ja" [メモリマップドレジスタ] - else if lang == "uk" [Відображені в пам'яті регістри] - else if lang == "zh" [存储映射的寄存器] - else { todo } + title: tr(( + en: [Memory-mapped Registers], + de: [Im Speicher abgebildete Register], + ja: [メモリマップドレジスタ], + uk: [Відображені в пам'яті регістри], + zh: [存储映射的寄存器], + )), ), "start/semihosting": ( content: include "start/semihosting.typ", - title: if lang in ("en", "de", "uk") [Semihosting] - else if lang == "ja" [セミホスティング] - else if lang == "zh" [半主机模式] - else { todo } + title: tr(( + ja: [セミホスティング], + zh: [半主机模式], + ), default: [Semihosting]), ), "start/panicking": ( content: include "start/panicking.typ", - title: if lang == "en" [Panicking] - else if lang == "de" [In Panik geraten] - else if lang == "ja" [パニック] - else if lang == "uk" [Паніка] - else if lang == "zh" [运行时恐慌(Panicking)] - else { todo } + title: tr(( + en: [Panicking], + de: [In Panik geraten], + ja: [パニック], + uk: [Паніка], + zh: [运行时恐慌(Panicking)], + )), ), "start/exceptions": ( content: include "start/exceptions.typ", - title: if lang in ("en", "de") [Exceptions] - else if lang == "ja" [例外] - else if lang == "uk" [Виключення] - else if lang == "zh" [异常] - else { todo } + title: tr(( + en: [Exceptions], + de: [Exceptions], + ja: [例外], + uk: [Виключення], + zh: [异常], + )), ), "start/interrupts": ( content: include "start/interrupts.typ", - title: if lang in ("en", "de") [Interrupts] - else if lang == "ja" [割り込み] - else if lang == "uk" [Переривання] - else if lang == "zh" [中断] - else { todo } + title: tr(( + en: [Interrupts], + de: [Interrupts], + ja: [割り込み], + uk: [Переривання], + zh: [中断], + )), ), ) ), "peripherals/index": ( content: include "peripherals/index.typ", - title: if lang == "en" [Peripherals] - else if lang == "de" [Peripheriegeräte] - else if lang == "ja" [ペリフェラル] - else if lang == "uk" [Периферійні пристрої] - else if lang == "zh" [外设] - else { todo }, + title: tr(( + en: [Peripherals], + de: [Peripheriegeräte], + ja: [ペリフェラル], + uk: [Периферійні пристрої], + zh: [外设], + )), sub: ( "peripherals/a-first-attempt": ( content: include "peripherals/a-first-attempt.typ", - title: if lang == "en" [A first attempt in Rust] - else if lang == "de" [Ein erster Versuch in Rust] - else if lang == "ja" [Rustでの最初の試み] - else if lang == "uk" [Перша спроба на Rust] - else if lang == "zh" [Rust尝鲜] - else { todo } + title: tr(( + en: [A first attempt in Rust], + de: [Ein erster Versuch in Rust], + ja: [Rustでの最初の試み], + uk: [Перша спроба на Rust], + zh: [Rust尝鲜], + )), ), "peripherals/borrowck": ( content: include "peripherals/borrowck.typ", - title: if lang == "en" [The Borrow Checker] - else if lang == "de" [Der Borrow-Prüfer] - else if lang == "ja" [借用チェッカ] - else if lang == "uk" [Перевірка запозичень] - else if lang == "zh" [借用检查器] - else { todo } + title: tr(( + en: [The Borrow Checker], + de: [Der Borrow-Prüfer], + ja: [借用チェッカ], + uk: [Перевірка запозичень], + zh: [借用检查器], + )), ), "peripherals/singletons": ( content: include "peripherals/singletons.typ", - title: if lang in ("en", "de") [Singletons] - else if lang == "ja" [シングルトン] - else if lang == "uk" [Одинаки] - else if lang == "zh" [单例] - else { todo } + title: tr(( + en: [Singletons], + de: [Singletons], + ja: [シングルトン], + uk: [Одинаки], + zh: [单例], + )), ) ) ), "static-guarantees/index": ( content: include "static-guarantees/index.typ", - title: if lang == "en" [Static Guarantees] - else if lang == "de" [Statische Garantien] - else if lang == "ja" [静的な保証] - else if lang == "uk" [Статичні гарантії] - else if lang == "zh" [静态保障(static guarantees)] - else { todo }, + title: tr(( + en: [Static Guarantees], + de: [Statische Garantien], + ja: [静的な保証], + uk: [Статичні гарантії], + zh: [静态保障(static guarantees)], + )), sub: ( "static-guarantees/typestate-programming": ( content: include "static-guarantees/typestate-programming.typ", - title: if lang == "en" [Typestate Programming] - else if lang == "de" [Typgestützte Programmierung] - else if lang == "ja" [型状態プログラミング] - else if lang == "uk" [Програмування типів-станів] - else if lang == "zh" [类型状态编程] - else { todo } + title: tr(( + en: [Typestate Programming], + de: [Typgestützte Programmierung], + ja: [型状態プログラミング], + uk: [Програмування типів-станів], + zh: [类型状态编程], + )), ), "static-guarantees/state-machines": ( content: include "static-guarantees/state-machines.typ", - title: if lang == "en" [Peripherals as State Machines] - else if lang == "de" [Peripheriegeräte als Zustandsmaschinen] - else if lang == "ja" [ステートマシンとしてのペリフェラル] - else if lang == "uk" [Периферія як кінцеві автомати] - else if lang == "zh" [把外设当作状态机] - else { todo } + title: tr(( + en: [Peripherals as State Machines], + de: [Peripheriegeräte als Zustandsmaschinen], + ja: [ステートマシンとしてのペリフェラル], + uk: [Периферія як кінцеві автомати], + zh: [把外设当作状态机], + )), ), "static-guarantees/design-contracts": ( content: include "static-guarantees/design-contracts.typ", - title: if lang == "en" [Design Contracts] - else if lang == "de" [Designverträge] - else if lang == "ja" [設計契約] - else if lang == "uk" [Угоди щодо дизайну] - else if lang == "zh" [设计约定] - else { todo } + title: tr(( + en: [Design Contracts], + de: [Designverträge], + ja: [設計契約], + uk: [Угоди щодо дизайну], + zh: [设计约定], + )), ), "static-guarantees/zero-cost-abstractions": ( content: include "static-guarantees/zero-cost-abstractions.typ", - title: if lang == "en" [Zero Cost Abstractions] - else if lang == "de" [Abstraktionen ohne Kosten] - else if lang == "ja" [ゼロコスト抽象化] - else if lang == "uk" [Безкоштовні абстракції] - else if lang == "zh" [零成本抽象] - else { todo } + title: tr(( + en: [Zero Cost Abstractions], + de: [Abstraktionen ohne Kosten], + ja: [ゼロコスト抽象化], + uk: [Безкоштовні абстракції], + zh: [零成本抽象], + )), ), ) ), "portability/index": ( content: include "portability/index.typ", - title: if lang == "en" [Portability] - else if lang == "de" [Portabilität] - else if lang == "ja" [移植性] - else if lang == "uk" [Переносимість] - else if lang == "zh" [可移植性] - else { todo } + title: tr(( + en: [Portability], + de: [Portabilität], + ja: [移植性], + uk: [Переносимість], + zh: [可移植性], + )), ), "concurrency/index": ( content: include "concurrency/index.typ", - title: if lang == "en" [Concurrency] - else if lang == "de" [Nebenläufigkeit] - else if lang == "ja" [並行性] - else if lang == "uk" [Паралелізм] - else if lang == "zh" [并发] - else { todo } + title: tr(( + en: [Concurrency], + de: [Nebenläufigkeit], + ja: [並行性], + uk: [Паралелізм], + zh: [并发], + )), ), "collections/index": ( content: include "collections/index.typ", - title: if lang == "en" [Collections] - else if lang == "de" [Sammlungen] - else if lang == "ja" [コレクション] - else if lang == "uk" [Колекції] - else if lang == "zh" [容器] - else { todo } + title: tr(( + en: [Collections], + de: [Sammlungen], + ja: [コレクション], + uk: [Колекції], + zh: [容器], + )), ), "design-patterns/index": ( content: include "design-patterns/index.typ", - title: if lang == "en" [Design Patterns] - else if lang == "de" [Desginmuster] - else if lang == "uk" [Шаблони проектування] - else if lang == "zh" [设计模式] - else { todo }, + title: tr(( + en: [Design Patterns], + de: [Desginmuster], + uk: [Шаблони проектування], + zh: [设计模式], + )), sub: ( "design-patterns/hal/index": ( content: include "design-patterns/hal/index.typ", - title: if lang in ("en", "zh", "de") [HALs] - else if lang == "uk" [HALи] - else { todo }, + title: tr(( + en: [HALs], + de: [HALs], + zh: [HALs], + uk: [HALи] + )), sub: ( "design-patterns/hal/checklist": ( content: include "design-patterns/hal/checklist.typ", - title: if lang == "en" [Checklist] - else if lang == "de" [Checkliste] - else if lang == "uk" [Контрольний список] - else if lang == "zh" [列表] - else { todo } + title: tr(( + en: [Checklist], + de: [Checkliste], + uk: [Контрольний список], + zh: [列表], + )), ), "design-patterns/hal/naming": ( content: include "design-patterns/hal/naming.typ", - title: if lang == "en" [Naming] - else if lang == "de" [Benennung] - else if lang == "uk" [Найменування] - else if lang == "zh" [命名] - else { todo } + title: tr(( + en: [Naming], + de: [Benennung], + uk: [Найменування], + zh: [命名], + )), ), "design-patterns/hal/interoperability": ( content: include "design-patterns/hal/interoperability.typ", - title: if lang == "en" [Interoperability] - else if lang == "de" [Interoperabilität] - else if lang == "uk" [Сумісність] - else if lang == "zh" [互操性] - else { todo } + title: tr(( + en: [Interoperability], + de: [Interoperabilität], + uk: [Сумісність], + zh: [互操性], + )), ), "design-patterns/hal/predictability": ( content: include "design-patterns/hal/predictability.typ", - title: if lang == "en" [Predictability] - else if lang == "de" [Vorhersehbarkeit] - else if lang == "uk" [Передбачуваність] - else if lang == "zh" [可预见性] - else { todo } + title: tr(( + en: [Predictability], + de: [Vorhersehbarkeit], + uk: [Передбачуваність], + zh: [可预见性], + )), ), "design-patterns/hal/gpio": ( content: include "design-patterns/hal/gpio.typ", @@ -296,77 +333,85 @@ ), "c-tips/index": ( content: include "c-tips/index.typ", - title: if lang == "en" [Tips for embedded C developers] - else if lang == "de" [Tipps für Entwickler im Bereich Embedded-C] - else if lang == "ja" [組込みC開発者へのヒント] - else if lang == "uk" [Поради для розробників мовою C] - else if lang == "zh" [给嵌入式C开发者的贴士] - else { todo } + title: tr(( + en: [Tips for embedded C developers], + de: [Tipps für Entwickler im Bereich Embedded-C], + ja: [組込みC開発者へのヒント], + uk: [Поради для розробників мовою C], + zh: [给嵌入式C开发者的贴士], + )), ), "interoperability/index": ( content: include "interoperability/index.typ", - title: if lang == "en" [Interoperability] - else if lang == "de" [Interoperabilität] - else if lang == "ja" [相互運用性] - else if lang == "uk" [Сумісність] - else if lang == "zh" [互操性] - else { todo }, + title: tr(( + en: [Interoperability], + de: [Interoperabilität], + ja: [相互運用性], + uk: [Сумісність], + zh: [互操性], + )), sub: ( "interoperability/c-with-rust": ( content: include "interoperability/c-with-rust.typ", - title: if lang == "en" [A little C with your Rust] - else if lang == "de" [Ein bisschen C zu Ihrem Rust] - else if lang == "ja" [Rustと少しのC] - else if lang == "uk" [Трошки C в вашому Rust] - else if lang == "zh" [使用C的Rust] - else { todo } + title: tr(( + en: [A little C with your Rust], + de: [Ein bisschen C zu Ihrem Rust], + ja: [Rustと少しのC], + uk: [Трошки C в вашому Rust], + zh: [使用C的Rust], + )), ), "interoperability/rust-with-c": ( content: include "interoperability/rust-with-c.typ", - title: if lang == "en" [A little Rust with your C] - else if lang == "de" [Ein bisschen Rust zu Ihrem C] - else if lang == "ja" [Cと少しのRust] - else if lang == "uk" [Трошки Rust в вашому C] - else if lang == "zh" [使用Rust的C] - else { todo } + title: tr(( + en: [A little Rust with your C], + de: [Ein bisschen Rust zu Ihrem C], + ja: [Cと少しのRust], + uk: [Трошки Rust в вашому C], + zh: [使用Rust的C], + )), ) ) ), "unsorted/index": ( content: include "unsorted/index.typ", - title: if lang == "en" [Unsorted topics] - else if lang == "de" [Unsortierte Themen] - else if lang == "ja" [未分類のトピック] - else if lang == "uk" [Невідсортовані теми] - else if lang == "zh" [没有排序的主题] - else { todo }, + title: tr(( + en: [Unsorted topics], + de: [Unsortierte Themen], + ja: [未分類のトピック], + uk: [Невідсортовані теми], + zh: [没有排序的主题], + )), sub: ( "unsorted/speed-vs-size": ( content: include "unsorted/speed-vs-size.typ", - title: if lang == "en" [Optimizations: The speed size tradeoff] - else if lang == "de" [Optimierungen: Der Kompromiss zwischen Geschwindigkeit und Größe] - else if lang == "ja" [最適化: 速度とサイズのトレードオフ] - else if lang == "uk" [Оптимізація: компроміс між швидкістю та розміром] - else if lang == "zh" [优化: 速度与大小间的博弈] - else { todo } + title: tr(( + en: [Optimizations: The speed size tradeoff], + de: [Optimierungen: Der Kompromiss zwischen Geschwindigkeit und Größe], + ja: [最適化: 速度とサイズのトレードオフ], + uk: [Оптимізація: компроміс між швидкістю та розміром], + zh: [优化: 速度与大小间的博弈], + )), ), "unsorted/math": ( content: include "unsorted/math.typ", - title: if lang == "en" [Performing Math Functionality] - else if lang == "de" [Ausführung mathematischer Funktionen] - else if lang == "uk" [Виконання математики] - else if lang == "zh" [执行数学运算] - else { todo } + title: tr(( + en: [Performing Math Functionality], + de: [Ausführung mathematischer Funktionen], + uk: [Виконання математики], + zh: [执行数学运算], + )), ) ) ), "appendix/glossary": ( content: include "appendix/glossary.typ", - title: if lang == "en" [Appendix A: Glossary] - else if lang == "de" [Anhang A: Glossar] - else if lang == "uk" [Додаток А: Глосарій] - else if lang == "zh" [附录A: 词汇表] - else { todo } + title: tr(( + en: [Appendix A: Glossary], + de: [Anhang A: Glossar], + uk: [Додаток А: Глосарій], + zh: [附录A: 词汇表], + )), ) ) diff --git a/typ/c-tips/index.typ b/typ/c-tips/index.typ index a236bb1e..3181b84c 100644 --- a/typ/c-tips/index.typ +++ b/typ/c-tips/index.typ @@ -1,69 +1,82 @@ #import "../config.typ": * -#h1(if lang == "en" [Tips for embedded C developers] - else if lang == "de" [Tipps für Embedded-C-Entwickler] - else if lang == "zh" [给嵌入式C开发者的贴士] - else { todo }) - -#if lang == "en" [ +#h1(tr(( + en: [Tips for embedded C developers], + de: [Tipps für Embedded-C-Entwickler], + zh: [给嵌入式C开发者的贴士], +))) + +#tr(( +en: [ This chapter collects a variety of tips that might be useful to experienced embedded C developers looking to start writing Rust. It will especially highlight how things you might already be used to in C are different in Rust. -] else if lang == "de" [ +], +de: [ Dieses Kapitel versammelt eine Reihe von Tipps, die für erfahrene Embedded-C-Entwickler nützlich sein können, die mit der Programmierung in Rust beginnen möchten. Dabei wird insbesondere hervorgehoben, wie sich Dinge, die man aus C bereits kennt, in Rust unterscheiden. -] else if lang == "zh" [ +], +zh: [ 这个章节收集了可能对于刚开始编写Rust的,有经验的嵌入式C开发者来说,有用的各种各样的贴士。它将解释你在C中可能已经用到的那些东西与Rust中的有何不同。 -] else { todo } +])) -== #(if lang == "en" [Preprocessor] - else if lang == "de" [Präprozessor] - else if lang == "zh" [预处理器] - else { todo }) +== #tr(( + en: [Preprocessor], + de: [Präprozessor], + zh: [预处理器], +)) -#if lang == "en" [ +#tr(( +en: [ In embedded C it is very common to use the preprocessor for a variety of purposes, such as: - Compile-time selection of code blocks with `#ifdef` - Compile-time array sizes and computations - Macros to simplify common patterns (to avoid function call overhead) -] else if lang == "de" [ +], +de: [ In der Embedded-C-Programmierung ist es sehr üblich, den Präprozessor für eine Vielzahl von Zwecken zu nutzen, wie zum Beispiel: - Auswahl von Codeblöcken zur Kompilierzeit mittels `#ifdef` - Zur Kompilierzeit festgelegte Array-Größen und Berechnungen - Makros zur Vereinfachung häufiger Muster (um den Overhead von Funktionsaufrufen zu vermeiden) -] else if lang == "zh" [ +], +zh: [ 在嵌入式C中,为了各种各样的目的使用预处理器是很常见的,比如: - 使用`#ifdef`编译时选择代码块 - 编译时的数组大小和计算 - 用来简化常见的模式的宏(避免调用函数的开销) -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ In Rust there is no preprocessor, and so many of these use cases are addressed differently. In the rest of this section we cover various alternatives to using the preprocessor. -] else if lang == "de" [ +], +de: [ In Rust gibt es keinen Präprozessor, weshalb viele dieser Anwendungsfälle anders gelöst werden. Im weiteren Verlauf dieses Abschnitts behandeln wir verschiedene Alternativen zur Verwendung des Präprozessors. -] else if lang == "zh" [ +], +zh: [ 在Rust中没有预处理器,所以许多案例有不同的处理方法。本章节剩下的部分,我们将介绍各种替代预处理器的方法。 -] else { todo } +])) -=== #(if lang == "en" [Compile-Time Code Selection] - else if lang == "de" [Code-Auswahl zur Kompilierzeit] - else if lang == "zh" [编译时的代码选择] - else { todo }) +=== #tr(( + en: [Compile-Time Code Selection], + de: [Code-Auswahl zur Kompilierzeit], + zh: [编译时的代码选择], +)) #let url_features = "https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section" -#if lang == "en" [ +#tr(( +en: [ The closest match to `#ifdef ... #endif` in Rust are #link(url_features)[Cargo features]. These are a little more formal than the C preprocessor: all possible @@ -71,7 +84,8 @@ off. Features are turned on when you list a crate as a dependency, and are additive: if any crate in your dependency tree enables a feature for another crate, that feature will be enabled for all users of that crate. -] else if lang == "de" [ +], +de: [ Das Äquivalent zu `#ifdef ... #endif` in Rust sind #link(url_features)[Cargo-Features]. Diese sind etwas formaler als der C-Präprozessor: Alle möglichen @@ -80,28 +94,32 @@ man ein Crate als Abhängigkeit angibt; sie sind zudem additiv: Wenn irgendein Crate im Abhängigkeitsbaum ein Feature für ein anderes Crate aktiviert, ist dieses Feature für alle Nutzer jenes Crates aktiviert. -] else if lang == "zh" [ +], +zh: [ Rust中最接近`#ifdef ... #endif`的是#link(url_features)[Cargo features]。这些比C预处理器更正式一点: 每个crate显式列举的,所有可能的features只能是关了的或者打开了的。当你把一个crate列为依赖项时,Features被打开,且是可添加的:如果你依赖树中的任何crate为另一个crate打开了一个feature,那么这个feature将为所有使用那个crate的用户而打开。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ For example, you might have a crate which provides a library of signal processing primitives. Each one might take some extra time to compile or declare some large table of constants which you'd like to avoid. You could declare a Cargo feature for each component in your `Cargo.toml`: -] else if lang == "de" [ +], +de: [ Angenommen, du hast ein Crate, das eine Bibliothek von Grundbausteinen für die Signalverarbeitung bereitstellt. Jeder dieser Bausteine ​​könnte zusätzliche Kompilierzeit beanspruchen oder eine umfangreiche Tabelle von Konstanten definieren, die du gerne vermeiden möchtest. Du könntest für jede Komponente in deiner `Cargo.toml` ein Cargo-Feature definieren: -] else if lang == "zh" [ +], +zh: [ 比如,你可能有一个crate,其提供一个信号处理的基本类型库(library of signal processing primitives)。每个基本类型可能带来一些额外的时间去编译大量的常量,你想要避开这些常量。你可以为你的`Cargo.toml`中每个组件声明一个Cargo feature。 -] else { todo } +])) ```toml [features] @@ -109,24 +127,25 @@ FIR = [] IIR = [] ``` -#if lang == "en" [ +#tr(( +en: [ Then, in your code, use `#[cfg(feature="FIR")]` to control what is included. -] else if lang == "de" [ +], +de: [ Verwenden Sie dann in Ihrem Code `#[cfg(feature="FIR")]`, um zu steuern, was einbezogen wird. -] else if lang == "zh" [ +], +zh: [ 然后,在你的代码中,使用`#[cfg(feature="FIR")]`去控制要包含什么东西。 -] else { todo } +])) #raw(block: true, lang: "rust", -"/// " + if lang == "en" { - "In your top-level lib.rs" - } else if lang == "de" { - "In deiner lib.rs auf oberster Ebene" - } else if lang == "zh" { - "在你的顶层的lib.rs中" - } else { todos } + " +"/// " + ts(( + en: "In your top-level lib.rs", + de: "In deiner lib.rs auf oberster Ebene", + zh: "在你的顶层的lib.rs中" + )) + " #[cfg(feature=\"FIR\")] pub mod fir; @@ -135,39 +154,46 @@ pub mod fir; pub mod iir; ") -#if lang == "en" [ +#tr(( +en: [ You can similarly include code blocks only if a feature is _not_ enabled, or if any combination of features are or are not enabled. -] else if lang == "de" [ +], +de: [ Sie können Codeblöcke analog dazu nur dann einfügen, wenn eine Funktion _nicht_ aktiviert ist oder wenn eine beliebige Kombination von Funktionen aktiviert oder deaktiviert ist. -] else if lang == "zh" [ +], +zh: [ 同样地,你可以控制,只有当某个feature _没有_ 被打开时,包含代码块,或者某些features的组合被打开或者被关闭时。 -] else { todo } +])) #let url_conditional = "https://doc.rust-lang.org/reference/conditional-compilation.html" -#if lang == "en" [ +#tr(( +en: [ Additionally, Rust provides a number of automatically-set conditions you can use, such as `target_arch` to select different code based on architecture. For full details of the conditional compilation support, refer to the #link(url_conditional)[conditional compilation] chapter of the Rust reference. -] else if lang == "de" [ +], +de: [ Darüber hinaus bietet Rust eine Reihe automatisch gesetzter Bedingungen, die Sie verwenden können, beispielsweise `target_arch`, um je nach Architektur unterschiedlichen Code auszuwählen. Ausführliche Informationen zur bedingten Kompilierung finden Sie im Kapitel #link(url_conditional)[Bedingte Kompilierung] der Rust-Referenz. -] else if lang == "zh" [ +], +zh: [ 另外,Rust提供了许多可以使用的自动配置了的条件,比如`target_arch`用来选择不同的代码所基于的架构。对于条件编译的全部细节,可以参看the Rust reference的#link(url_conditional)[conditional compilation]章节。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ The conditional compilation will only apply to the next statement or block. If a block can not be used in the current scope then the `cfg` attribute will need to be used multiple times. It's worth noting that @@ -175,7 +201,8 @@ pub mod iir; the compiler to remove dead code when optimising: it's simpler for you and your users, and in general the compiler will do a good job of removing unused code. -] else if lang == "de" [ +], +de: [ Die bedingte Kompilierung bezieht sich nur auf die unmittelbar folgende Anweisung oder den folgenden Block. Kann im aktuellen Gültigkeitsbereich kein Block verwendet werden, muss das `cfg`-Attribut mehrfach eingesetzt @@ -184,30 +211,35 @@ pub mod iir; mehr benötigten Code („Dead Code") entfernen zu lassen: Dies ist für Sie und Ihre Nutzer einfacher, und der Compiler leistet im Allgemeinen gute Arbeit beim Entfernen von ungenutztem Code. -] else if lang == "zh" [ +], +zh: [ 条件编译将只应用于下一条语句或者块。如果一个块不能在现在的作用域中被使用,那么`cfg`属性将需要被多次使用。值得注意的是大多数时间,仅是包含所有的代码而让编译器在优化时去删除死代码(dead code)更好,通常,在移除不使用的代码方面的工作,编译器做得很好。 -] else { todo } +])) -=== #(if lang == "en" [Compile-Time Sizes and Computation] - else if lang == "de" [Größen und Berechnungen zur Kompilierzeit] - else if lang == "zh" [编译时大小和计算] - else { todo }) +=== #tr(( + en: [Compile-Time Sizes and Computation], + de: [Größen und Berechnungen zur Kompilierzeit], + zh: [编译时大小和计算], +)) -#if lang == "en" [ +#tr(( +en: [ Rust supports `const fn`, functions which are guaranteed to be evaluable at compile-time and can therefore be used where constants are required, such as in the size of arrays. This can be used alongside features mentioned above, for example: -] else if lang == "de" [ +], +de: [ Rust unterstützt `const fn` -- Funktionen, die garantiert zur Kompilierzeit ausgewertet werden können und sich daher überall dort einsetzen lassen, wo Konstanten erforderlich sind, etwa bei der Größe von Arrays. Dies lässt sich mit den zuvor genannten Funktionen kombinieren, zum Beispiel: -] else if lang == "zh" [ +], +zh: [ Rust支持`const fn`,`const fn`是在编译时可以被计算的函数,因此可以被用在需要常量的地方,比如在数组的大小中。这个能与上述的features一起使用,比如: -] else { todo } +])) ```rust const fn array_size() -> usize { @@ -220,29 +252,34 @@ const fn array_size() -> usize { static BUF: [u32; array_size()] = [0u32; array_size()]; ``` -#if lang == "en" [ +#tr(( +en: [ These are new to stable Rust as of 1.31, so documentation is still sparse. The functionality available to `const fn` is also very limited at the time of writing; in future Rust releases it is expected to expand on what is permitted in a `const fn`. -] else if lang == "de" [ +], +de: [ Diese Funktionen sind seit Version 1.31 in Stable Rust verfügbar, weshalb die Dokumentation noch spärlich ist. Auch der für `const fn` verfügbare Funktionsumfang ist zum Zeitpunkt der Erstellung dieses Textes stark eingeschränkt; es ist jedoch zu erwarten, dass die in einer `const fn` zulässigen Operationen in künftigen Rust-Versionen erweitert werden. -] else if lang == "zh" [ +], +zh: [ 这些对于stable版本的Rust来说是新的特性,从1.31开始引入,因此文档依然很少。在写这篇文章的时候`const fn`可用的功能也非常有限; 在未来的Rust release版本中,我们可以期望`const fn`将带来更多的功能。 -] else { todo } +])) -=== #(if lang == "en" [Macros] - else if lang == "de" [Makros] - else if lang == "zh" [宏] - else { todo }) +=== #tr(( + en: [Macros], + de: [Makros], + zh: [宏], +)) #let url_macros = "https://doc.rust-lang.org/book/ch19-06-macros.html" -#if lang == "en" [ +#tr(( +en: [ Rust provides an extremely powerful #link(url_macros)[macro system]. While the C preprocessor operates almost directly on the text of your source code, the Rust macro system operates at a higher level. There are @@ -252,7 +289,8 @@ static BUF: [u32; array_size()] = [0u32; array_size()]; statement, item, or pattern. Procedural macros are more complex but permit extremely powerful additions to the Rust language: they can transform arbitrary Rust syntax into new Rust syntax. -] else if lang == "de" [ +], +de: [ Rust bietet ein äußerst leistungsfähiges #link(url_macros)[Makrosystem]. Während der C-Präprozessor fast direkt auf dem Text des Quellcodes arbeitet, operiert das Rust-Makrosystem auf einer höheren Ebene. Es gibt @@ -263,14 +301,16 @@ static BUF: [u32; array_size()] = [0u32; array_size()]; expandiert werden. Prozedurale Makros sind komplexer, ermöglichen jedoch äußerst leistungsfähige Erweiterungen der Sprache Rust: Sie können beliebige Rust-Syntax in neue Rust-Syntax umwandeln. -] else if lang == "zh" [ +], +zh: [ Rust提供一个极度强大的#link(url_macros)[宏系统]。虽然C预处理器几乎直接在你的源代码之上进行操作,但是Rust宏系统可以在一个更高的级别上操作。存在两种Rust宏: _声明宏_ 和 _过程宏_ 。前者更简单也最常见; 它们看起来像是函数调用,且能扩展成一个完整的表达式,语句,项,或者模式。过程宏更复杂但是却能让Rust更强大: 它们可以把任一条Rust语法变成一个新的Rust语法。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ In general, where you might have used a C preprocessor macro, you probably want to see if a macro-by-example can do the job instead. They can be defined in your crate and easily used by your own crate or @@ -278,7 +318,8 @@ static BUF: [u32; array_size()] = [0u32; array_size()]; complete expressions, statements, items, or patterns, some use cases of C preprocessor macros will not work, for example a macro that expands to part of a variable name or an incomplete set of items in a list. -] else if lang == "de" [ +], +de: [ Wenn Sie normalerweise ein C-Präprozessor-Makro verwenden würden, sollten Sie im Allgemeinen prüfen, ob stattdessen ein „Macro-by-Example" (Makro-durch-Beispiel) die Aufgabe erfüllen kann. Solche Makros lassen @@ -288,12 +329,14 @@ static BUF: [u32; array_size()] = [0u32; array_size()]; expandieren müssen; bestimmte Anwendungsfälle von C-Präprozessor-Makros funktionieren daher nicht -- etwa Makros, die nur einen Teil eines Variablennamens oder unvollständige Listenelemente erzeugen. -] else if lang == "zh" [ +], +zh: [ 通常,你可能想知道在那些使用一个C预处理器宏的地方,能否使用一个声明宏做同样的工作。你可以在crate中定义它们,且在你的crate中轻松使用它们或者导出给其他人用。但是请注意,因为它们必须扩展成完整的表达式,语句,项或者模式,因此C预处理器宏的某些用例没法用,比如可以扩展成一个变量名的一部分的宏或者可以把列表中的项扩展成不完整的集合的宏。 -] else { todo } +])) #let url_inline = "https://doc.rust-lang.org/reference/attributes.html#inline-attribute" -#if lang == "en" [ +#tr(( +en: [ As with Cargo features, it is worth considering if you even need the macro. In many cases a regular function is easier to understand and will be inlined to the same code as a macro. The `#[inline]` and @@ -302,7 +345,8 @@ static BUF: [u32; array_size()] = [0u32; array_size()]; taken here as well --- the compiler will automatically inline functions from the same crate where appropriate, so forcing it to do so inappropriately might actually lead to decreased performance. -] else if lang == "de" [ +], +de: [ Wie bei Cargo-Features lohnt es sich auch hier zu überlegen, ob das Makro überhaupt erforderlich ist. Oftmals ist eine gewöhnliche Funktion leichter verständlich und wird vom Compiler zu demselben Maschinencode @@ -312,32 +356,39 @@ static BUF: [u32; array_size()] = [0u32; array_size()]; Vorsicht geboten ist: Der Compiler inlined Funktionen aus demselben Crate ohnehin automatisch, wenn dies sinnvoll ist; ein erzwungenes Inlining in ungeeigneten Fällen kann die Leistung sogar verschlechtern. -] else if lang == "zh" [ +], +zh: [ 和Cargo features一样,值得考虑下你是否真的需要宏。在一些例子中一个常规的函数更容易被理解,它也能被内联成和一个和宏一样的代码。`#[inline]`和`#[inline(always)]` #link(url_inline)[attributes] 能让你更深入控制这个过程,这里也要小心 - 编译器会从同一个crate的恰当的地方自动地内联函数,因此不恰当地强迫它内联函数实际可能会导致性能下降。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Explaining the entire Rust macro system is out of scope for this tips page, so you are encouraged to consult the Rust documentation for full details. -] else if lang == "de" [ +], +de: [ Eine Erläuterung des gesamten Rust-Makrosystems würde den Rahmen dieser Tipps-Seite sprengen; für alle Einzelheiten sei daher auf die Rust-Dokumentation verwiesen. -] else if lang == "zh" [ +], +zh: [ 研究完整的Rust宏系统超出了本节内容,因此我们鼓励你去查阅Rust文档了解完整的细节。 -] else { todo } +])) -== #(if lang in ("en", "de") [Build System] - else if lang == "zh" [编译系统] - else { todo }) +== #tr(( + en: [Build System], + de: [Build System], + zh: [编译系统], +)) #let url_build_scripts = "https://doc.rust-lang.org/cargo/reference/build-scripts.html" -#if lang == "en" [ +#tr(( +en: [ Most Rust crates are built using Cargo (although it is not required). This takes care of many difficult problems with traditional build systems. However, you may wish to customise the build process. Cargo @@ -345,7 +396,8 @@ static BUF: [u32; array_size()] = [0u32; array_size()]; #link(url_build_scripts)[`build.rs` scripts] for this purpose. They are Rust scripts which can interact with the Cargo build system as required. -] else if lang == "de" [ +], +de: [ Die meisten Rust-Crates werden mit Cargo erstellt (auch wenn dies nicht zwingend erforderlich ist). Cargo löst dabei viele der schwierigen Probleme, die mit herkömmlichen Build-Systemen verbunden sind. Dennoch @@ -353,12 +405,14 @@ static BUF: [u32; array_size()] = [0u32; array_size()]; stellt Cargo #link(url_build_scripts)[`build.rs`-Skripte] bereit. Dabei handelt es sich um Rust-Skripte, die bei Bedarf mit dem Cargo-Build-System interagieren können. -] else if lang == "zh" [ +], +zh: [ 大多数Rust crates使用Cargo编译 (即使这不是必须的)。这解决了传统编译系统带来的许多难题。然而,你可能希望自定义编译过程。为了实现这个目的,Cargo提供了#link(url_build_scripts)[`build.rs`脚本]。它们是可以根据需要与Cargo编译系统进行交互的Rust脚本。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Common use cases for build scripts include: - provide build-time information, for example statically embedding the build date or Git commit hash into your executable @@ -366,7 +420,8 @@ static BUF: [u32; array_size()] = [0u32; array_size()]; or other logic - change the Cargo build configuration - add extra static libraries to link against -] else if lang == "de" [ +], +de: [ Zu den häufigen Anwendungsfällen für Build-Skripte gehören: - Informationen zum Build-Zeitpunkt bereitstellen, zum Beispiel durch das statische Einbetten des Build-Datums oder des Git-Commit-Hashs in @@ -375,75 +430,90 @@ static BUF: [u32; array_size()] = [0u32; array_size()]; Funktionen oder anderer Logik. - die Cargo-Build-Konfiguration ändern - Zusätzliche statische Bibliotheken für den Linkvorgang hinzufügen -] else if lang == "zh" [ +], +zh: [ 与编译脚本有关的常见用例包括: - 提供编译时信息,比如静态嵌入编译日期或者Git commit hash进你的可执行文件中 - 根据被选择的features或者其它逻辑在编译时生成链接脚本 - 改变Cargo的编译配置 - 添加额外的静态链接库以进行链接 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ At present there is no support for post-build scripts, which you might traditionally have used for tasks like automatic generation of binaries from the build objects or printing build information. -] else if lang == "de" [ +], +de: [ Derzeit gibt es keine Unterstützung für Post-Build-Skripte, wie man sie üblicherweise für Aufgaben wie die automatische Erstellung von Binärdateien aus den Build-Objekten oder die Ausgabe von Build-Informationen verwendet hat. -] else if lang == "zh" [ +], +zh: [ 现在还不支持post-build脚本,通常将它用于像是从编译的对象自动生生成二进制文件或者打印编译信息这类任务中。 -] else { todo } +])) -=== #(if lang == "en" [Cross-Compiling] - else if lang == "de" [Cross-Kompilierung] - else if lang == "zh" [交叉编译] - else { todo }) +=== #tr(( + en: [Cross-Compiling], + de: [Cross-Kompilierung], + zh: [交叉编译], +)) -#if lang == "en" [ +#tr(( +en: [ Using Cargo for your build system also simplifies cross-compiling. In most cases it suffices to tell Cargo `--target thumbv6m-none-eabi` and find a suitable executable in `target/thumbv6m-none-eabi/debug/myapp`. -] else if lang == "de" [ +], +de: [ Die Verwendung von Cargo als Build-System vereinfacht auch die Cross-Kompilierung. In den meisten Fällen genügt es, Cargo die Option `--target thumbv6m-none-eabi` mitzugeben und die entsprechende ausführbare Datei unter `target/thumbv6m-none-eabi/debug/myapp` zu finden. -] else if lang == "zh" [ +], +zh: [ 为你的编译系统使用Cargo也能简化交叉编译。在大多数例子里,告诉Cargo `--target thumbv6m-none-eabi`就行了,可以在`target/thumbv6m-none-eabi/debug/myapp`中找到一个合适的可执行文件。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ For platforms not natively supported by Rust, you will need to build `libcore` for that target yourself. On such platforms, #link("https://github.com/japaric/xargo")[Xargo] can be used as a stand-in for Cargo which automatically builds `libcore` for you. -] else if lang == "de" [ +], +de: [ Für Plattformen, die nicht nativ von Rust unterstützt werden, müssen Sie `libcore` für das jeweilige Zielsystem selbst kompilieren. Auf solchen Plattformen lässt sich #link("https://github.com/japaric/xargo")[Xargo] als Ersatz für Cargo verwenden, da es `libcore` automatisch für Sie erstellt. -] else if lang == "zh" [ +], +zh: [ 对于那些并不是Rust原生支持的平台,将需要自己为那个目标平台编译`libcore`。遇到这样的平台,#link("https://github.com/japaric/xargo")[Xargo]可以作为Cargo的替代来使用,它可以自动地为你编译`libcore`。 -] else { todo } +])) -== #(if lang == "en" [Iterators vs Array Access] - else if lang == "de" [Iteratoren vs.~Array-Zugriff] - else if lang == "zh" [迭代器与数组访问] - else { todo }) +== #tr(( + en: [Iterators vs Array Access], + de: [Iteratoren vs.~Array-Zugriff], + zh: [迭代器与数组访问], +)) -#if lang == "en" [ +#tr(( +en: [ In C you are probably used to accessing arrays directly by their index: -] else if lang == "de" [ +], +de: [ In C sind Sie es wahrscheinlich gewohnt, direkt über den Index auf Arrays zuzugreifen: -] else if lang == "zh" [ +], +zh: [ 在C中,你可能习惯于通过索引直接访问数组: -] else { todo } +])) ```c int16_t arr[16]; @@ -453,31 +523,37 @@ for(i=0; i)[Concurrency chapter]). -] else if lang == "de" [ +], +de: [ Es ist selten erforderlich, diese Funktionen direkt im eigenen Code zu verwenden, da sie üblicherweise von höherwertigen Bibliotheken für Sie übernommen werden. Bei speicherabgebildeten Peripheriekomponenten @@ -781,44 +878,52 @@ fn driver() { volatilen Zugriff automatisch, während für Nebenläufigkeits-Primitive bessere Abstraktionen zur Verfügung stehen (siehe das Kapitel #link()[Nebenläufigkeit]). -] else if lang == "zh" [ +], +zh: [ 在你的代码中直接使用这些函数是很少见的,因为它们通常由更高级的库封装起来为你提供服务。对于存储映射的外设,提供外设访问的crates将自动实现volatile访问,而对于并发的基本类型,存在更好的抽象可用。(看#link()[并发章节]) -] else { todo } +])) -== #(if lang == "en" [Packed and Aligned Types] - else if lang == "de" [Gepackte und ausgerichtete Datentypen] - else if lang == "zh" [填充和对齐类型] - else { todo }) +== #tr(( + en: [Packed and Aligned Types], + de: [Gepackte und ausgerichtete Datentypen], + zh: [填充和对齐类型], +)) -#if lang == "en" [ +#tr(( +en: [ In embedded C it is common to tell the compiler a variable must have a certain alignment or a struct must be packed rather than aligned, usually to meet specific hardware or protocol requirements. -] else if lang == "de" [ +], +de: [ In der Embedded-Programmierung mit C ist es üblich, dem Compiler vorzugeben, dass eine Variable eine bestimmte Ausrichtung (Alignment) aufweisen oder eine Struktur „gepackt" (packed) statt ausgerichtet sein muss -- meist, um spezifische Hardware- oder Protokollanforderungen zu erfüllen. -] else if lang == "zh" [ +], +zh: [ 在嵌入式C中,告诉编译器一个变量必须遵守某个对齐或者一个结构体必须被填充而不是对齐,是很常见的行为,通常是为了满足特定的硬件或者协议要求。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ In Rust this is controlled by the `repr` attribute on a struct or union. The default representation provides no guarantees of layout, so should not be used for code that interoperates with hardware or C. The compiler may re-order struct members or insert padding and the behaviour may change with future versions of Rust. -] else if lang == "de" [ +], +de: [ In Rust wird dies über das `repr`-Attribut an einer `struct` oder `union` gesteuert. Die Standarddarstellung macht keine Zusagen über das Speicherlayout und sollte daher nicht für Code verwendet werden, der mit Hardware oder C interagiert. Der Compiler kann die Reihenfolge der Strukturmitglieder ändern oder Füllbytes (Padding) einfügen, und das Verhalten kann sich in zukünftigen Rust-Versionen ändern. -] else if lang == "zh" [ +], +zh: [ 在Rust中,这由一个结构体或者联合体上的`repr`属性来控制。默认的表示(representation)不保障布局,因此不应该被用于与硬件或者C互用的代码。编译器可能会对结构体成员重新排序或者插入填充,且这种行为可能在未来的Rust版本中改变。 -] else { todo } +])) #raw(block: true, lang: "rust", "struct Foo { @@ -833,24 +938,25 @@ fn main() { } // 0x7ffecb3511d0 0x7ffecb3511d4 0x7ffecb3511d2 -// " + if lang == "en" { - "Note ordering has been changed to x, z, y to improve packing." - } else if lang == "de" { - "Die Reihenfolge der Noten wurde auf x, z, y geaendert, um die Packdichte zu -// verbessern." - } else if lang == "zh" { - "0x7ffecb3511d0 0x7ffecb3511d4 0x7ffecb3511d2 +// " + ts(( + en: "Note ordering has been changed to x, z, y to improve packing.", + de: "Die Reihenfolge der Noten wurde auf x, z, y geaendert, um die Packdichte zu +// verbessern.", + zh: "0x7ffecb3511d0 0x7ffecb3511d4 0x7ffecb3511d2 // 注意为了改进填充,顺序已经被变成了x, z, y" - } else { todos } + " + )) + " ") -#if lang == "en" [ +#tr(( +en: [ To ensure layouts that are interoperable with C, use `repr(C)`: -] else if lang == "de" [ +], +de: [ Um Layouts zu gewährleisten, die mit C interoperabel sind, verwenden Sie `repr(C)`: -] else if lang == "zh" [ +], +zh: [ 使用`repr(C)`可以确保布局可以与C互用。 -] else { todo } +])) #raw(block: true, lang: "rust", "#[repr(C)] @@ -866,28 +972,29 @@ fn main() { } // 0x7fffd0d84c60 0x7fffd0d84c62 0x7fffd0d84c64 -// " + if lang == "en" { - "Ordering is preserved and the layout will not change over time. -// `z` is two-byte aligned so a byte of padding exists between `y` and `z`." - } else if lang == "de" { - "Die Reihenfolge bleibt erhalten und das Layout veraendert sich im Laufe der +// " + ts(( + en: "Ordering is preserved and the layout will not change over time. +// `z` is two-byte aligned so a byte of padding exists between `y` and `z`.", + de: "Die Reihenfolge bleibt erhalten und das Layout veraendert sich im Laufe der // Zeit nicht. // `z` ist auf Zwei-Byte-Grenzen ausgerichtet, sodass sich zwischen `y` und `z` -// ein Padding-Byte befindet." - } else if lang == "zh" { - "0x7fffd0d84c60 0x7fffd0d84c62 0x7fffd0d84c64 +// ein Padding-Byte befindet.", + zh: "0x7fffd0d84c60 0x7fffd0d84c62 0x7fffd0d84c64 // 顺序被保留了,布局将不会随着时间而改变 // `z`是两个字节对齐,因此在`y`和`z`之间填充了一个字节。" - } else { todos } + " + )) + " ") -#if lang == "en" [ +#tr(( +en: [ To ensure a packed representation, use `repr(packed)`: -] else if lang == "de" [ +], +de: [ Um eine kompakte Darstellung zu gewährleisten, verwenden Sie `repr(packed)`: -] else if lang == "zh" [ +], +zh: [ 使用`repr(packed)`去确保表示(representation)被填充了: -] else { todo } +])) #raw(block: true, lang: "rust", "#[repr(packed)] @@ -899,19 +1006,17 @@ struct Foo { fn main() { let v = Foo { x: 0, y: 0, z: 0 }; - // " + if lang == "en" { - "References must always be aligned, so to check the addresses of the + // " + ts(( + en: "References must always be aligned, so to check the addresses of the // struct's fields, we use `std::ptr::addr_of!()` to get a raw pointer - // instead of just printing `&v.x`." - } else if lang == "de" { - "Referenzen muessen stets korrekt ausgerichtet sein; um also die + // instead of just printing `&v.x`.", + de: "Referenzen muessen stets korrekt ausgerichtet sein; um also die // Adressen der Struct-Felder zu ueberpruefen, verwenden wir // `std::ptr::addr_of!()`, um einen Rohzeiger (Raw Pointer) zu erhalten, - // anstatt einfach `&v.x` auszugeben." - } else if lang == "zh" { - "引用必须总是对齐的,因此为了检查结构体字段的地址,我们使用 + // anstatt einfach `&v.x` auszugeben.", + zh: "引用必须总是对齐的,因此为了检查结构体字段的地址,我们使用 // `std::ptr::addr_of!()`去获取一个裸指针而不仅是打印`&v.x`" - } else { todos } + " + )) + " let px = std::ptr::addr_of!(v.x); let py = std::ptr::addr_of!(v.y); let pz = std::ptr::addr_of!(v.z); @@ -919,36 +1024,40 @@ fn main() { } // 0x7ffd33598490 0x7ffd33598492 0x7ffd33598493 -// " + if lang == "en" { - "No padding has been inserted between `y` and `z`, so now `z` is unaligned." - } else if lang == "de" { - "Zwischen `y` und `z` wurde kein Padding eingefuegt, daher ist `z` nun -// nicht ausgerichtet." - } else if lang == "zh" { - "0x7ffd33598490 0x7ffd33598492 0x7ffd33598493 +// " + ts(( + en: "No padding has been inserted between `y` and `z`, so now `z` is unaligned.", + de: "Zwischen `y` und `z` wurde kein Padding eingefuegt, daher ist `z` nun +// nicht ausgerichtet.", + zh: "0x7ffd33598490 0x7ffd33598492 0x7ffd33598493 // 在`y`和`z`没有填充被插入,因此现在`z`没有被对齐。" - } else { todos } + " + )) + " ") -#if lang == "en" [ +#tr(( +en: [ Note that using `repr(packed)` also sets the alignment of the type to `1`. -] else if lang == "de" [ +], +de: [ Beachten Sie, dass die Verwendung von `repr(packed)` die Ausrichtung des Typs ebenfalls auf `1` setzt. -] else if lang == "zh" [ +], +zh: [ 注意使用`repr(packed)`也会将类型的对齐设置成`1` 。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Finally, to specify a specific alignment, use `repr(align(n))`, where `n` is the number of bytes to align to (and must be a power of two): -] else if lang == "de" [ +], +de: [ Um eine bestimmte Ausrichtung festzulegen, verwenden Sie schließlich `repr(align(n))`, wobei `n` die Anzahl der auszurichtenden Bytes ist (und eine Zweierpotenz sein muss). -] else if lang == "zh" [ +], +zh: [ 最后,为了指定一个特定的对齐,可以使用`repr(align(n))`,`n`是要对齐的字节数(必须是2的幂): -] else { todo } +])) #raw(block: true, lang: "rust", "#[repr(C)] @@ -968,75 +1077,83 @@ fn main() { // 0x7ffec909a000 0x7ffec909a002 0x7ffec909a004 // 0x7ffec909b000 0x7ffec909b002 0x7ffec909b004 -// " + if lang == "en" { - "The two instances `u` and `v` have been placed on 4096-byte alignments, -// evidenced by the `000` at the end of their addresses." - } else if lang == "de" { - "Die beiden Instanzen `u` und `v` wurden an 4096-Byte-Grenzen ausgerichtet, -// was an den `000` am Ende ihrer Adressen erkennbar ist." - } else if lang == "zh" { - "`u`和`v`两个实例已经被放置在4096字节的对齐上。 +// " + ts(( + en: "The two instances `u` and `v` have been placed on 4096-byte alignments, +// evidenced by the `000` at the end of their addresses.", + de: "Die beiden Instanzen `u` und `v` wurden an 4096-Byte-Grenzen ausgerichtet, +// was an den `000` am Ende ihrer Adressen erkennbar ist.", + zh: "`u`和`v`两个实例已经被放置在4096字节的对齐上。 // 它们地址结尾处的`000`证明了这件事。" - } else { todos } + " + )) + " ") -#if lang == "en" [ +#tr(( +en: [ Note we can combine `repr(C)` with `repr(align(n))` to obtain an aligned and C-compatible layout. It is not permissible to combine `repr(align(n))` with `repr(packed)`, since `repr(packed)` sets the alignment to `1`. It is also not permissible for a `repr(packed)` type to contain a `repr(align(n))` type. -] else if lang == "de" [ +], +de: [ Beachten Sie, dass wir `repr(C)` mit `repr(align(n))` kombinieren können, um ein ausgerichtetes und C-kompatibles Layout zu erhalten. Die Kombination von `repr(align(n))` mit `repr(packed)` ist nicht zulässig, da `repr(packed)` die Ausrichtung auf `1` setzt. Ebenso ist es nicht zulässig, dass ein `repr(packed)`-Typ einen `repr(align(n))`-Typ enthält. -] else if lang == "zh" [ +], +zh: [ 注意我们可以结合`repr(C)`和`repr(align(n))`来获取一个对齐的c兼容的布局。不允许将`repr(align(n))`和`repr(packed)`一起使用,因为`repr(packed)`将对齐设置为`1`。也不允许一个`repr(packed)`类型包含一个`repr(align(n))`类型。 -] else { todo } +])) #let url_layout = "https://doc.rust-lang.org/reference/type-layout.html" -#if lang == "en" [ +#tr(( +en: [ For further details on type layouts, refer to the #link(url_layout)[type layout] chapter of the Rust Reference. -] else if lang == "de" [ +], +de: [ Weitere Details zu Typ-Layouts finden Sie im Kapitel #link(url_layout)[Typ-Layout] der Rust-Referenz. -] else if lang == "zh" [ +], +zh: [ 关于类型布局更多的细节,参考the Rust Reference的#link("https://doc.rust-lang.org/reference/type-layout.html")[type layout]章节。 -] else { todo } +])) -== #(if lang == "en" [Other Resources] - else if lang == "de" [Weitere Ressourcen] - else if lang == "zh" [其它资源] - else { todo }) +== #tr(( + en: [Other Resources], + de: [Weitere Ressourcen], + zh: [其它资源], +)) #let url_faq = "https://docs.rust-embedded.org/faq.html" #let url_for_c = "http://blahg.josefsipek.net/?p=580" #let url_pointers = "https://github.com/diwic/reffers-rs/blob/master/docs/Pointers.html" -#if lang == "en" [ +#tr(( +en: [ - In this book: - #link()[A little C with your Rust] - #link()[A little Rust with your C] - #link(url_faq)[The Rust Embedded FAQs] - #link(url_for_c)[Rust Pointers for C Programmers] - #link(url_pointers)[I used to use pointers - now what?] -] else if lang == "de" [ +], +de: [ - In diesem Buch: - #link()[Ein bisschen C zu Ihrem Rust] - #link()[Ein bisschen Rust zu Ihrem C] - #link(url_faq)[Die Rust-Embedded-FAQs] - #link(url_for_c)[Rust-Pointer für C-Programmierer] - #link(url_pointers)[Früher habe ich Pointer verwendet -- und jetzt?] -] else if lang == "zh" [ +], +zh: [ - 这本书中: - #link()[使用C的Rust] - #link()[使用Rust的C] - #link(url_faq)[The Rust Embedded FAQs] - #link(url_for_c)[Rust Pointers for C Programmers] - #link(url_pointers)[I used to use pointers - now what?] -] else { todo } +])) diff --git a/typ/collections/index.typ b/typ/collections/index.typ index a86932b5..479af1a4 100644 --- a/typ/collections/index.typ +++ b/typ/collections/index.typ @@ -1,86 +1,103 @@ #import "../config.typ": * -#h1(if lang == "en" [Collections] - else if lang == "de" [Sammlungen (Collections)] - else if lang == "zh" [集合] - else { todo }) +#h1(tr(( + en: [Collections], + de: [Sammlungen (Collections)], + zh: [集合], +))) #let ln_vec = link("https://doc.rust-lang.org/std/vec/struct.Vec.html")[`Vec`] #let ln_string = link("https://doc.rust-lang.org/std/string/struct.String.html")[`String`] #let ln_map = link("https://doc.rust-lang.org/std/collections/struct.HashMap.html")[`HashMap`] -#if lang == "en" [ +#tr(( +en: [ Eventually you'll want to use dynamic data structures (AKA collections) in your program. `std` provides a set of common collections: #ln_vec, #ln_string, #ln_map, etc. All the collections implemented in `std` use a global dynamic memory allocator (AKA the heap). -] else if lang == "de" [ +], +de: [ Irgendwann wirst du in deinem Programm dynamische Datenstrukturen (auch bekannt als Collections) verwenden wollen. Die Standardbibliothek (`std`) stellt eine Reihe gängiger Collections bereit: #ln_vec, #ln_string, #ln_map usw. Alle in `std` implementierten Collections nutzen einen globalen dynamischen Speicherverwalter (den sogenannten Heap). -] else if lang == "zh" [ +], +zh: [ 最后,还希望在程序里使用动态数据结构(也称为集合)。`std` 提供了一组常见的集合: #ln_vec,#ln_string,#ln_map,等等。所有这些在`std`中被实现的集合都使用一个全局动态分配器(也称为堆)。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ As `core` is, by definition, free of memory allocations these implementations are not available there, but they can be found in the `alloc` crate that's shipped with the compiler. -] else if lang == "de" [ +], +de: [ Da `core` definitionsgemäß frei von Speicherzuweisungen ist, stehen diese Implementierungen dort nicht zur Verfügung; sie sind jedoch im `alloc`-Crate zu finden, das mit dem Compiler ausgeliefert wird. -] else if lang == "zh" [ +], +zh: [ 因为`core`的定义中是没有内存分配的,所以这些实现在`core`中是没有的,但是我们可以在编译器附带的`alloc` crate中找到。 -] else { todo } +])) #let ln_heapless = link("https://crates.io/crates/heapless")[`heapless`] -#if lang == "en" [ +#tr(( +en: [ If you need collections, a heap allocated implementation is not your only option. You can also use _fixed capacity_ collections; one such implementation can be found in the #ln_heapless crate. -] else if lang == "de" [ +], +de: [ Wenn Sie Sammlungen benötigen, ist eine auf dem Heap alloziierte Implementierung nicht Ihre einzige Option. Sie können auch Sammlungen mit _fester Kapazität_ verwenden; eine solche Implementierung findet sich im #ln_heapless;-Crate. -] else if lang == "zh" [ +], +zh: [ 如果需要集合,一个基于堆分配的实现不是唯一的选择。也可以使用 _fixed capacity_ 集合; 其实现可以在 #ln_heapless crate中被找到。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ In this section, we'll explore and compare these two implementations. -] else if lang == "de" [ +], +de: [ In diesem Abschnitt werden wir diese beiden Implementierungen untersuchen und vergleichen. -] else if lang == "zh" [ +], +zh: [ 在这部分,我们将研究和比较这两个实现。 -] else { todo } +])) -== #(if lang == "en" [Using `alloc`] - else if lang == "de" [Verwendung von `alloc`] - else if lang == "zh" [使用 `alloc`] - else { todo }) +== #tr(( + en: [Using `alloc`], + de: [Verwendung von `alloc`], + zh: [使用 `alloc`], +)) -#if lang == "en" [ +#tr(( +en: [ The `alloc` crate is shipped with the standard Rust distribution. To import the crate you can directly `use` it _without_ declaring it as a dependency in your `Cargo.toml` file. -] else if lang == "de" [ +], +de: [ Die `alloc`-Crate ist in der Standard-Rust-Distribution enthalten. Um das Crate zu importieren, können Sie es direkt per `use` einbinden, _ohne_ es als Abhängigkeit in Ihrer `Cargo.toml`-Datei zu deklarieren. -] else if lang == "zh" [ +], +zh: [ `alloc` crate与标准的Rust发行版在一起。你可以直接 `use` 导入这个crate,而不需要在`Cargo.toml`文件中把它声明为一个依赖。 -] else { todo } +])) ```rust #![feature(alloc)] @@ -91,46 +108,50 @@ use alloc::vec::Vec; ``` #let ln_alloc = link("https://doc.rust-lang.org/core/alloc/trait.GlobalAlloc.html")[`GlobalAlloc`] -#if lang == "en" [ +#tr(( +en: [ To be able to use any collection you'll first need use the `global_allocator` attribute to declare the global allocator your program will use. It's required that the allocator you select implements the #ln_alloc trait. -] else if lang == "de" [ +], +de: [ Um eine beliebige Collection nutzen zu können, müssen Sie zunächst das Attribut `global_allocator` verwenden, um den globalen Allocator zu deklarieren, den Ihr Programm einsetzen soll. Der gewählte Allocator muss zwingend das Trait #ln_alloc implementieren. -] else if lang == "zh" [ +], +zh: [ 为了能使用集合,首先需要使用`global_allocator`属性去声明程序将使用的全局分配器。它要求选择的分配器实现了#link("https://doc.rust-lang.org/core/alloc/trait.GlobalAlloc.html")[`GlobalAlloc`] trait 。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ For completeness and to keep this section as self-contained as possible we'll implement a simple bump pointer allocator and use that as the global allocator. However, we _strongly_ suggest you use a battle tested allocator from crates.io in your program instead of this allocator. -] else if lang == "de" [ +], +de: [ Der Vollständigkeit halber und um diesen Abschnitt so in sich abgeschlossen wie möglich zu halten, implementieren wir einen einfachen Bump-Pointer-Allokator und verwenden diesen als globalen Allokator. Wir empfehlen Ihnen jedoch _dringend_, in Ihrem Programm stattdessen einen praxiserprobten Allokator von crates.io zu verwenden. -] else if lang == "zh" [ +], +zh: [ 为了完整性和尽可能保持本节的自包含性,我们将实现一个简单线性指针分配器且用它作为全局分配器。然而,我们 _强烈地_ 建议你在你的程序中使用一个来自crates.io的久经战斗测试的分配器而不是这个分配器。 -] else { todo } +])) #raw(block: true, lang: "rust", -"// " + if lang == "en" { - "Bump pointer allocator implementation" - } else if lang == "de" { - "Implementierung eines Bump-Pointer-Allocators" - } else if lang == "zh" { - "线性指针分配器实现" - } else { todos } + " +"// " + ts(( + en: "Bump pointer allocator implementation", + de: "Implementierung eines Bump-Pointer-Allocators", + zh: "线性指针分配器实现", + )) + " use core::alloc::{GlobalAlloc, Layout}; use core::cell::UnsafeCell; @@ -138,13 +159,11 @@ use core::ptr; use cortex_m::interrupt; -// " + if lang == "en" { - "Bump pointer allocator for *single* core systems" - } else if lang == "de" { - "Bump-Pointer-Allocator fuer Systeme mit *einem* Rechenkern" - } else if lang == "zh" { - "用于单核系统的线性指针分配器" - } else { todos } + " +// " + ts(( + en: "Bump pointer allocator for *single* core systems", + de: "Bump-Pointer-Allocator fuer Systeme mit *einem* Rechenkern", + zh: "用于单核系统的线性指针分配器", + )) + " struct BumpPointerAlloc { head: UnsafeCell, end: usize, @@ -154,38 +173,32 @@ unsafe impl Sync for BumpPointerAlloc {} unsafe impl GlobalAlloc for BumpPointerAlloc { unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - // " + if lang == "en" { - "`interrupt::free` is a critical section that makes our allocator safe - // to use from within interrupts" - } else if lang == "de" { - "`interrupt::free` ist ein kritischer Abschnitt, der die sichere - // Verwendung unseres Allocators aus Interrupts heraus ermoeglicht." - } else if lang == "zh" { - "`interrupt::free`是一个临界区,临界区让我们的分配器在中断中用起来安全" - } else { todos } + " + // " + ts(( + en: "`interrupt::free` is a critical section that makes our allocator safe + // to use from within interrupts", + de: "`interrupt::free` ist ein kritischer Abschnitt, der die sichere + // Verwendung unseres Allocators aus Interrupts heraus ermoeglicht.", + zh: "`interrupt::free`是一个临界区,临界区让我们的分配器在中断中用起来安全", + )) + " interrupt::free(|_| { let head = self.head.get(); let size = layout.size(); let align = layout.align(); let align_mask = !(align - 1); - // " + if lang == "en" { - "move start up to the next alignment boundary" - } else if lang == "de" { - "Verschiebe den Startpunkt zur naechsten Ausrichtungsgrenze." - } else if lang == "zh" { - "将start移至下一个对齐边界。" - } else { todos } + " + // " + ts(( + en: "move start up to the next alignment boundary", + de: "Verschiebe den Startpunkt zur naechsten Ausrichtungsgrenze.", + zh: "将start移至下一个对齐边界。", + )) + " let start = (*head + align - 1) & align_mask; if start + size > self.end { - // " + if lang == "en" { - "a null pointer signal an Out Of Memory condition" - } else if lang == "de" { - "ein Nullzeiger signalisiert einen „Out of Memory“-Zustand" - } else if lang == "zh" { - "一个空指针通知内存不足" - } else { todos } + " + // " + ts(( + en: "a null pointer signal an Out Of Memory condition", + de: "ein Nullzeiger signalisiert einen „Out of Memory“-Zustand", + zh: "一个空指针通知内存不足", + )) + " ptr::null_mut() } else { *head = start + size; @@ -195,30 +208,26 @@ unsafe impl GlobalAlloc for BumpPointerAlloc { } unsafe fn dealloc(&self, _: *mut u8, _: Layout) { - // " + if lang == "en" { - "this allocator never deallocates memory" - } else if lang == "de" { - "Dieser Allokator gibt niemals Speicher frei." - } else if lang == "zh" { - "这个分配器从不释放内存" - } else { todos } + " + // " + ts(( + en: "this allocator never deallocates memory", + de: "Dieser Allokator gibt niemals Speicher frei.", + zh: "这个分配器从不释放内存", + )) + " } } -// " + if lang == "en" { - "Declaration of the global memory allocator +// " + ts(( + en: "Declaration of the global memory allocator // NOTE the user must ensure that the memory region `[0x2000_0100, 0x2000_0200]` -// is not used by other parts of the program" - } else if lang == "de" { - "Deklaration des globalen Speicher-Allocators +// is not used by other parts of the program", + de: "Deklaration des globalen Speicher-Allocators // HINWEIS: Der Benutzer muss sicherstellen, dass der Speicherbereich // `[0x2000_0100, 0x2000_0200]` nicht von anderen Teilen des Programms -// verwendet wird." - } else if lang == "zh" { - "全局内存分配器的声明 +// verwendet wird.", + zh: "全局内存分配器的声明 // 注意 用户必须确保`[0x2000_0100, 0x2000_0200]`内存区域 // 没有被程序的其它部分使用" - } else { todos } + " + )) + " #[global_allocator] static HEAP: BumpPointerAlloc = BumpPointerAlloc { head: UnsafeCell::new(0x2000_0100), @@ -226,18 +235,21 @@ static HEAP: BumpPointerAlloc = BumpPointerAlloc { }; ") -#if lang == "en" [ +#tr(( +en: [ Apart from selecting a global allocator the user will also have to define how Out Of Memory (OOM) errors are handled using the _unstable_ `alloc_error_handler` attribute. -] else if lang == "de" [ +], +de: [ Neben der Auswahl eines globalen Allocators muss der Benutzer auch festlegen, wie mit „Out of Memory" (OOM)-Fehlern umgegangen wird; hierfür wird das _instabile_ Attribut `alloc_error_handler` verwendet. -] else if lang == "zh" [ +], +zh: [ 除了选择一个全局分配器,用户也必须要定义如何使用_不稳定的_`alloc_error_handler`属性来处理内存溢出错误。 -] else { todo } +])) ```rust #![feature(alloc_error_handler)] @@ -252,14 +264,17 @@ fn on_oom(_layout: Layout) -> ! { } ``` -#if lang == "en" [ +#tr(( +en: [ Once all that is in place, the user can finally use the collections in `alloc`. -] else if lang == "de" [ +], +de: [ Sobald alles vorhanden ist, kann der Benutzer endlich die Sammlungen in „alloc" verwenden. -] else if lang == "zh" [ +], +zh: [ 一旦一切都完成了,用户最后就可以在`alloc`中使用集合。 -] else { todo } +])) ```rust #[entry] @@ -275,33 +290,40 @@ fn main() -> ! { } ``` -#if lang == "en" [ +#tr(( +en: [ If you have used the collections in the `std` crate then these will be familiar as they are exact same implementation. -] else if lang == "de" [ +], +de: [ Wenn Sie die Sammlungen aus dem `std`-Crate bereits verwendet haben, werden Ihnen diese vertraut vorkommen, da es sich um exakt dieselbe Implementierung handelt. -] else if lang == "zh" [ +], +zh: [ 如果你已经使用了`std` crate中的集合,那么这些对你来说将非常熟悉,因为他们的实现一样。 -] else { todo } +])) -== #(if lang == "en" [Using `heapless`] - else if lang == "de" [Verwendung von `heapless`] - else if lang == "zh" [使用 `heapless`] - else { todo }) +== #tr(( + en: [Using `heapless`], + de: [Verwendung von `heapless`], + zh: [使用 `heapless`], +)) -#if lang == "en" [ +#tr(( +en: [ `heapless` requires no setup as its collections don't depend on a global memory allocator. Just `use` its collections and proceed to instantiate them: -] else if lang == "de" [ +], +de: [ `heapless` erfordert keine Einrichtung, da seine Datenstrukturen nicht von einem globalen Speicher-Allocator abhängen. Binde die Datenstrukturen einfach per `use` ein und instanziiere sie: -] else if lang == "zh" [ +], +zh: [ `heapless`无需设置,因为它的集合不依赖一个全局内存分配器。只是`use`它的集合然后实例化它们: -] else { todo } +])) ```rust // heapless version: v0.4.x @@ -318,24 +340,29 @@ fn main() -> ! { } ``` -#if lang == "en" [ +#tr(( +en: [ You'll note two differences between these collections and the ones in `alloc`. -] else if lang == "de" [ +], +de: [ Sie werden zwei Unterschiede zwischen diesen Sammlungen und denen in `alloc` feststellen. -] else if lang == "zh" [ +], +zh: [ 你会注意到这些集合与`alloc`中的集合有两个不一样的地方。 -] else { todo } +])) #let ln_typenum = link("https://crates.io/crates/typenum")[`typenum`] -#if lang == "en" [ +#tr(( +en: [ First, you have to declare upfront the capacity of the collection. `heapless` collections never reallocate and have fixed capacities; this capacity is part of the type signature of the collection. In this case we have declared that `xs` has a capacity of 8 elements that is the vector can, at most, hold 8 elements. This is indicated by the `U8` (see #ln_typenum) in the type signature. -] else if lang == "de" [ +], +de: [ Erstens muss die Kapazität der Sammlung im Voraus deklariert werden. `heapless`-Sammlungen werden nie neu allokiert und haben eine feste Kapazität; diese Kapazität ist Teil der Typsignatur der Sammlung. In @@ -343,19 +370,22 @@ fn main() -> ! { Elementen hat, d.~h. der Vektor kann maximal 8 Elemente aufnehmen. Dies wird durch das `u8` (siehe #ln_typenum) in der Typsignatur angezeigt. -] else if lang == "zh" [ +], +zh: [ 第一,你必须预先声明集合的容量。`heapless`集合从来不会发生重分配且具有固定的容量;这个容量是集合的类型签名的一部分。在这个例子里,我们已经声明了`xs`的容量为8个元素,也就是说,这个vector最多只能有八个元素。这是通过类型签名中的`U8` (看#ln_typenum)来指定的。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Second, the `push` method, and many other methods, return a `Result`. Since the `heapless` collections have fixed capacity all operations that insert elements into the collection can potentially fail. The API reflects this problem by returning a `Result` indicating whether the operation succeeded or not. In contrast, `alloc` collections will reallocate themselves on the heap to increase their capacity. -] else if lang == "de" [ +], +de: [ Zweitens geben die Methode `push` sowie viele weitere Methoden ein `Result` zurück. Da `heapless`-Datenstrukturen über eine feste Kapazität verfügen, können alle Operationen, die Elemente in die Struktur @@ -364,48 +394,58 @@ fn main() -> ! { erfolgreich war oder nicht. Im Gegensatz dazu passen `alloc`-Datenstrukturen ihre Kapazität durch eine erneute Speicherzuweisung auf dem Heap an. -] else if lang == "zh" [ +], +zh: [ 第二,`push`方法和另外一些方法返回的是一个`Result`。因为`heapless`集合有一个固定的容量,所以所有插入的操作都可能会失败。通过返回一个`Result`,API反应了这个问题,指出操作是否成功还是失败。相反,`alloc`集合自己将会在堆上重新分配去增加它的容量。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ As of version v0.4.x all `heapless` collections store all their elements inline. This means that an operation like `let x = heapless::Vec::new();` will allocate the collection on the stack, but it's also possible to allocate the collection on a `static` variable, or even on the heap (`Box>`). -] else if lang == "de" [ +], +de: [ Seit Version v0.4.x speichern alle `heapless`-Datenstrukturen ihre Elemente direkt „inline". Das bedeutet, dass eine Operation wie `let x = heapless::Vec::new();` die Datenstruktur auf dem Stack anlegt; es ist jedoch auch möglich, sie in einer `static`-Variablen oder sogar auf dem Heap (`Box>`) zu speichern. -] else if lang == "zh" [ +], +zh: [ 自v0.4.x版本起,所有的`heapless`集合将所有的元素内联地存储起来了。这意味着像是`let x = heapless::Vec::new()`这样的一个操作将会在栈上分配集合,但是它也能够在一个`static`变量上分配集合,或者甚至在堆上(`Box>`)。 -] else { todo } +])) -== #(if lang == "en" [Trade-offs] - else if lang == "de" [Abwägungen] - else if lang == "zh" [取舍] - else { todo }) +== #tr(( + en: [Trade-offs], + de: [Abwägungen], + zh: [取舍], +)) -#if lang == "en" [ +#tr(( +en: [ Keep these in mind when choosing between heap allocated, relocatable collections and fixed capacity collections. -] else if lang == "de" [ +], +de: [ Berücksichtigen Sie diese Punkte, wenn Sie zwischen auf dem Heap alloziierten, verschiebbaren Sammlungen und Sammlungen mit fester Kapazität wählen. -] else if lang == "zh" [ +], +zh: [ 当在堆分配的可重定位的集合和固定容量的集合间进行选择的时候,记住这些内容。 -] else { todo } +])) -=== #(if lang == "en" [Out Of Memory and error handling] - else if lang == "de" [„Out of Memory" und Fehlerbehandlung] - else if lang == "zh" [内存溢出和错误处理] - else { todo }) +=== #tr(( + en: [Out Of Memory and error handling], + de: [„Out of Memory" und Fehlerbehandlung], + zh: [内存溢出和错误处理], +)) -#if lang == "en" [ +#tr(( +en: [ With heap allocations Out Of Memory is always a possibility and can occur in any place where a collection may need to grow: for example, all `alloc::Vec.push` invocations can potentially generate an OOM condition. @@ -413,7 +453,8 @@ fn main() -> ! { collections expose `try_reserve` methods that let you check for potential OOM conditions when growing the collection but you need be proactive about using them. -] else if lang == "de" [ +], +de: [ Bei Heap-Allokationen ist ein „Out of Memory" (OOM) -- also ein Speichermangel -- immer möglich; er kann überall dort auftreten, wo eine Collection wachsen muss: So können beispielsweise alle Aufrufe von @@ -422,37 +463,43 @@ fn main() -> ! { `alloc`-Collections bieten `try_reserve`-Methoden an, mit denen sich potenzielle OOM-Situationen beim Vergrößern der Collection überprüfen lassen; man muss diese Methoden jedoch aktiv einsetzen. -] else if lang == "zh" [ +], +zh: [ 使用堆分配,内存溢出总是有可能出现的且会发生在任何一个集合需要增长的地方: 比如,所有的 `alloc::Vec.push` 调用会潜在地产生一个OOM(Out of Memory)条件。因此一些操作可能会_隐式地_失败。一些`alloc`集合暴露了`try_reserve`方法,可以当增加集合时让你检查潜在的OOM条件,但是你需要主动地使用它们。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ If you exclusively use `heapless` collections and you don't use a memory allocator for anything else then an OOM condition is impossible. Instead, you'll have to deal with collections running out of capacity on a case by case basis. That is you'll have deal with _all_ the `Result`s returned by methods like `Vec.push`. -] else if lang == "de" [ +], +de: [ Wenn man ausschließlich `heapless`-Collections verwendet und keinen Speicher-Allocator für andere Zwecke nutzt, ist eine OOM-Situation ausgeschlossen. Stattdessen muss man im Einzelfall damit umgehen, wenn die Kapazität einer Collection erschöpft ist. Das bedeutet, man muss _alle_ `Result`-Werte behandeln, die von Methoden wie `Vec.push` zurückgegeben werden. -] else if lang == "zh" [ +], +zh: [ 如果你只使用`heapless`集合,而不使用内存分配器,那么一个OOM条件不可能出现。反而,你必须逐个处理容量不足的集合。也就是必须处理_所有_的`Result`,`Result`由像是`Vec.push`这样的方法返回的。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ OOM failures can be harder to debug than say `unwrap`-ing on all `Result`s returned by `heapless::Vec.push` because the observed location of failure may _not_ match with the location of the cause of the problem. For example, even `vec.reserve(1)` can trigger an OOM if the allocator is nearly exhausted because some other collection was leaking memory (memory leaks are possible in safe Rust). -] else if lang == "de" [ +], +de: [ Fehler durch Speichermangel (OOM) können schwieriger zu debuggen sein als etwa das Aufrufen von `unwrap` auf allen `Result`-Werten von `heapless::Vec.push`, da die Stelle, an der der Fehler sichtbar wird, @@ -460,16 +507,19 @@ fn main() -> ! { kann beispielsweise selbst `vec.reserve(1)` ein OOM auslösen, wenn der Allocator nahezu erschöpft ist, weil eine andere Collection Speicher verloren hat (Speicherlecks sind auch in „Safe Rust" möglich). -] else if lang == "zh" [ +], +zh: [ 与在所有由`heapless::Vec.push`返回的`Result`上调用`unwrap`相比,OOM错误更难调试,因为错误被发现的位置可能与导致问题的位置_不_一致。比如,甚至如果分配器接近消耗完`vec.reserve(1)`都能触发一个OOM,因为一些其它的集合正在泄露内存(内存泄露在安全的Rust是会发生的)。 -] else { todo } +])) -=== #(if lang == "en" [Memory usage] - else if lang == "de" [Speichernutzung] - else if lang == "zh" [内存使用] - else { todo }) +=== #tr(( + en: [Memory usage], + de: [Speichernutzung], + zh: [内存使用], +)) -#if lang == "en" [ +#tr(( +en: [ Reasoning about memory usage of heap allocated collections is hard because the capacity of long lived collections can change at runtime. Some operations may implicitly reallocate the collection increasing its @@ -479,7 +529,8 @@ fn main() -> ! { shrink the memory allocation or not. Additionally, the allocator may have to deal with memory fragmentation which can increase the _apparent_ memory usage. -] else if lang == "de" [ +], +de: [ Die Einschätzung des Speicherverbrauchs von auf dem Heap allozierten Collections ist schwierig, da sich die Kapazität langlebiger Collections zur Laufzeit ändern kann. Manche Operationen führen möglicherweise zu @@ -490,131 +541,158 @@ fn main() -> ! { reduziert wird. Zudem muss der Allocator unter Umständen mit Speicherfragmentierung umgehen, was den _scheinbaren_ Speicherverbrauch erhöhen kann. -] else if lang == "zh" [ +], +zh: [ 推理堆分配集合的内存使用是很难的因为长期使用的集合的大小会在运行时改变。一些操作可能隐式地重分配集合,增加了它的内存使用,一些集合暴露的方法,像是`shrink_to_fit`,会潜在地减少集合使用的内存 -- 最终,它由分配器去决定是否确定减小内存的分配或者不。另外,分配器可能不得不处理内存碎片,它会_明显_增加内存的使用。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ On the other hand if you exclusively use fixed capacity collections, store most of them in `static` variables and set a maximum size for the call stack then the linker will detect if you try to use more memory than what's physically available. -] else if lang == "de" [ +], +de: [ Verwendet man hingegen ausschließlich Collections mit fester Kapazität, legt die meisten davon in `static`-Variablen ab und legt eine maximale Größe für den Aufruf-Stack (Call Stack) fest, so erkennt der Linker, wenn mehr Speicher beansprucht wird, als physisch verfügbar ist. -] else if lang == "zh" [ +], +zh: [ 另一方面,如果你只使用固定容量的集合,请把大多数的数据保存在`static`变量中,并为调用栈设置一个最大尺寸,随后如果你尝试使用大于可用的物理内存的内存大小时,链接器会发现它。 -] else { todo } +])) #let ln_z_emit = link("https://doc.rust-lang.org/beta/unstable-book/compiler-flags/emit-stack-sizes.html")[`-Z emit-stack-sizes`] #let ln_stack_sizes = link("https://crates.io/crates/stack-sizes")[`stack-sizes`] -#if lang == "en" [ +#tr(( +en: [ Furthermore, fixed capacity collections allocated on the stack will be reported by #ln_z_emit flag which means that tools that analyze stack usage (like #ln_stack_sizes) will include them in their analysis. -] else if lang == "de" [ +], +de: [ Darüber hinaus werden auf dem Stack alloziierte Collections mit fester Kapazität durch das Flag #ln_z_emit erfasst; das bedeutet, dass Werkzeuge zur Analyse der Stack-Nutzung (wie #ln_stack_sizes) diese in ihre Auswertung einbeziehen. -] else if lang == "zh" [ +], +zh: [ 另外,在栈上分配的固定容量的集合可以通过#ln_z_emit;标识来报告,其意味着用来分析栈使用的工具(像是#ln_stack_sizes)将会把在栈上分配的集合包含进它们的分析中。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ However, fixed capacity collections can _not_ be shrunk which can result in lower load factors (the ratio between the size of the collection and its capacity) than what relocatable collections can achieve. -] else if lang == "de" [ +], +de: [ Sammlungen mit fester Kapazität können jedoch _nicht_ verkleinert werden, was zu niedrigeren Auslastungsgraden (dem Verhältnis zwischen der Größe der Sammlung und ihrer Kapazität) führen kann, als sie bei Sammlungen mit veränderbarer Kapazität möglich sind. -] else if lang == "zh" [ +], +zh: [ 然而,固定容量的集合_不_能被减少,与可重定位集合所能达到的负载系数(集合的大小和它的容量之间的比值)相比,它能产生更低的负载系数。 -] else { todo } +])) -=== #(if lang == "en" [Worst Case Execution Time (WCET)] - else if lang == "de" [Maximale Ausführungszeit (WCET)] - else if lang == "zh" [最坏执行时间 (WCET)] - else { todo }) +=== #tr(( + en: [Worst Case Execution Time (WCET)], + de: [Maximale Ausführungszeit (WCET)], + zh: [最坏执行时间 (WCET)], +)) -#if lang == "en" [ +#tr(( +en: [ If you are building time sensitive applications or hard real time applications then you care, maybe a lot, about the worst case execution time of the different parts of your program. -] else if lang == "de" [ +], +de: [ Wenn Sie zeitkritische oder Echtzeitanwendungen entwickeln, ist Ihnen die Worst-Case-Ausführungszeit (WCET) der verschiedenen Programmteile wahrscheinlich sehr wichtig. -] else if lang == "zh" [ +], +zh: [ 如果你正在搭建时间敏感型应用或者硬实时应用,那么你可能更关心你程序的不同部分的最坏执行时间。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ The `alloc` collections can reallocate so the WCET of operations that may grow the collection will also include the time it takes to reallocate the collection, which itself depends on the _runtime_ capacity of the collection. This makes it hard to determine the WCET of, for example, the `alloc::Vec.push` operation as it depends on both the allocator being used and its runtime capacity. -] else if lang == "de" [ +], +de: [ Da `alloc`-Collections Speicher neu allokieren können, umfasst die WCET von Operationen, die die Collection vergrößern, auch die Zeit für die Neuallokation. Diese hängt wiederum von der _Laufzeitkapazität_ der Collection ab. Daher ist es schwierig, die WCET beispielsweise der Operation `alloc::Vec.push` zu bestimmen, da sie sowohl vom verwendeten Allokator als auch von dessen Laufzeitkapazität abhängt. -] else if lang == "zh" [ +], +zh: [ `alloc`集合能重分配,所以操作的WCET可能会增加,集合也将包括它用来重分配集合所需的时间,它取决于集合的_运行时_容量。这使得它更难去决定操作,比如`alloc::Vec.push`,的WCET,因为它依赖被使用的分配器和它的运行时容量。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ On the other hand fixed capacity collections never reallocate so all operations have a predictable execution time. For example, `heapless::Vec.push` executes in constant time. -] else if lang == "de" [ +], +de: [ Collections mit fester Kapazität hingegen allokieren nie Speicher neu, sodass alle Operationen eine vorhersehbare Ausführungszeit haben. Beispielsweise wird `heapless::Vec.push` in konstanter Zeit ausgeführt. -] else if lang == "zh" [ +], +zh: [ 另一方面固定容量的集合不会重分配,因此所有的操作有个可预期的执行时间。比如,`heapless::Vec.push`以固定时间执行。 -] else { todo } +])) -=== #(if lang == "en" [Ease of use] - else if lang == "de" [Benutzerfreundlichkeit] - else if lang == "zh" [易用性] - else { todo }) +=== #tr(( + en: [Ease of use], + de: [Benutzerfreundlichkeit], + zh: [易用性], +)) -#if lang == "en" [ +#tr(( +en: [ `alloc` requires setting up a global allocator whereas `heapless` does not. However, `heapless` requires you to pick the capacity of each collection that you instantiate. -] else if lang == "de" [ +], +de: [ Für `alloc` muss ein globaler Allocator eingerichtet werden, für `heapless` hingegen nicht. Allerdings erfordert `heapless`, dass man bei der Instanziierung jeder Collection deren Kapazität festlegt. -] else if lang == "zh" [ +], +zh: [ `alloc`要求配置一个全局分配器而`heapless`不需要。然而,`heapless`要求你去选择你要实例化的每一个集合的容量。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ The `alloc` API will be familiar to virtually every Rust developer. The `heapless` API tries to closely mimic the `alloc` API but it will never be exactly the same due to its explicit error handling -- some developers may feel the explicit error handling is excessive or too cumbersome. -] else if lang == "de" [ +], +de: [ Die `alloc`-API dürfte so gut wie jedem Rust-Entwickler vertraut sein. Die `heapless`-API orientiert sich zwar eng an der `alloc`-API, unterscheidet sich jedoch aufgrund der expliziten Fehlerbehandlung -- manche Entwickler empfinden diese explizite Fehlerbehandlung womöglich als übertrieben oder zu umständlich. -] else if lang == "zh" [ +], +zh: [ `alloc` API几乎为每一个Rust开发者所熟知。`heapless` API尝试模仿`alloc` API,但是因为`heapless`的显式错误处理,它们不可能会一模一样 -- 一些开发者可能会觉得显式的错误处理过多或太麻烦。 -] else { todo } +])) diff --git a/typ/concurrency/index.typ b/typ/concurrency/index.typ index 2418a484..82b3f18d 100644 --- a/typ/concurrency/index.typ +++ b/typ/concurrency/index.typ @@ -1,12 +1,14 @@ #import "../config.typ": * -#h1(if lang == "en" [Concurrency] - else if lang == "de" [Nebenläufigkeit] - else if lang == "zh" [并发] - else { todo }) +#h1(tr(( + en: [Concurrency], + de: [Nebenläufigkeit], + zh: [并发], +))) -#if lang == "en" [ +#tr(( +en: [ Concurrency happens whenever different parts of your program might execute at different times or out of order. In an embedded context, this includes: @@ -17,7 +19,8 @@ - and in some systems, multiple-core microprocessors, where each core can be independently running a different part of your program at the same time. -] else if lang == "de" [ +], +de: [ Nebenläufigkeit tritt immer dann auf, wenn verschiedene Teile Ihres Programms zu unterschiedlichen Zeiten oder in einer anderen als der vorgesehenen Reihenfolge ausgeführt werden können. Im Kontext @@ -29,40 +32,47 @@ - und in einigen Systemen Mehrkern-Mikroprozessoren, bei denen jeder Kern gleichzeitig und unabhängig voneinander einen anderen Teil Ihres Programms ausführen kann. -] else if lang == "zh" [ +], +zh: [ 当程序的不同部分有可能会在不同的时刻被执行或者不按顺序地被执行时,那并发就出现了。在一个嵌入式环境中,这包括: - 中断处理函数,一旦相关的中断发生时,中断处理函数就会运行, - 不同的多线程形式,在这块,微处理器通常会在程序的不同部分间进行切换, - 在一些多核微处理器系统中,每个核可以同时独立地运行程序的不同部分。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Since many embedded programs need to deal with interrupts, concurrency will usually come up sooner or later, and it's also where many subtle and difficult bugs can occur. Luckily, Rust provides a number of abstractions and safety guarantees to help us write correct code. -] else if lang == "de" [ +], +de: [ Da viele eingebettete Programme mit Interrupts umgehen müssen, spielt Nebenläufigkeit früher oder später meist eine Rolle -- und genau hier können auch viele schwer zu findende und komplexe Fehler auftreten. Glücklicherweise bietet Rust eine Reihe von Abstraktionen und Sicherheitsgarantien, die uns dabei helfen, korrekten Code zu schreiben. -] else if lang == "zh" [ +], +zh: [ 因为许多嵌入式程序需要处理中断,因此并发迟早会出现,这也是许多微妙和困难的bugs会出现的地方。幸运地是,Rust提供了许多抽象和安全保障去帮助我们写正确的代码。 -] else { todo } +])) -== #(if lang == "en" [No Concurrency] - else if lang == "de" [Keine Nebenläufigkeit] - else if lang == "zh" [没有并发] - else { todo }) +== #tr(( + en: [No Concurrency], + de: [Keine Nebenläufigkeit], + zh: [没有并发], +)) -#if lang == "en" [ +#tr(( +en: [ The simplest concurrency for an embedded program is no concurrency: your software consists of a single main loop which just keeps running, and there are no interrupts at all. Sometimes this is perfectly suited to the problem at hand! Typically your loop will read some inputs, perform some processing, and write some outputs. -] else if lang == "de" [ +], +de: [ Die einfachste Form der Nebenläufigkeit für ein Embedded-Programm ist der Verzicht darauf: Die Software besteht aus einer einzigen Hauptschleife, die kontinuierlich durchlaufen wird, und es kommen @@ -70,11 +80,12 @@ die vorliegende Aufgabenstellung ideal! Typischerweise liest die Schleife Eingabewerte ein, führt Berechnungen oder Verarbeitungen durch und gibt Ergebnisse aus. -] else if lang == "zh" [ +], +zh: [ 对于一个嵌入式程序来说最简单的并发是没有并发: 软件由一个保持运行的main循环组成,一点中断也没有。有时候这非常适合手边的问题! 通常你的循环将会读取一些输入,执行一些处理,且写入一些输出。 -] else { todo } +])) ```rust #[entry] @@ -88,26 +99,31 @@ fn main() { } ``` -#if lang == "en" [ +#tr(( +en: [ Since there's no concurrency, there's no need to worry about sharing data between parts of your program or synchronising access to peripherals. If you can get away with such a simple approach this can be a great solution. -] else if lang == "de" [ +], +de: [ Da keine Nebenläufigkeit vorliegt, müssen Sie sich keine Gedanken über die gemeinsame Datennutzung zwischen verschiedenen Programmteilen oder die Synchronisierung des Zugriffs auf Peripheriegeräte machen. Wenn ein solch einfacher Ansatz ausreicht, kann dies eine hervorragende Lösung sein. -] else if lang == "zh" [ +], +zh: [ 因为这里没有并发,因此不需要担心程序不同部分间的共享数据或者同步对外设的访问。如果可以使用一个简单的方法来解决问题,这种方法是个不错的选择。 -] else { todo } +])) -== #(if lang == "en" [Global Mutable Data] - else if lang == "de" [Globale veränderliche Daten] - else if lang == "zh" [全局可变数据] - else { todo }) +== #tr(( + en: [Global Mutable Data], + de: [Globale veränderliche Daten], + zh: [全局可变数据], +)) -#if lang == "en" [ +#tr(( +en: [ Unlike non-embedded Rust, we will not usually have the luxury of creating heap allocations and passing references to that data into a newly-created thread. Instead, our interrupt handlers might be called at @@ -115,7 +131,8 @@ fn main() { using. At the lowest level, this means we must have _statically allocated_ mutable memory, which both the interrupt handler and the main code can refer to. -] else if lang == "de" [ +], +de: [ Im Gegensatz zu Rust-Anwendungen außerhalb des Embedded-Bereichs haben wir meist nicht den Luxus, Speicher auf dem Heap zu reservieren und Referenzen auf diese Daten an einen neu erstellten Thread zu übergeben. @@ -125,19 +142,22 @@ fn main() { über _statisch reservierten_, veränderbaren Speicher verfügen müssen, auf den sowohl der Interrupt-Handler als auch der Hauptprogrammcode zugreifen können. -] else if lang == "zh" [ +], +zh: [ 不像非嵌入式Rust,我们通常不会奢侈地在堆上分配数据,并将对该数据的引用传递到新创建的线程中。相反,我们的中断处理函数随时可能被调用,且必须知道如何访问我们正在使用的共享内存。从最底层看来,这意味着我们必须有 _静态分配的_ 可变的内存,中断处理函数和main代码都可以引用这块内存。 -] else { todo } +])) #let ln_staticmut = link("https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable")[`static mut`] -#if lang == "en" [ +#tr(( +en: [ In Rust, such #ln_staticmut variables are always unsafe to read or write, because without taking special care, you might trigger a race condition, where your access to the variable is interrupted halfway through by an interrupt which also accesses that variable. -] else if lang == "de" [ +], +de: [ In Rust ist der Lese- oder Schreibzugriff auf solche #ln_staticmut;-Variablen stets als `unsafe` (unsicher) eingestuft. Ohne besondere @@ -145,22 +165,26 @@ fn main() { (Wettlaufsituation) kommen: Der Zugriff auf die Variable wird mitten im Vorgang durch einen Interrupt unterbrochen, der seinerseits ebenfalls auf diese Variable zugreift. -] else if lang == "zh" [ +], +zh: [ 在Rust中,#ln_staticmut;这样的变量读取或者写入总是unsafe的,因为不特别关注它们的话,可能会触发一个竞态条件,对变量的访问在中途就被一个也访问那个变量的中断打断了。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ For an example of how this behaviour can cause subtle errors in your code, consider an embedded program which counts rising edges of some input signal in each one-second period (a frequency counter): -] else if lang == "de" [ +], +de: [ Um zu veranschaulichen, wie dieses Verhalten subtile Fehler in Ihrem Code verursachen kann, betrachten Sie ein eingebettetes Programm, das die steigenden Flanken eines Eingangssignals innerhalb jedes Ein-Sekunden-Intervalls zählt (einen Frequenzzähler): -] else if lang == "zh" [ +], +zh: [ 为了举例这种行为如何在代码中导致了微妙的错误,思考一个嵌入式程序,这个程序在每一秒的周期内计数一些输入信号的上升沿(一个频率计数器): -] else { todo } +])) #raw(block: true, lang: "rust", "static mut COUNTER: u32 = 0; @@ -172,14 +196,12 @@ fn main() -> ! { loop { let state = read_signal_level(); if state && !last_state { - // " + if lang == "en" { - "DANGER - Not actually safe! Could cause data races." - } else if lang == "de" { - "GEFAHR – Nicht wirklich sicher! Koennte zu Datenwettlaeufen - // fuehren." - } else if lang == "zh" { - "危险 - 实际不安全! 可能导致数据竞争。" - } else { todos } + " + // " + ts(( + en: "DANGER - Not actually safe! Could cause data races.", + de: "GEFAHR – Nicht wirklich sicher! Koennte zu Datenwettlaeufen + // fuehren.", + zh: "危险 - 实际不安全! 可能导致数据竞争。", + )) + " unsafe { COUNTER += 1 }; } last_state = state; @@ -192,7 +214,8 @@ fn timer() { } ") -#if lang == "en" [ +#tr(( +en: [ Each second, the timer interrupt sets the counter back to 0. Meanwhile, the main loop continually measures the signal, and incremements the counter when it sees a change from low to high. We've had to use @@ -204,7 +227,8 @@ fn timer() { fired after the load but before the store, the reset back to 0 would be ignored after the interrupt returns --- and we would count twice as many transitions for that period. -] else if lang == "de" [ +], +de: [ Sekündlich setzt der Timer-Interrupt den Zähler auf 0 zurück. Währenddessen misst die Hauptschleife kontinuierlich das Signal und erhöht den Zähler, sobald ein Wechsel von „Low" auf „High" erkannt wird. @@ -218,34 +242,39 @@ fn timer() { ausgelöst, ginge das Zurücksetzen auf 0 nach der Rückkehr aus dem Interrupt verloren -- und wir würden für diesen Zeitraum doppelt so viele Signalwechsel zählen. -] else if lang == "zh" [ +], +zh: [ 每秒计时器中断会把计数器设置回0。这期间,main循环连续地测量信号,且当看到从低电平到高电平的变化时,增加计数器的值。因为它是`static mut`的,我们不得不使用`unsafe`去访问`COUNTER`,意思是我们向编译器保证我们的操作不会导致任何未定义的行为。你能发现竞态条件吗?`COUNTER`上的增加并不一定是原子的 - 事实上,在大多数嵌入式平台上,它将被分开成一个读取操作,然后是增加,然后是写回。如果中断在计数器被读取之后但是在被写回之前被激活,在中断返回后,重置回0的操作会被忽略掉 - 那期间,我们会算出两倍的转换次数。 -] else { todo } +])) -== #(if lang == "en" [Critical Sections] - else if lang == "de" [Kritische Abschnitte] - else if lang == "zh" [临界区(Critical Sections)] - else { todo }) +== #tr(( + en: [Critical Sections], + de: [Kritische Abschnitte], + zh: [临界区(Critical Sections)], +)) -#if lang == "en" [ +#tr(( +en: [ So, what can we do about data races? A simple approach is to use _critical sections_, a context where interrupts are disabled. By wrapping the access to `COUNTER` in `main` in a critical section, we can be sure the timer interrupt will not fire until we're finished incrementing `COUNTER`: -] else if lang == "de" [ +], +de: [ Was können wir also gegen Data Races unternehmen? Ein einfacher Ansatz ist die Verwendung von _kritischen Abschnitten_ -- also Bereichen, in denen Interrupts deaktiviert sind. Indem wir den Zugriff auf `COUNTER` in der Funktion `main` in einen kritischen Abschnitt einbetten, stellen wir sicher, dass der Timer-Interrupt erst ausgelöst wird, nachdem wir das Inkrementieren von `COUNTER` abgeschlossen haben: -] else if lang == "zh" [ +], +zh: [ 因此,关于数据竞争可以做些什么?一个简单的方法是使用 _临界区(critical sections)_ ,在临界区的上下文中中断被关闭了。通过把对`main`中的`COUNTER`访问封装进一个临界区,我们能确保计时器中断将不会激活,直到我们完成了增加`COUNTER`的操作: -] else { todo } +])) #raw(block: true, lang: "rust", "static mut COUNTER: u32 = 0; @@ -257,14 +286,12 @@ fn main() -> ! { loop { let state = read_signal_level(); if state && !last_state { - // " + if lang == "en" { - "New critical section ensures synchronised access to COUNTER" - } else if lang == "de" { - "Ein neuer kritischer Abschnitt gewaehrleistet den - // synchronisierten Zugriff auf COUNTER." - } else if lang == "zh" { - "新的临界区确保对COUNTER的同步访问" - } else { todos } + " + // " + ts(( + en: "New critical section ensures synchronised access to COUNTER", + de: "Ein neuer kritischer Abschnitt gewaehrleistet den + // synchronisierten Zugriff auf COUNTER.", + zh: "新的临界区确保对COUNTER的同步访问", + )) + " cortex_m::interrupt::free(|_| { unsafe { COUNTER += 1 }; }); @@ -279,53 +306,63 @@ fn timer() { } ") -#if lang == "en" [ +#tr(( +en: [ In this example, we use `cortex_m::interrupt::free`, but other platforms will have similar mechanisms for executing code in a critical section. This is also the same as disabling interrupts, running some code, and then re-enabling interrupts. -] else if lang == "de" [ +], +de: [ In diesem Beispiel verwenden wir `cortex_m::interrupt::free`, doch auch andere Plattformen verfügen über ähnliche Mechanismen, um Code in einem kritischen Abschnitt auszuführen. Dies entspricht im Grunde dem Deaktivieren von Interrupts, der Ausführung von Code und dem anschließenden erneuten Aktivieren der Interrupts. -] else if lang == "zh" [ +], +zh: [ 在这个例子里,我们使用 `cortex_m::interrupt::free`,但是其它平台将会有更简单的机制在一个临界区中执行代码。它们都有一样的逻辑,关闭中断,运行一些代码,然后重新使能中断。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Note we didn't need to put a critical section inside the timer interrupt, for two reasons: - Writing 0 to `COUNTER` can't be affected by a race since we don't read it - It will never be interrupted by the `main` thread anyway -] else if lang == "de" [ +], +de: [ Beachten Sie, dass wir innerhalb des Timer-Interrupts keinen kritischen Abschnitt benötigten; dies hat zwei Gründe: - Das Schreiben von 0 in `COUNTER` kann nicht von einer Race-Condition betroffen sein, da wir den Wert nicht lesen. - Es wird ohnehin niemals vom `main`-Thread unterbrochen werden. -] else if lang == "zh" [ +], +zh: [ 注意,有两个理由,不需要把一个临界区放进计时器中断中: - 向`COUNTER`写入0不会被一个竞争影响,因为我们不需要读取它 - 无论如何,它永远不会被`main`线程中断 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ If `COUNTER` was being shared by multiple interrupt handlers that might _preempt_ each other, then each one might require a critical section as well. -] else if lang == "de" [ +], +de: [ Wenn `COUNTER` von mehreren Interrupt-Handlern gemeinsam genutzt würde, die sich gegenseitig _unterbrechen_ könnten, müsste jeder von ihnen ebenfalls einen kritischen Abschnitt verwenden. -] else if lang == "zh" [ +], +zh: [ 如果`COUNTER`被多个可能相互 _抢占_ 的中断处理函数共享,那么每一个也需要一个临界区。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ This solves our immediate problem, but we're still left writing a lot of unsafe code which we need to carefully reason about, and we might be using critical sections needlessly. Since each critical section @@ -334,7 +371,8 @@ fn timer() { may take longer to be processed, and the time until they are processed will be more variable). Whether this is a problem depends on your system, but in general, we'd like to avoid it. -] else if lang == "de" [ +], +de: [ Dies löst zwar unser unmittelbares Problem, doch wir müssen weiterhin viel unsicheren Code schreiben, dessen Verhalten wir sorgfältig analysieren müssen; zudem setzen wir möglicherweise unnötigerweise @@ -345,34 +383,40 @@ fn timer() { bis zur Verarbeitung schwankt stärker). Ob dies ein Problem darstellt, hängt vom jeweiligen System ab, doch im Allgemeinen sollten wir dies vermeiden. -] else if lang == "zh" [ +], +zh: [ 这解决了我们眼前的问题,但是我们仍然要编写许多unsafe的代码,我们需要仔细推敲这些代码,有些我们可能不需要使用临界区。因为每个临界区暂时暂停了中断处理,就会带来一些相关的成本,一些额外的代码大小,更高的中断延迟和抖动(中断可能花费很长时间去处理,等待被处理的时间变化非常大)。这是否是个问题取决于你的系统,但是通常,我们想要避免它。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ It's worth noting that while a critical section guarantees no interrupts will fire, it does not provide an exclusivity guarantee on multi-core systems! The other core could be happily accessing the same memory as your core, even without interrupts. You will need stronger synchronisation primitives if you are using multiple cores. -] else if lang == "de" [ +], +de: [ Es ist wichtig zu beachten: Auch wenn ein kritischer Abschnitt garantiert, dass keine Interrupts ausgelöst werden, bietet er auf Mehrkernsystemen keine Exklusivitätsgarantie! Der andere Rechenkern könnte -- selbst ohne Interrupts -- problemlos auf denselben Speicher zugreifen wie Ihr eigener Kern. Wenn Sie mehrere Rechenkerne nutzen, benötigen Sie daher leistungsfähigere Synchronisationsmechanismen. -] else if lang == "zh" [ +], +zh: [ 值得注意的是,虽然一个临界区保障了不会发生中断,但是它在多核系统上不提供一个排他性保证(exclusivity guarantee)!其它核可能很开心地访问与你的核一样的内存区域,即使没有中断。如果你正在使用多核,你将需要更强的同步原语(synchronisation primitives)。 -] else { todo } +])) -== #(if lang == "en" [Atomic Access] - else if lang == "de" [Atomarer Zugriff] - else if lang == "zh" [原子访问] - else { todo }) +== #tr(( + en: [Atomic Access], + de: [Atomarer Zugriff], + zh: [原子访问], +)) -#if lang == "en" [ +#tr(( +en: [ On some platforms, special atomic instructions are available, which provide guarantees about read-modify-write operations. Specifically for Cortex-M: `thumbv6` (Cortex-M0, Cortex-M0+) only provide atomic load and @@ -382,7 +426,8 @@ fn timer() { attempt the increment, it will succeed most of the time, but if it was interrupted it will automatically retry the entire increment operation. These atomic operations are safe even across multiple cores. -] else if lang == "de" [ +], +de: [ Auf einigen Plattformen stehen spezielle atomare Befehle zur Verfügung, die Garantien für Lese-Modifizier-Schreib-Operationen (Read-Modify-Write) bieten. Speziell für Cortex-M gilt: `thumbv6` @@ -394,11 +439,12 @@ fn timer() { durchzuführen -- was meistens gelingt --, doch sollte eine Unterbrechung auftreten, wird der gesamte Vorgang automatisch erneut versucht. Diese atomaren Operationen sind auch in Mehrkernsystemen sicher. -] else if lang == "zh" [ +], +zh: [ 在一些平台上,可以使用特定的原子指令,它保障了读取-修改-写回操作。针对Cortex-M: `thumbv6`\(Cortex-M0,Cortex-M0+)只提供原子读取和存取指令,而`thumv7`\(Cortex-M3及以上)提供完整的比较和交换(CAS)指令。这些CAS指令可以替代过重的禁用所有中断的方法: 我们可以尝试执行加法操作,它在大多数情况下都会成功,但是如果它被中断了它将会自动重试完整的加法操作。这些原子操作甚至在多核间也是安全的。 -] else { todo } +])) #raw(block: true, lang: "rust", "use core::sync::atomic::{AtomicUsize, Ordering}; @@ -412,13 +458,11 @@ fn main() -> ! { loop { let state = read_signal_level(); if state && !last_state { - // " + if lang == "en" { - "Use `fetch_add` to atomically add 1 to COUNTER" - } else if lang == "de" { - "Verwenden Sie `fetch_add`, um 1 atomar zu COUNTER zu addieren." - } else if lang == "zh" { - "使用 `fetch_add` 原子性地给 COUNTER 加一" - } else { todos } + " + // " + ts(( + en: "Use `fetch_add` to atomically add 1 to COUNTER", + de: "Verwenden Sie `fetch_add`, um 1 atomar zu COUNTER zu addieren.", + zh: "使用 `fetch_add` 原子性地给 COUNTER 加一", + )) + " COUNTER.fetch_add(1, Ordering::Relaxed); } last_state = state; @@ -427,35 +471,37 @@ fn main() -> ! { #[interrupt] fn timer() { - // " + if lang == "en" { - "Use `store` to write 0 directly to COUNTER" - } else if lang == "de" { - "Verwenden Sie `store`, um 0 direkt in COUNTER zu schreiben." - } else if lang == "zh" { - "使用 `store` 将 0 直接写入 COUNTER" - } else { todos } + " + // " + ts(( + en: "Use `store` to write 0 directly to COUNTER", + de: "Verwenden Sie `store`, um 0 direkt in COUNTER zu schreiben.", + zh: "使用 `store` 将 0 直接写入 COUNTER", + )) + " COUNTER.store(0, Ordering::Relaxed) } ") -#if lang == "en" [ +#tr(( +en: [ This time `COUNTER` is a safe `static` variable. Thanks to the `AtomicUsize` type `COUNTER` can be safely modified from both the interrupt handler and the main thread without disabling interrupts. When possible, this is a better solution --- but it may not be supported on your platform. -] else if lang == "de" [ +], +de: [ Diesmal ist `COUNTER` eine sichere `static`-Variable. Dank des Typs `AtomicUsize` kann `COUNTER` sowohl vom Interrupt-Handler als auch vom Hauptthread aus sicher geändert werden, ohne Interrupts zu deaktivieren. Wenn möglich, ist dies die bessere Lösung -- wird aber möglicherweise von Ihrer Plattform nicht unterstützt. -] else if lang == "zh" [ +], +zh: [ 这时,`COUNTER`是一个safe的`static`变量。多亏了`AtomicUsize`类型,不需要禁用中断,`COUNTER`能从中断处理函数和main线程被安全地修改。当可以这么做时,这是一个更好的解决方案 - 然而平台上可能不支持这么做。 -] else { todo } +])) #let ln_ordering = link("https://doc.rust-lang.org/core/sync/atomic/enum.Ordering.html")[`Ordering`] -#if lang == "en" [ +#tr(( +en: [ A note on #ln_ordering: this affects how the compiler and hardware may reorder instructions, and also has consequences on cache visibility. Assuming that the target is a @@ -465,7 +511,8 @@ fn timer() { on what you're using atomics for you may or may not need this! The precise details of the atomic model are complicated and best described elsewhere. -] else if lang == "de" [ +], +de: [ Hinweis zur #ln_ordering: Diese beeinflusst, wie Compiler und Hardware Anweisungen neu anordnen, und hat auch Auswirkungen auf die Cache-Sichtbarkeit. Bei einer @@ -475,92 +522,99 @@ fn timer() { Je nachdem, wofür Sie atomare Operationen verwenden, benötigen Sie dies möglicherweise nicht. Die genauen Details des atomaren Modells sind komplex und werden am besten an anderer Stelle beschrieben. -] else if lang == "zh" [ +], +zh: [ 关于#ln_ordering;的提醒: 它可能影响编译器和硬件如何重新排序指令,也会影响缓存可见性。假设目标是个单核平台,在这个案例里`Relaxed`是充足的和最有效的选择。更严格的排序将导致编译器在原子操作周围产生内存屏障(Memory Barriers);取决于你做什么原子操作,你可能需要或者不需要这个排序!原子模型的精确细节是复杂的,最好写在其它地方。 -] else { todo } +])) #let url_atomics = "https://doc.rust-lang.org/nomicon/atomics.html" -#if lang == "en" [ +#tr(( +en: [ For more details on atomics and ordering, see the #link(url_atomics)[nomicon]. -] else if lang == "de" [ +], +de: [ Weitere Informationen zu atomaren Operationen und deren Reihenfolge finden Sie im #link(url_atomics)[nomicon]. -] else if lang == "zh" [ +], +zh: [ 关于原子操作和排序的更多细节,可以看这里#link(url_atomics)[nomicon]。 -] else { todo } +])) -== #(if lang == "en" [Abstractions, Send, and Sync] - else if lang == "de" [Abstraktionen, Send und Sync] - else if lang == "zh" [抽象,Send和Sync] - else { todo }) +== #tr(( + en: [Abstractions, Send, and Sync], + de: [Abstraktionen, Send und Sync], + zh: [抽象,Send和Sync], +)) -#if lang == "en" [ +#tr(( +en: [ None of the above solutions are especially satisfactory. They require `unsafe` blocks which must be very carefully checked and are not ergonomic. Surely we can do better in Rust! -] else if lang == "de" [ +], +de: [ Keine der oben genannten Lösungen ist wirklich zufriedenstellend. Sie erfordern unsichere Blöcke, die sehr sorgfältig geprüft werden müssen und nicht ergonomisch sind. In Rust geht das doch bestimmt besser! -] else if lang == "zh" [ +], +zh: [ 上面的解决方案都不是特别令人满意。它们需要`unsafe`块,`unsafe`块必须要被十分小心地检查且不符合人体工程学。确实,我们在Rust中可以做得更好! -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ We can abstract our counter into a safe interface which can be safely used anywhere else in our code. For this example, we'll use the critical-section counter, but you could do something very similar with atomics. -] else if lang == "de" [ +], +de: [ Wir können unseren Zähler in eine sichere Schnittstelle abstrahieren, die überall im Code sicher verwendet werden kann. In diesem Beispiel verwenden wir den Zähler für kritische Abschnitte, aber mit atomaren Operationen ließe sich etwas sehr Ähnliches realisieren. -] else if lang == "zh" [ +], +zh: [ 我们可以把我们的计数器抽象进一个安全的接口中,它可以在代码的其它地方被安全地使用。在这个例子里,我们将使用临界区的(cirtical-section)计数器,但是你可以用原子操作做一些非常类似的事情。 -] else { todo } +])) #raw(block: true, lang: "rust", "use core::cell::UnsafeCell; use cortex_m::interrupt; -// " + if lang == "en" { - "Our counter is just a wrapper around UnsafeCell, which is the heart +// " + ts(( + en: "Our counter is just a wrapper around UnsafeCell, which is the heart // of interior mutability in Rust. By using interior mutability, we can have // COUNTER be `static` instead of `static mut`, but still able to mutate -// its counter value." - } else if lang == "de" { - "Unser Zaehler ist lediglich ein Wrapper um `UnsafeCell`, das Herzstueck +// its counter value.", + de: "Unser Zaehler ist lediglich ein Wrapper um `UnsafeCell`, das Herzstueck // der „Interior Mutability“ in Rust. Dank dieses Konzepts können wir `COUNTER` // als `static` statt als `static mut` definieren und dennoch den Zaehlerwert -// veraendern." - } else if lang == "zh" { - "我们的计数器只是包围UnsafeCell的一个封装,它是Rust中内部可变性 +// veraendern.", + zh: "我们的计数器只是包围UnsafeCell的一个封装,它是Rust中内部可变性 // (interior mutability)的关键。通过使用内部可变性,我们能让COUNTER // 变成`static`而不是`static mut`,但是仍能改变它的计数器值。" - } else { todos } + " + )) + " struct CSCounter(UnsafeCell); const CS_COUNTER_INIT: CSCounter = CSCounter(UnsafeCell::new(0)); impl CSCounter { pub fn reset(&self, _cs: &interrupt::CriticalSection) { - // " + if lang == "en" { - "By requiring a CriticalSection be passed in, we know we must + // " + ts(( + en: "By requiring a CriticalSection be passed in, we know we must // be operating inside a CriticalSection, and so can confidently - // use this unsafe block (required to call UnsafeCell::get)." - } else if lang == "de" { - "Indem wir die Uebergabe einer `CriticalSection` voraussetzen, wissen + // use this unsafe block (required to call UnsafeCell::get).", + de: "Indem wir die Uebergabe einer `CriticalSection` voraussetzen, wissen // wir, dass wir uns innerhalb einer `CriticalSection` befinden; daher // koennen wir bedenkenlos diesen `unsafe`-Block verwenden (der für den - // Aufruf von `UnsafeCell::get` erforderlich ist)." - } else if lang == "zh" { - "通过要求一个CriticalSection被传递进来,我们知道我们肯定正在一个 - // CriticalSection中操作,且因此可以自信地使用这个unsafe块(调用UnsafeCell::get的前提)。" - } else { todos } + " + // Aufruf von `UnsafeCell::get` erforderlich ist).", + zh: "通过要求一个CriticalSection被传递进来,我们知道我们肯定正在一个 + // CriticalSection中操作,且因此可以自信地使用这个unsafe块(调用UnsafeCell::get的前提)。", + )) + " unsafe { *self.0.get() = 0 }; } @@ -569,26 +623,22 @@ impl CSCounter { } } -// " + if lang == "en" { - "Required to allow static CSCounter. See explanation below." - } else if lang == "de" { - "Erforderlich, um ein statisches CSCounter zu ermoeglichen. Siehe Erlaeuterung -// unten." - } else if lang == "zh" { - "允许静态CSCounter的前提。看下面的解释。" - } else { todos } + " +// " + ts(( + en: "Required to allow static CSCounter. See explanation below.", + de: "Erforderlich, um ein statisches CSCounter zu ermoeglichen. Siehe Erlaeuterung +// unten.", + zh: "允许静态CSCounter的前提。看下面的解释。" + )) + " unsafe impl Sync for CSCounter {} -// " + if lang == "en" { - "COUNTER is no longer `mut` as it uses interior mutability; -// therefore it also no longer requires unsafe blocks to access." - } else if lang == "de" { - "COUNTER ist nicht mehr `mut`, da es „Interior Mutability“ verwendet; daher -// sind fuer den Zugriff auch keine `unsafe`-Bloecke mehr erforderlich." - } else if lang == "zh" { - "COUNTER不再是`mut`的因为它使用内部可变性; +// " + ts(( + en: "COUNTER is no longer `mut` as it uses interior mutability; +// therefore it also no longer requires unsafe blocks to access.", + de: "COUNTER ist nicht mehr `mut`, da es „Interior Mutability“ verwendet; daher +// sind fuer den Zugriff auch keine `unsafe`-Bloecke mehr erforderlich.", + zh: "COUNTER不再是`mut`的因为它使用内部可变性; // 因此访问它也不再需要unsafe块。" - } else { todos } + " + )) + " static COUNTER: CSCounter = CS_COUNTER_INIT; #[entry] @@ -598,11 +648,10 @@ fn main() -> ! { loop { let state = read_signal_level(); if state && !last_state { - // " + if lang in ("en", "de") { - "No unsafe here!" - } else if lang == "zh" { - "这里不用unsafe!" - } else { todos } + " + // " + ts(( + en: "No unsafe here!", + zh: "这里不用unsafe!", + )) + " interrupt::free(|cs| COUNTER.increment(cs)); } last_state = state; @@ -611,48 +660,48 @@ fn main() -> ! { #[interrupt] fn timer() { - // " + if lang == "en" { - "We do need to enter a critical section here just to obtain a valid + // " + ts(( + en: "We do need to enter a critical section here just to obtain a valid // cs token, even though we know no other interrupt could pre-empt - // this one." - } else if lang == "de" { - "Wir muessen hier tatsaechlich einen kritischen Abschnitt betreten, nur um + // this one.", + de: "Wir muessen hier tatsaechlich einen kritischen Abschnitt betreten, nur um // ein gueltiges CS-Token zu erhalten, auch wenn wir wissen, dass kein - // anderer Interrupt diesen unterbrechen koennte." - } else if lang == "zh" { - "这里我们需要进入一个临界区,只是为了传递进一个有效的cs token,尽管我们知道 - // 没有其它中断可以抢占这个中断。 " - } else { todos } + " + // anderer Interrupt diesen unterbrechen koennte.", + zh: "这里我们需要进入一个临界区,只是为了传递进一个有效的cs token,尽管我们知道 + // 没有其它中断可以抢占这个中断。" + )) + " interrupt::free(|cs| COUNTER.reset(cs)); - // " + if lang == "en" { - "We could use unsafe code to generate a fake CriticalSection if we - // really wanted to, avoiding the overhead:" - } else if lang == "de" { - "Wir koennten „unsicheren“ Code (unsafe code) verwenden, um eine + // " + ts(( + en: "We could use unsafe code to generate a fake CriticalSection if we + // really wanted to, avoiding the overhead:", + de: "Wir koennten „unsicheren“ Code (unsafe code) verwenden, um eine // gefaelschte CriticalSection zu erzeugen, falls wir das wirklich wollten, - // und so den Overhead vermeiden:" - } else if lang == "zh" { - "如果我们真的需要,我们可以使用unsafe代码去生成一个假CriticalSection, + // und so den Overhead vermeiden:", + zh: "如果我们真的需要,我们可以使用unsafe代码去生成一个假CriticalSection, // 避免开销:" - } else { todos } + " + )) + " // let cs = unsafe { interrupt::CriticalSection::new() }; } ") -#if lang == "en" [ +#tr(( +en: [ We've moved our `unsafe` code to inside our carefully-planned abstraction, and now our application code does not contain any `unsafe` blocks. -] else if lang == "de" [ +], +de: [ Wir haben unseren `unsafe`-Code in unsere sorgfältig entworfene Abstraktion verlagert; nun enthält unser Anwendungscode keine `unsafe`-Blöcke mehr. -] else if lang == "zh" [ +], +zh: [ 我们已经把我们的`unsafe`代码移进了精心安排的抽象中,现在我们的应用代码不包含任何`unsafe`块。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ This design requires that the application pass a `CriticalSection` token in: these tokens are only safely generated by `interrupt::free`, so by requiring one be passed in, we ensure we are operating inside a critical @@ -661,7 +710,8 @@ fn timer() { runtime overhead associated with `cs`. If we had multiple counters, they could all be given the same `cs`, without requiring multiple nested critical sections. -] else if lang == "de" [ +], +de: [ Dieser Entwurf setzt voraus, dass die Anwendung ein `CriticalSection`-Token übergibt: Da diese Tokens nur sicher durch `interrupt::free` erzeugt werden können, stellen wir durch die @@ -671,15 +721,17 @@ fn timer() { entsteht also kein Laufzeit-Overhead durch `cs`. Hätten wir mehrere Zähler, könnten diese alle dasselbe `cs` verwenden, ohne dass mehrere verschachtelte kritische Abschnitte erforderlich wären. -] else if lang == "zh" [ +], +zh: [ 这个设计要求应用传递一个`CriticalSection` token进来: 这些tokens仅由`interrupt::free`安全地产生,因此通过要求传递进一个`CriticalSection` token,我们确保我们正在一个临界区中操作,不用自己动手锁起来。这个保障由编译器静态地提供: 这将不会带来任何与`cs`有关的运行时消耗。如果我们有多个计数器,它们都可以被指定同一个`cs`,而不用要求多个嵌套的临界区。 -] else { todo } +])) #let url_sendsync = "https://doc.rust-lang.org/nomicon/send-and-sync.html" -#if lang == "en" [ +#tr(( +en: [ This also brings up an important topic for concurrency in Rust: the #link(url_sendsync)[`Send` and `Sync`] traits. To summarise the Rust book, a type is Send when it can safely be @@ -687,7 +739,8 @@ fn timer() { between multiple threads. In an embedded context, we consider interrupts to be executing in a separate thread to the application code, so variables accessed by both an interrupt and the main code must be Sync. -] else if lang == "de" [ +], +de: [ Dies führt zu einem wichtigen Thema der Nebenläufigkeit in Rust: den Traits #link(url_sendsync)[`Send` und `Sync`]. @@ -698,68 +751,80 @@ fn timer() { vom Anwendungscode getrennten Thread ausgeführt werden; daher müssen Variablen, auf die sowohl ein Interrupt als auch der Hauptcode zugreift, `Sync` implementieren. -] else if lang == "zh" [ +], +zh: [ 这也带来了Rust中关于并发的一个重要主题: #link(url_sendsync)[`Send` and `Sync`] traits。总结一下Rust book,当一个类型能够安全地被移动到另一个线程,它是Send,当一个类型能被安全地在多个线程间共享的时候,它是Sync。在一个嵌入式上下文中,我们认为中断是在应用代码的一个独立线程中执行的,因此在一个中断和main代码中都能被访问的变量必须是Sync。 -] else { todo } +])) #let ln_unsafecell = link("https://doc.rust-lang.org/core/cell/struct.UnsafeCell.html")[`UnsafeCell`] -#if lang == "en" [ +#tr(( +en: [ For most types in Rust, both of these traits are automatically derived for you by the compiler. However, because `CSCounter` contains an #ln_unsafecell, it is not Sync, and therefore we could not make a `static CSCounter`: `static` variables _must_ be Sync, since they can be accessed by multiple threads. -] else if lang == "de" [ +], +de: [ Für die meisten Typen in Rust werden diese beiden Traits automatisch vom Compiler für dich abgeleitet. Da `CSCounter` jedoch eine #ln_unsafecell enthält, ist der Typ nicht `Sync`\; folglich konnten wir keinen `static CSCounter` definieren, denn `static`-Variablen _müssen_ `Sync` sein, da von mehreren Threads aus auf sie zugegriffen werden kann. -] else if lang == "zh" [ +], +zh: [ 在Rust中的大多数类型,这两个traits都会由你的编译器为你自动地产生。然而,因为`CSCounter`包含了一个#ln_unsafecell,它不是Sync,因此我们不能使用一个`static CSCounter`: `static` 变量 _必须_ 是Sync,因此它们能被多个线程访问。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ To tell the compiler we have taken care that the `CSCounter` is in fact safe to share between threads, we implement the Sync trait explicitly. As with the previous use of critical sections, this is only safe on single-core platforms: with multiple cores, you would need to go to greater lengths to ensure safety. -] else if lang == "de" [ +], +de: [ Um dem Compiler mitzuteilen, dass wir sichergestellt haben, dass der `CSCounter` tatsächlich gefahrlos zwischen Threads geteilt werden kann, implementieren wir das `Sync`-Trait explizit. Wie schon bei der früheren Verwendung kritischer Abschnitte ist dies nur auf Single-Core-Plattformen sicher; bei mehreren Kernen wäre ein deutlich höherer Aufwand erforderlich, um die Sicherheit zu gewährleisten. -] else if lang == "zh" [ +], +zh: [ 为了告诉编译器我们已经注意到`CSCounter`事实上在线程间共享是安全的,我们显式地实现了Sync trait。与之前使用的临界区一样,这只在单核平台上是安全的: 对于多核,你需要做更多的事来确保安全。 -] else { todo } +])) -== #(if lang == "en" [Mutexes] - else if lang == "de" [Mutexe] - else if lang == "zh" [互斥量(Mutexs)] - else { todo }) +== #tr(( + en: [Mutexes], + de: [Mutexe], + zh: [互斥量(Mutexs)], +)) -#if lang == "en" [ +#tr(( +en: [ We've created a useful abstraction specific to our counter problem, but there are many common abstractions used for concurrency. -] else if lang == "de" [ +], +de: [ Wir haben eine nützliche, speziell auf unser Zählerproblem zugeschnittene Abstraktion entwickelt; es gibt jedoch viele gängige Abstraktionen für Nebenläufigkeit. -] else if lang == "zh" [ +], +zh: [ 我们已经为我们的计数器问题创造了一个有用的抽象,但是关于并发这里还存在许多通用的抽象。 -] else { todo } +])) #let ln_drop = link("https://doc.rust-lang.org/core/ops/trait.Drop.html")[`Drop`] -#if lang == "en" [ +#tr(( +en: [ One such _synchronisation primitive_ is a mutex, short for mutual exclusion. These constructs ensure exclusive access to a variable, such as our counter. A thread can attempt to _lock_ (or _acquire_) @@ -770,7 +835,8 @@ fn timer() { _releases_) the mutex, allowing another thread to lock it. In Rust, we would usually implement the unlock using the #ln_drop trait to ensure it is always released when the mutex goes out of scope. -] else if lang == "de" [ +], +de: [ Ein solches _Synchronisationsprimitiv_ ist der Mutex (kurz für „mutual exclusion", also gegenseitiger Ausschluss). Diese Konstrukte gewährleisten den exklusiven Zugriff auf eine Variable, wie etwa unseren @@ -785,7 +851,8 @@ fn timer() { #ln_drop;-Traits implementieren; so ist sichergestellt, dass der Mutex immer freigegeben wird, sobald er den Gültigkeitsbereich (Scope) verlässt. -] else if lang == "zh" [ +], +zh: [ 一个互斥量(mutex),互斥(mutual exclusion)的缩写,就是这样的一个 _同步原语_ 。这些构造确保了对一个变量的排他访问,比如我们的计数器。一个线程会尝试 @@ -794,9 +861,10 @@ fn timer() { _unlocks_ (或者 _releases_) 互斥量,允许其它线程锁住它。在Rust中,我们通常使用#ln_drop trait实现unlock去确保当互斥量超出作用域时它总是被释放。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Using a mutex with interrupt handlers can be tricky: it is not normally acceptable for the interrupt handler to block, and it would be especially disastrous for it to block waiting for the main thread to @@ -804,7 +872,8 @@ fn timer() { will never release the lock because execution stays in the interrupt handler). Deadlocking is not considered unsafe: it is possible even in safe Rust. -] else if lang == "de" [ +], +de: [ Die Verwendung eines Mutex in Interrupt-Handlern kann tückisch sein: Normalerweise darf ein Interrupt-Handler nicht blockieren. Besonders fatal wäre es, wenn er blockieren würde, während er darauf wartet, dass @@ -813,42 +882,49 @@ fn timer() { freigeben, da die Ausführung im Interrupt-Handler verbleibt). Ein Deadlock gilt nicht als „unsicher" (unsafe): Er ist selbst in sicherem Rust möglich. -] else if lang == "zh" [ +], +zh: [ 将中断处理函数与一个互斥量一起使用可能有点棘手: 阻塞中断处理函数通常是不可接受的,如果它阻塞等待main线程去释放一个锁,那将是一场灾难。因为我们会 _死锁_ (因为执行停留在中断处理函数中,主线程将永远不会释放锁)。死锁被认为是不安全的: 即使在安全的Rust中这也是可能发生的。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ To avoid this behaviour entirely, we could implement a mutex which requires a critical section to lock, just like our counter example. So long as the critical section must last as long as the lock, we can be sure we have exclusive access to the wrapped variable without even needing to track the lock/unlock state of the mutex. -] else if lang == "de" [ +], +de: [ Um dieses Verhalten vollständig zu vermeiden, könnten wir einen Mutex implementieren, der für das Sperren eine kritische Sektion erfordert -- genau wie in unserem Zähler-Beispiel. Solange die Dauer der kritischen Sektion der Dauer der Sperre entspricht, ist sichergestellt, dass wir exklusiven Zugriff auf die gekapselte Variable haben, ohne den Sperrstatus des Mutex explizit nachverfolgen zu müssen. -] else if lang == "zh" [ +], +zh: [ 为了完全避免这个行为,我们可以实现一个要求临界区的互斥量去锁住,就像我们的计数器例子一样。临界区的存在时间必须和锁存在的时间一样长,我们能确保我们对被封装的变量有排他式访问,甚至不需要跟踪互斥量的 lock/unlock 状态。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ This is in fact done for us in the `cortex_m` crate! We could have written our counter using it: -] else if lang == "de" [ +], +de: [ Genau dies wird uns bereits durch die `cortex_m`-Crate abgenommen! Wir hätten unseren Zähler auch unter Verwendung dieser Crate implementieren können: -] else if lang == "zh" [ +], +zh: [ 实际上我们在 `cortex_m` crate中就是这么做的!我们可以用它来写入我们的计数器: -] else { todo } +])) #raw(block: true, lang: "rust", "use core::cell::Cell; @@ -872,20 +948,19 @@ fn main() -> ! { #[interrupt] fn timer() { - // " + if lang == "en" { - "We still need to enter a critical section here to satisfy the Mutex." - } else if lang == "de" { - "Wir muessen hier noch einen kritischen Abschnitt betreten, um die - // Mutex-Bedingung zu erfuellen." - } else if lang == "zh" { - "这里我们仍然需要进入一个临界区去满足互斥量。" - } else { todos } + " + // " + ts(( + en: "We still need to enter a critical section here to satisfy the Mutex.", + de: "Wir muessen hier noch einen kritischen Abschnitt betreten, um die + // Mutex-Bedingung zu erfuellen.", + zh: "这里我们仍然需要进入一个临界区去满足互斥量。", + )) + " interrupt::free(|cs| COUNTER.borrow(cs).set(0)); } ") #let ln_cell = link("https://doc.rust-lang.org/core/cell/struct.Cell.html")[`Cell`] -#if lang == "en" [ +#tr(( +en: [ We're now using #ln_cell, which along with its sibling `RefCell` is used to provide safe interior mutability. We've already seen `UnsafeCell` which is the bottom layer of @@ -896,7 +971,8 @@ fn timer() { since it is not Sync, it cannot be shared between threads. These constraints mean it's safe to use, but we couldn't use it directly in a `static` variable as a `static` must be Sync. -] else if lang == "de" [ +], +de: [ Wir verwenden nun #ln_cell;; dieses bietet -- ebenso wie sein Pendant `RefCell` -- eine sichere Form der „Interior Mutability" (internen Veränderbarkeit). Wir haben bereits @@ -911,54 +987,64 @@ fn timer() { Verwendung sicher, allerdings lässt sie sich nicht direkt in einer `static`-Variablen einsetzen, da `static`-Elemente die Eigenschaft `Sync` erfüllen müssen. -] else if lang == "zh" [ +], +zh: [ 我们现在使用了#link("https://doc.rust-lang.org/core/cell/struct.Cell.html")[`Cell`],它与它的兄弟`RefCell`一起被用于提供safe的内部可变性。我们已经见过`UnsafeCell`了,在Rust中它是内部可变性的底层: 它允许你去获得对某个值的多个可变引用,但是只能与不安全的代码一起工作。一个`Cell`像一个`UnsafeCell`一样但是它提供了一个安全的接口: 它只允许拷贝现在的值或者替换它,不允许获取一个引用,因此它不是Sync,它不能被在线程间共享。这些限制意味着它用起来是safe的,但是我们不能直接将它用于`static`变量因为一个`static`必须是Sync。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ So why does the example above work? The `Mutex` implements Sync for any `T` which is Send --- such as a `Cell`. It can do this safely because it only gives access to its contents during a critical section. We're therefore able to get a safe counter with no unsafe code at all! -] else if lang == "de" [ +], +de: [ Warum funktioniert das obige Beispiel? Der `Mutex` implementiert `Sync` für jedes `T`, das `Send` ist -- wie beispielsweise eine `Cell`. Dies ist sicher möglich, da der Zugriff auf den Inhalt nur während eines kritischen Abschnitts gewährt wird. Dadurch erhalten wir einen sicheren Zähler ohne jeglichen unsicheren Code! -] else if lang == "zh" [ +], +zh: [ 因此为什么上面的例子可以工作?`Mutex`对于任何是Send的`T`实现了Sync - 比如一个`Cell`。因为它只能在临界区对它的内容进行访问,所以它这么做是safe的。因此我们可以即使没有一点unsafe的代码我们也能获取一个safe的计数器! -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ This is great for simple types like the `u32` of our counter, but what about more complex types which are not Copy? An extremely common example in an embedded context is a peripheral struct, which generally is not Copy. For that, we can turn to `RefCell`. -] else if lang == "de" [ +], +de: [ Das ist ideal für einfache Typen wie den `u32` unseres Zählers. Was aber ist mit komplexeren Typen, die nicht `Copy` sind? Ein sehr häufiges Beispiel in eingebetteten Systemen ist eine Peripheriestruktur, die in der Regel nicht `Copy` ist. Für diesen Fall können wir `RefCell` verwenden. -] else if lang == "zh" [ +], +zh: [ 对于我们的简单类型,像是我们的计数器的`u32`来说是很棒的,但是对于更复杂的不能拷贝的类型呢?在一个嵌入式上下文中一个极度常见的例子是一个外设结构体,通常它们不是Copy。针对那种情况,我们可以使用`RefCell`。 -] else { todo } +])) -== #(if lang == "en" [Sharing Peripherals] - else if lang == "de" [Gemeinsame Nutzung von Peripheriegeräten] - else if lang == "zh" [共享外设] - else { todo }) +== #tr(( + en: [Sharing Peripherals], + de: [Gemeinsame Nutzung von Peripheriegeräten], + zh: [共享外设], +)) -#if lang == "en" [ +#tr(( +en: [ Device crates generated using `svd2rust` and similar abstractions provide safe access to peripherals by enforcing that only one instance of the peripheral struct can exist at a time. This ensures safety, but makes it difficult to access a peripheral from both the main thread and an interrupt handler. -] else if lang == "de" [ +], +de: [ Mit `svd2rust` und ähnlichen Abstraktionen erzeugte Device-Crates ermöglichen einen sicheren Zugriff auf Peripheriekomponenten, indem sie sicherstellen, dass zu jedem Zeitpunkt nur eine einzige Instanz der @@ -966,19 +1052,22 @@ fn timer() { die Sicherheit, erschwert jedoch den gleichzeitigen Zugriff auf eine Peripheriekomponente sowohl aus dem Haupt-Thread als auch aus einem Interrupt-Handler heraus. -] else if lang == "zh" [ +], +zh: [ 使用`svd2rust`生成的设备crates和相似的抽象,通过强制要求同时只能存在一个外设结构体的实例,提供了对外设的安全的访问。这个确保了安全性,但是使得它很难从main线程和一个中断处理函数一起访问一个外设。 -] else { todo } +])) #let ln_refcell = link("https://doc.rust-lang.org/core/cell/struct.RefCell.html")[`RefCell`] -#if lang == "en" [ +#tr(( +en: [ To safely share peripheral access, we can use the `Mutex` we saw before. We'll also need to use #ln_refcell, which uses a runtime check to ensure only one reference to a peripheral is given out at a time. This has more overhead than the plain `Cell`, but since we are giving out references rather than copies, we must be sure only one exists at a time. -] else if lang == "de" [ +], +de: [ Um den Zugriff auf Peripheriekomponenten sicher zu teilen, können wir den bereits bekannten `Mutex` verwenden. Zudem benötigen wir #ln_refcell;; @@ -988,24 +1077,28 @@ fn timer() { einfachen `Cell`, doch da wir Referenzen anstatt Kopien weitergeben, müssen wir sicherstellen, dass zu jedem Zeitpunkt nur eine einzige Referenz existiert. -] else if lang == "zh" [ +], +zh: [ 为了安全地共享对外设的访问,我们能使用我们之前看到的`Mutex`。我们也将需要使用#ln_refcell,它使用一个运行时检查去确保对一个外设每次只有一个引用被给出。这个比纯`Cell`消耗更多,但是因为我们正给出引用而不是拷贝,我们必须确保每次只有一个引用存在。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Finally, we'll also have to account for somehow moving the peripheral into the shared variable after it has been initialised in the main code. To do this we can use the `Option` type, initialised to `None` and later set to the instance of the peripheral. -] else if lang == "de" [ +], +de: [ Schließlich müssen wir auch berücksichtigen, wie die Peripheriekomponente in die gemeinsam genutzte Variable übertragen werden kann, nachdem sie im Hauptcode initialisiert wurde. Hierfür können wir den Typ `Option` verwenden, der zunächst mit `None` initialisiert und später auf die Instanz der Peripheriekomponente gesetzt wird. -] else if lang == "zh" [ +], +zh: [ 最终,我们也必须考虑在main代码中初始化外设后,如何将外设移到共享变量中。为了做这个,我们使用`Option`类型,初始成`None`,之后设置成外设的实例。 -] else { todo } +])) #raw(block: true, lang: "rust", "use core::cell::RefCell; @@ -1017,89 +1110,75 @@ static MY_GPIO: Mutex>> = #[entry] fn main() -> ! { - // " + if lang == "en" { - "Obtain the peripheral singletons and configure it. + // " + ts(( + en: "Obtain the peripheral singletons and configure it. // This example is from an svd2rust-generated crate, but - // most embedded device crates will be similar." - } else if lang == "de" { - "Die Peripherie-Singletons abrufen und konfigurieren. + // most embedded device crates will be similar.", + de: "Die Peripherie-Singletons abrufen und konfigurieren. // Dieses Beispiel stammt aus einer mit svd2rust erstellten Crate, die - // meisten Crates für eingebettete Geräte sind jedoch ähnlich." - } else if lang == "zh" { - "获得外设的单例并配置它。这个例子来自一个svd2rust生成的crate, - // 但是大多数的嵌入式设备crates都相似。" - } else { todos } + " + // meisten Crates für eingebettete Geräte sind jedoch ähnlich.", + zh: "获得外设的单例并配置它。这个例子来自一个svd2rust生成的crate, + // 但是大多数的嵌入式设备crates都相似。", + )) + " let dp = stm32f405::Peripherals::take().unwrap(); let gpioa = &dp.GPIOA; - // " + if lang == "en" { - "Some sort of configuration function. - // Assume it sets PA0 to an input and PA1 to an output." - } else if lang == "de" { - "Eine Art Konfigurationsfunktion. + // " + ts(( + en: "Some sort of configuration function. + // Assume it sets PA0 to an input and PA1 to an output.", + de: "Eine Art Konfigurationsfunktion. // Gehen wir davon aus, dass es PA0 als Eingang und PA1 als Ausgang - // konfiguriert." - } else if lang == "zh" { - "某个配置函数。假设它把PA0设置成一个输入和把PA1设置成一个输出。" - } else { todos } + " + // konfiguriert.", + zh: "某个配置函数。假设它把PA0设置成一个输入和把PA1设置成一个输出。", + )) + " configure_gpio(gpioa); - // " + if lang == "en" { - "Store the GPIOA in the mutex, moving it." - } else if lang == "de" { - "Speichere GPIOA im Mutex und verschiebe es dabei." - } else { todos } + " + // " + ts(( + en: "Store the GPIOA in the mutex, moving it.", + de: "Speichere GPIOA im Mutex und verschiebe es dabei.", + zh: "把GPIOA存进互斥量中,移动它。", + )) + " interrupt::free(|cs| MY_GPIO.borrow(cs).replace(Some(dp.GPIOA))); - // " + if lang == "en" { - "We can no longer use `gpioa` or `dp.GPIOA`, and instead have to" - } else if lang == "de" { - "Wir koennen `gpioa` oder `dp.GPIOA` nicht mehr verwenden, sondern - // muessen stattdessen ueber den Mutex darauf zugreifen." - } else if lang == "zh" { - "把GPIOA存进互斥量中,移动它。" - } else { todos } + " + // " + ts(( + en: "We can no longer use `gpioa` or `dp.GPIOA`, and instead have to // access it via the mutex. - // " + if lang == "en" { - "Be careful to enable the interrupt only after setting MY_GPIO: + // Be careful to enable the interrupt only after setting MY_GPIO: // otherwise the interrupt might fire while it still contains None, - // and as-written (with `unwrap()`), it would panic." - } else if lang == "de" { - "Achten Sie darauf, den Interrupt erst nach dem Setzen von MY_GPIO zu + // and as-written (with `unwrap()`), it would panic.", + de: "Wir koennen `gpioa` oder `dp.GPIOA` nicht mehr verwenden, sondern + // muessen stattdessen ueber den Mutex darauf zugreifen. + + // Achten Sie darauf, den Interrupt erst nach dem Setzen von MY_GPIO zu // aktivieren: // Andernfalls koennte der Interrupt ausgeloest werden, solange MY_GPIO // noch None enthaelt, was – wie implementiert (mit `unwrap()`) – zu einer - // Panic fuehren wuerde." - } else if lang == "zh" { - "我可以不再用`gpioa`或者`dp.GPIOA`,反而必须通过互斥量访问它。 + // Panic fuehren wuerde.", + zh: "我可以不再用`gpioa`或者`dp.GPIOA`,反而必须通过互斥量访问它。 // 请注意,只有在设置MY_GPIO后才能使能中断: 要不然当MY_GPIO还是包含None的时候, // 中断可能会发生,然后像上面写的那样操作(使用`unwrap()`),它将发生运行时恐慌。" - } else { todos } + " + )) + " set_timer_1hz(); let mut last_state = false; loop { - // " + if lang == "en" { - "We'll now read state as a digital input, via the mutex" - } else if lang == "de" { - "Wir lesen den Status nun als digitalen Eingang ueber den Mutex aus." - } else if lang == "zh" { - "我们现在将通过互斥量,读取其作为数字输入时的状态。" - } else { todos } + " + // " + ts(( + en: "We'll now read state as a digital input, via the mutex", + de: "Wir lesen den Status nun als digitalen Eingang ueber den Mutex aus.", + zh: "我们现在将通过互斥量,读取其作为数字输入时的状态。", + )) + " let state = interrupt::free(|cs| { let gpioa = MY_GPIO.borrow(cs).borrow(); gpioa.as_ref().unwrap().idr.read().idr0().bit_is_set() }); if state && !last_state { - // " + if lang == "en" { - "Set PA1 high if we've seen a rising edge on PA0." - } else if lang == "de" { - "Setze PA1 auf High, wenn an PA0 eine steigende Flanke erkannt - // wurde." - } else if lang == "zh" { - "如果我们在PA0上已经看到了一个上升沿,拉高PA1。" - } else { todos } + " + // " + ts(( + en: "Set PA1 high if we've seen a rising edge on PA0.", + de: "Setze PA1 auf High, wenn an PA0 eine steigende Flanke erkannt + // wurde.", + zh: "如果我们在PA0上已经看到了一个上升沿,拉高PA1。" + )) + " interrupt::free(|cs| { let gpioa = MY_GPIO.borrow(cs).borrow(); gpioa.as_ref().unwrap().odr.modify(|_, w| w.odr1().set_bit()); @@ -1111,47 +1190,47 @@ fn main() -> ! { #[interrupt] fn timer() { - // " + if lang == "en" { - "This time in the interrupt we'll just clear PA0." - } else if lang == "de" { - "Diesmal setzen wir im Interrupt lediglich PA0 zurueck." - } else if lang == "zh" { - "这次在中断中,我们将清除PA0。" - } else { todos } + " + // " + ts(( + en: "This time in the interrupt we'll just clear PA0.", + de: "Diesmal setzen wir im Interrupt lediglich PA0 zurueck.", + zh: "这次在中断中,我们将清除PA0。", + )) + " interrupt::free(|cs| { - // " + if lang == "en" { - "We can use `unwrap()` because we know the interrupt wasn't enabled + // " + ts(( + en: "We can use `unwrap()` because we know the interrupt wasn't enabled // until after MY_GPIO was set; otherwise we should handle the potential - // for a None value." - } else if lang == "de" { - "Wir koennen `unwrap()` verwenden, da wir wissen, dass der Interrupt + // for a None value.", + de: "Wir koennen `unwrap()` verwenden, da wir wissen, dass der Interrupt // erst aktiviert wurde, nachdem `MY_GPIO` gesetzt war; andernfalls - // muessten wir den moeglichen Fall eines `None`-Werts behandeln." - } else if lang == "zh" { - "我们可以使用`unwrap()` 因为我们知道直到MY_GPIO被设置后,中断都是禁用的; + // muessten wir den moeglichen Fall eines `None`-Werts behandeln.", + zh: "我们可以使用`unwrap()` 因为我们知道直到MY_GPIO被设置后,中断都是禁用的; // 否则我应该处理会出现一个None值的潜在可能" - } else { todos } + " + )) + " let gpioa = MY_GPIO.borrow(cs).borrow(); gpioa.as_ref().unwrap().odr.modify(|_, w| w.odr1().clear_bit()); }); } ") -#if lang == "en" [ +#tr(( +en: [ That's quite a lot to take in, so let's break down the important lines. -] else if lang == "de" [ +], +de: [ Das ist eine ganze Menge, die man erst einmal verarbeiten muss -- schauen wir uns also die wichtigen Zeilen genauer an. -] else if lang == "zh" [ +], +zh: [ 这需要理解的内容很多,所以让我们把重要的内容分解一下。 -] else { todo } +])) ```rust static MY_GPIO: Mutex>> = Mutex::new(RefCell::new(None)); ``` -#if lang == "en" [ +#tr(( +en: [ Our shared variable is now a `Mutex` around a `RefCell` which contains an `Option`. The `Mutex` ensures we only have access during a critical section, and therefore makes the variable Sync, even though a plain @@ -1160,7 +1239,8 @@ static MY_GPIO: Mutex>> = us initialise this variable to something empty, and only later actually move the variable in. We cannot access the peripheral singleton statically, only at runtime, so this is required. -] else if lang == "de" [ +], +de: [ Unsere gemeinsam genutzte Variable ist nun ein `Mutex`, der einen `RefCell` umschließt, welcher wiederum ein `Option` enthält. Der `Mutex` stellt sicher, dass der Zugriff nur innerhalb eines kritischen @@ -1172,27 +1252,31 @@ static MY_GPIO: Mutex>> = eigentlichen Wert erst später hineinzubewegen. Da wir nicht statisch, sondern nur zur Laufzeit auf das Peripherie-Singleton zugreifen können, ist dieses Vorgehen erforderlich. -] else if lang == "zh" [ +], +zh: [ 我们的共享变量现在是一个包围了一个`RefCell`的`Mutex`,`RefCell`包含一个`Option`。`Mutex`确保只在一个临界区中的时候可以访问,因此使变量变成了Sync,甚至即使一个纯`RefCell`不是Sync。`RefCell`赋予了我们引用的内部可变性,我们将需要使用我们的`GPIOA`。`Option`让我们可以初始化这个变量成空的东西,只在随后实际移动变量进来。只有在运行时,我们才能静态地访问外设单例,因此这是必须的。 -] else { todo } +])) ```rust interrupt::free(|cs| MY_GPIO.borrow(cs).replace(Some(dp.GPIOA))); ``` -#if lang == "en" [ +#tr(( +en: [ Inside a critical section we can call `borrow()` on the mutex, which gives us a reference to the `RefCell`. We then call `replace()` to move our new value into the `RefCell`. -] else if lang == "de" [ +], +de: [ Innerhalb eines kritischen Abschnitts können wir `borrow()` auf dem Mutex aufrufen, was uns eine Referenz auf die `RefCell` liefert. Anschließend rufen wir `replace()` auf, um unseren neuen Wert in die `RefCell` zu verschieben. -] else if lang == "zh" [ +], +zh: [ 在一个临界区中,我们可以在互斥量上调用`borrow()`,其给了我们一个指向`RefCell`的引用。然后我们调用`replace()`去移动我们的新值进来`RefCell`。 -] else { todo } +])) ```rust interrupt::free(|cs| { @@ -1201,13 +1285,15 @@ interrupt::free(|cs| { }); ``` -#if lang == "en" [ +#tr(( +en: [ Finally, we use `MY_GPIO` in a safe and concurrent fashion. The critical section prevents the interrupt firing as usual, and lets us borrow the mutex. The `RefCell` then gives us an `&Option`, and tracks how long it remains borrowed - once that reference goes out of scope, the `RefCell` will be updated to indicate it is no longer borrowed. -] else if lang == "de" [ +], +de: [ Schließlich verwenden wir `MY_GPIO` auf sichere und nebenläufige Weise. Der kritische Abschnitt verhindert wie üblich das Auslösen von Interrupts und ermöglicht es uns, den Mutex auszuleihen. Die `RefCell` @@ -1215,36 +1301,43 @@ interrupt::free(|cs| { Ausleihe; sobald die Referenz ihren Gültigkeitsbereich verlässt, wird der Status der `RefCell` aktualisiert, um anzuzeigen, dass sie nicht mehr ausgeliehen ist. -] else if lang == "zh" [ +], +zh: [ 最终,我们用一种安全和并发的方式使用`MY_GPIO`。临界区禁止了中断像往常一样发生,让我们借用互斥量。`RefCell`然后给了我们一个`&Option`并追踪它还要借用多久 - 一旦引用超出作用域,`RefCell`将会被更新去指出引用不再被借用。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Since we can't move the `GPIOA` out of the `&Option`, we need to convert it to an `&Option<&GPIOA>` with `as_ref()`, which we can finally `unwrap()` to obtain the `&GPIOA` which lets us modify the peripheral. -] else if lang == "de" [ +], +de: [ Da wir das `GPIOA`-Objekt nicht aus dem `&Option` herausverschieben können, müssen wir es mittels `as_ref()` in ein `&Option<&GPIOA>` umwandeln. Dieses können wir schließlich mit `unwrap()` auflösen, um das `&GPIOA` zu erhalten, das uns den Zugriff zur Modifikation der Peripherieeinheit ermöglicht. -] else if lang == "zh" [ +], +zh: [ 因为我不能把`GPIOA`移出`&Option`,我们需要用`as_ref()`将它转换成一个`&Option<&GPIOA>`,最终我们能使用`unwrap()`获得`&GPIOA`,其让我们可以修改外设。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ If we need a mutable reference to a shared resource, then `borrow_mut` and `deref_mut` should be used instead. The following code shows an example using the TIM2 timer. -] else if lang == "de" [ +], +de: [ Wenn wir eine veränderbare Referenz auf eine gemeinsam genutzte Ressource benötigen, sollten stattdessen `borrow_mut` und `deref_mut` verwendet werden. Der folgende Code zeigt ein Beispiel unter Verwendung des TIM2-Timers. -] else if lang == "zh" [ +], +zh: [ 如果我们需要一个共享的资源的可变引用,那么`borrow_mut`和`deref_mut`应该被使用。下面的代码展示了一个使用TIM2计时器的例子。 -] else { todo } +])) #raw(block: true, lang: "rust", "use core::cell::RefCell; @@ -1261,18 +1354,16 @@ fn main() -> ! { let mut cp = cm::Peripherals::take().unwrap(); let dp = stm32f405::Peripherals::take().unwrap(); - // " + if lang == "en" { - "Some sort of timer configuration function. + // " + ts(( + en: "Some sort of timer configuration function. // Assume it configures the TIM2 timer, its NVIC interrupt, - // and finally starts the timer." - } else if lang == "de" { - "Eine Art Timer-Konfigurationsfunktion. + // and finally starts the timer.", + de: "Eine Art Timer-Konfigurationsfunktion. // Angenommen, sie konfiguriert den TIM2-Timer sowie dessen NVIC-Interrupt - // und startet schließlich den Timer." - } else if lang == "zh" { - "某个计时器配置函数。假设它配置了TIM2计时器和它的NVIC中断, + // und startet schließlich den Timer.", + zh: "某个计时器配置函数。假设它配置了TIM2计时器和它的NVIC中断, // 最终启动计时器。" - } else { todos } + " + )) + " let tim = configure_timer_interrupt(&mut cp, dp); interrupt::free(|cs| { @@ -1294,20 +1385,24 @@ fn timer() { } ") -#if lang == "en" [ +#tr(( +en: [ Whew! This is safe, but it is also a little unwieldy. Is there anything else we can do? -] else if lang == "de" [ +], +de: [ Puh! Das ist zwar sicher, aber auch etwas unhandlich. Können wir sonst noch etwas tun? -] else if lang == "zh" [ +], +zh: [ 呼!这是安全的,但也有点笨拙。我们还能做些什么吗? -] else { todo } +])) == RTIC #let url_rtic = "https://github.com/rtic-rs/cortex-m-rtic" -#if lang == "en" [ +#tr(( +en: [ One alternative is the #link(url_rtic)[RTIC framework], short for Real Time Interrupt-driven Concurrency. It enforces static priorities and tracks accesses to `static mut` variables ("resources") to statically ensure @@ -1316,7 +1411,8 @@ fn timer() { counting (as in `RefCell`). This has a number of advantages such as guaranteeing no deadlocks and giving extremely low time and memory overhead. -] else if lang == "de" [ +], +de: [ Eine Alternative ist das #link(url_rtic)[RTIC-Framework] (kurz für _Real Time Interrupt-driven Concurrency_). Es erzwingt statische Prioritäten @@ -1327,92 +1423,106 @@ fn timer() { (wie bei `RefCell`) entstünde. Dies bietet eine Reihe von Vorteilen, wie etwa die Garantie von Deadlock-Freiheit sowie einen extrem geringen Zeit- und Speicheraufwand. -] else if lang == "zh" [ +], +zh: [ 另一个方法是使用#link(url_rtic)[RTIC框架],Real Time Interrupt-driven Concurrency的缩写。它强制执行静态优先级并追踪对`static mut`变量("资源")的访问去确保共享资源总是能被安全地访问,而不需要总是进入临界区和使用引用计数带来的消耗(如`RefCell`中所示)。这有许多好处,比如保证没有死锁且时间和内存的消耗极度低。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ RTIC comes with as asynchronous executor, so your software tasks are `async` functions where you can use `async` APIs in addition to regular synchronous APIs. -] else if lang == "de" [ +], +de: [ RTIC bietet einen asynchronen Executor, sodass Ihre Software-Tasks als `async`-Funktionen implementiert sind; darin können Sie neben herkömmlichen synchronen APIs auch `async`-APIs nutzen. -] else { todo } +])) #let url_rtic_doc = "https://rtic.rs" -#if lang == "en" [ +#tr(( +en: [ The framework also includes other features like message passing, which reduces the need for explicit shared state, and the ability to schedule tasks to run at a given time, which can be used to implement periodic tasks. Check out #link(url_rtic_doc)[the documentation] for more information! -] else if lang == "de" [ +], +de: [ Das Framework umfasst zudem Funktionen wie Message Passing -- was den Bedarf an explizit gemeinsam genutztem Zustand (Shared State) verringert -- sowie die Möglichkeit, Tasks für einen bestimmten Zeitpunkt zu planen, womit sich beispielsweise periodische Aufgaben realisieren lassen. Weitere Informationen finden Sie in #link(url_rtic_doc)[der Dokumentation]! -] else if lang == "zh" [ +], +zh: [ 这个框架也包括了其它的特性,像是消息传递(message passing),消息传递减少了对显式共享状态的需要,还提供了在一个给定时间调度任务去运行的功能,这功能能被用来实现周期性的任务。看下#link(url_rtic_doc)[文档]可以知道更多的信息! -] else { todo } +])) == Embassy #let url_executor = "https://docs.rs/embassy-executor/latest/embassy_executor/" -#if lang == "en" [ +#tr(( +en: [ Embassy is an ecosystem of libraries which focus on using the `async` / `await` syntax included in Rust for concurrency. The core of embassy is its #link(url_executor)[asynchronous executor] which supports most common MCU architectures. -] else if lang == "de" [ +], +de: [ Embassy ist ein Ökosystem von Bibliotheken, die sich auf die Nutzung der in Rust enthaltenen `async`/`await`-Syntax für Nebenläufigkeit konzentrieren. Das Herzstück von Embassy ist sein #link(url_executor)[asynchroner Executor], der die gängigsten MCU-Architekturen unterstützt. -] else { todo } +])) #let url_em_time = "https://docs.rs/embassy-time/latest/embassy_time/" #let ln_en_sync = link("https://docs.embassy.dev/embassy-sync/git/default/index.html")[embassy-sync] -#if lang == "en" [ +#tr(( +en: [ embassy also takes a battery-included approach and offers many other components, for example: - #link(url_em_time)[Time library] - Various HAL libraries which also provide the time library support. - #ln_en_sync for synchronization primitives -] else if lang == "de" [ +], +de: [ Embassy verfolgt zudem einen „Batteries-included"-Ansatz und bietet viele weitere Komponenten an, zum Beispiel: - #link(url_em_time)[Zeit-Bibliothek] - Verschiedene HAL-Bibliotheken, die auch Unterstützung für die Zeitbibliothek bieten. - #ln_en_sync für Synchronisationsprimitive -] else { todo } +])) #let url_em_site = "https://embassy.dev/" #let url_em_book = "https://embassy.dev/book/" -#if lang == "en" [ +#tr(( +en: [ You can check the #link(url_em_site)[website] and the #link(url_em_book)[book] for more information. -] else if lang == "de" [ +], +de: [ Weitere Informationen finden Sie auf der #link(url_em_site)[Website] und im #link(url_em_book)[Buch]. -] else { todo } +])) -== #(if lang == "en" [Real Time Operating Systems] - else if lang == "de" [Echtzeitbetriebssysteme] - else if lang == "zh" [实时操作系统] - else { todo }) +== #tr(( + en: [Real Time Operating Systems], + de: [Echtzeitbetriebssysteme], + zh: [实时操作系统], +)) #let ln_freertos = link("https://freertos.org/")[FreeRTOS] #let ln_chibi = link("http://chibios.org/")[ChibiOS] -#if lang == "en" [ +#tr(( +en: [ Another common model for embedded concurrency is the real-time operating system (RTOS). While currently less well explored in Rust, they are widely used in traditional embedded development. Open source examples @@ -1422,7 +1532,8 @@ fn timer() { based on a regular timer or interrupts (preemptive multitasking). The RTOS typically provide mutexes and other synchronisation primitives, and often interoperate with hardware features such as DMA engines. -] else if lang == "de" [ +], +de: [ Ein weiteres verbreitetes Modell für Nebenläufigkeit in eingebetteten Systemen ist das Echtzeitbetriebssystem (RTOS). Während diese in Rust bislang weniger stark erforscht sind, kommen sie in der klassischen @@ -1435,29 +1546,35 @@ fn timer() { Typischerweise stellen RTOS Mechanismen wie Mutexe und andere Synchronisationsprimitive bereit und interagieren häufig mit Hardwarefunktionen wie DMA-Controllern. -] else if lang == "zh" [ +], +zh: [ #todoupd("zh") 与嵌入式并发有关的另一个模型是实时操作系统(RTOS)。虽然现在在Rust中的研究较少,但是它们被广泛用于传统的嵌入式开发。开源的例子包括#ln_freertos;和#ln_chibi。这些RTOSs提供对运行多个应用线程的支持,CPU在这些线程间进行切换,切换要么发生在当线程让出控制权的时候(被称为非抢占式多任务),要么是基于一个常规计时器或者中断(抢占式多任务)。RTOS通常提供互斥量或者其它的同步原语,经常与硬件功能相互使用,比如DMA引擎。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ At the time of writing, there are not many Rust RTOS examples to point to, but it's an interesting area so watch this space! -] else if lang == "de" [ +], +de: [ Zum jetzigen Zeitpunkt gibt es noch nicht viele Beispiele für Rust-RTOS, auf die man verweisen könnte; es handelt sich jedoch um ein interessantes Gebiet -- bleiben Sie also dran! -] else if lang == "zh" [ +], +zh: [ 在撰写本文时,没有太多的Rust RTOS示例可供参考,但这是一个有趣的领域,所以请关注这块! -] else { todo } +])) -== #(if lang == "en" [Multiple Cores] - else if lang == "de" [Mehrere Kerne] - else if lang == "zh" [多个核心] - else { todo }) +== #tr(( + en: [Multiple Cores], + de: [Mehrere Kerne], + zh: [多个核心], +)) -#if lang == "en" [ +#tr(( +en: [ It is becoming more common to have two or more cores in embedded processors, which adds an extra layer of complexity to concurrency. All the examples using a critical section (including the @@ -1465,7 +1582,8 @@ fn timer() { the interrupt thread, but on a multi-core system that's no longer true. Instead, we'll need synchronisation primitives designed for multiple cores (also called SMP, for symmetric multi-processing). -] else if lang == "de" [ +], +de: [ Es wird immer üblicher, eingebettete Prozessoren mit zwei oder mehr Kernen auszustatten, was die Parallelverarbeitung zusätzlich verkompliziert. Alle Beispiele mit kritischen Abschnitten @@ -1474,27 +1592,34 @@ fn timer() { Mehrkernsystem trifft dies jedoch nicht mehr zu. Stattdessen benötigen wir Synchronisierungsprimitive, die speziell für Mehrkernsysteme (auch SMP, für symmetrisches Multiprocessing, genannt) entwickelt wurden. -] else if lang == "zh" [ +], +zh: [ 在嵌入式处理器中有两个或者多个核心很正常,其为并发添加了额外一层复杂性。所有使用临界区的例子(包括`cortex_m::interrupt::Mutex`)都假设了另一个执行的线程仅是中断线程,但是在一个多核系统中,这不再是正确的假设。反而,我们将需要为多核设计的同步原语(也被叫做SMP,symmetric multi-processing的缩写)。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ These typically use the atomic instructions we saw earlier, since the processing system will ensure that atomicity is maintained over all cores. -] else if lang == "de" [ +], +de: [ Diese verwenden typischerweise die bereits erwähnten atomaren Befehle, da das Verarbeitungssystem die Atomarität über alle Kerne hinweg gewährleistet. -] else if lang == "zh" [ +], +zh: [ 我们之前看到的,这些通常使用原子指令,因为处理系统将确保原子性在所有的核中都保持着。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Covering these topics in detail is currently beyond the scope of this book, but the general patterns are the same as for the single-core case. -] else if lang == "de" [ +], +de: [ Eine detaillierte Behandlung dieser Themen würde den Rahmen dieses Buches sprengen, die allgemeinen Muster sind jedoch dieselben wie im Einzelkernfall. -] else if lang == "zh" [ +], +zh: [ 覆盖这些主题的细节已经超出了本书的范围,但是常规的模式与单核的相似。 -] else { todo } +])) diff --git a/typ/config.typ b/typ/config.typ index 8af09cc1..7f1eca92 100644 --- a/typ/config.typ +++ b/typ/config.typ @@ -1,5 +1,6 @@ #let default_lang = sys.inputs.at("default-lang", default: "en") #let lang = sys.inputs.at("lang", default: default_lang) +#let goal = sys.inputs.at("goal", default: "publish") #let languages = ( "en": [English], "de": [German], @@ -40,6 +41,24 @@ todoupds(l) ) +// content: use in other places +#let tr(dict, default: todo) = { + if true { + for l in dict.keys() { + assert(l in languages, message: "Language `"+l+"` is not supported yet") + } + } + dict.at( + lang, + default: if default != todo and default != todos { default } + else if goal == "publish" { dict.at(default_lang) } + else { default } + ) +} + +// str: use in code blocks +#let ts = tr.with(default: todos) + #let book_title = "The Embedded Rust Book" #let h1(it, offset: 0) = if tgt == "pdf" { diff --git a/typ/design-patterns/hal/checklist.typ b/typ/design-patterns/hal/checklist.typ index fbdb389d..ed686baa 100644 --- a/typ/design-patterns/hal/checklist.typ +++ b/typ/design-patterns/hal/checklist.typ @@ -1,13 +1,14 @@ #import "../../config.typ": * -#h1(offset: whole*2, - if lang == "en" [HAL Design Patterns Checklist] - else if lang == "de" [Checkliste HAL-Design-Muster] - else if lang == "zh" [HAL设计检查清单] - else { todo }) +#h1(offset: whole*2, tr(( + en: [HAL Design Patterns Checklist], + de: [Checkliste HAL-Design-Muster], + zh: [HAL设计检查清单], +))) -#if lang == "en" [ +#tr(( +en: [ *Naming* (_crate aligns with Rust naming conventions_) - ☐ The crate is named appropriately (#link()[C-CRATE-NAME]) - *Interoperability* (_crate interacts nicely with other library functionality_) @@ -20,7 +21,8 @@ - ☐ Pin types are zero-sized by default (#link()[C-ZST-PIN]) - ☐ Pin types provide methods to erase pin and port (#link()[C-ERASED-PIN]) - ☐ Pin state should be encoded as type parameters (#link()[C-PIN-STATE]) -] else if lang == "de" [ +], +de: [ - *Benennung* (_Crate richtet sich nach den Namenskonventionen von Rust_) - ☐ Das Crate trägt einen passenden Namen. (#link()[C-CRATE-NAME]) - *Interoperabilität* (_das Crate harmoniert gut mit der Funktionalität anderer Bibliotheken_) @@ -33,7 +35,8 @@ - ☐ Pin-Typen haben standardmäßig die Größe Null. (#link()[C-ZST-PIN]) - ☐ Pin-Typen stellen Methoden zum Löschen von Pins und Ports bereit. (#link()[C-ERASED-PIN]) - ☐ Der Pin-Zustand sollte als Typparameter kodiert werden. (#link()[C-PIN-STATE]) -] else if lang == "zh" [ +], +zh: [ - *命名* (_crate要符合Rust命名规则_) - ☐ crate被恰当地命名 (#link()[C-CRATE-NAME]) - *互用性* (_crate要很好地与其它的库功能交互_) @@ -46,4 +49,4 @@ - ☐ Pin类型默认是零大小类型 (#link()[C-ZST-PIN]) - ☐ Pin类型提供擦除管脚和端口的方法 (#link()[C-ERASED-PIN]) - ☐ Pin状态应该被编码为类型参数 (#link()[C-PIN-STATE]) -] else { todo } +])) diff --git a/typ/design-patterns/hal/gpio.typ b/typ/design-patterns/hal/gpio.typ index 61a03866..b9e47830 100644 --- a/typ/design-patterns/hal/gpio.typ +++ b/typ/design-patterns/hal/gpio.typ @@ -1,46 +1,55 @@ #import "../../config.typ": * -#h1(offset: whole*2, - if lang == "en" [Recommendations for GPIO Interfaces] - else if lang == "de" [Empfehlungen für GPIO-Schnittstellen] - else if lang == "zh" [关于GPIO接口的建议] - else { todo }) +#h1(offset: whole*2, tr(( + en: [Recommendations for GPIO Interfaces], + de: [Empfehlungen für GPIO-Schnittstellen], + zh: [关于GPIO接口的建议], +))) -= #(if lang == "en" [Pin types are zero-sized by default] - else if lang == "de" [Pin-Typen haben standardmäßig die Größe Null] - else if lang == "zh" [Pin类型默认是零大小的] - else { todo }) (C-ZST-PIN) += #tr(( + en: [Pin types are zero-sized by default], + de: [Pin-Typen haben standardmäßig die Größe Null], + zh: [Pin类型默认是零大小的], +)) (C-ZST-PIN) -#if lang == "en" [ +#tr(( +en: [ GPIO Interfaces exposed by the HAL should provide dedicated zero-sized types for each pin on every interface or port, resulting in a zero-cost GPIO abstraction when all pin assignments are statically known. -] else if lang == "de" [ +], +de: [ Die von der HAL bereitgestellten GPIO-Schnittstellen sollten für jeden Pin jeder Schnittstelle oder jedes Ports einen dedizierten Datentyp der Größe Null bereitstellen. Dies führt zu einer kostenlosen GPIO-Abstraktion, wenn alle Pinbelegungen statisch bekannt sind. -] else if lang == "zh" [ +], +zh: [ 由HAL暴露的GPIO接口应该为所有接口或者端口上的每一个管脚提供一个专用的零大小类型,从而当所有的管脚分配静态已知时,提供一个零开销抽象。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Each GPIO Interface or Port should implement a `split` method returning a struct with every pin. -] else if lang == "de" [ +], +de: [ Jede GPIO-Schnittstelle oder jeder Port sollte eine `split`-Methode implementieren, die eine Struktur mit allen Pins zurückgibt. -] else if lang == "zh" [ +], +zh: [ 每个GPIO接口或者端口应该实现一个`split`方法,它返回一个拥有所有管脚的结构体。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Example: -] else if lang == "de" [ +], +de: [ Beispiel: -] else { todo } +])) ```rust pub struct PA0; @@ -66,37 +75,44 @@ pub struct PortAPins { } ``` -= #(if lang == "en" [Pin types provide methods to erase pin and port] - else if lang == "de" [Pin-Typen stellen Methoden zum Löschen von Pins und Ports bereit] - else if lang == "zh" [管脚类型提供方法去擦除管脚和端口] - else { todo }) (C-ERASED-PIN) += #tr(( + en: [Pin types provide methods to erase pin and port], + de: [Pin-Typen stellen Methoden zum Löschen von Pins und Ports bereit], + zh: [管脚类型提供方法去擦除管脚和端口], +)) (C-ERASED-PIN) -#if lang == "en" [ +#tr(( +en: [ Pins should provide type erasure methods that move their properties from compile time to runtime, and allow more flexibility in applications. -] else if lang == "de" [ +], +de: [ Pins sollten Methoden zur Typeliminierung (Type Erasure) bereitstellen, die ihre Eigenschaften von der Kompilierzeit in die Laufzeit verlagern und so mehr Flexibilität in Anwendungen ermöglichen. -] else if lang == "zh" [ +], +zh: [ 从编译时到运行时,管脚都应该提供可以改变属性的类型擦出方法,允许在应用中有更多的灵活性。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Example: -] else if lang == "de" [ +], +de: [ Beispiel: -] else if lang == "zh" [ +], +zh: [ 案例: -] else { todo } +])) #raw(block: true, lang: "rust", -"/// " + if lang in ("en", "de") { - "Port A, pin 0." - } else if lang == "zh" { - "端口 A, 管脚 0。" - } else { todos } + " +"/// " + ts(( + en: "Port A, pin 0.", + de: "Port A, pin 0.", + zh: "端口 A, 管脚 0。", + )) + " pub struct PA0; impl PA0 { @@ -105,17 +121,16 @@ impl PA0 { } } -/// " + if lang in ("en", "de") { - "A pin on port A." - } else if lang == "zh" { - "端口A上的A管脚。" - } else { todos } + " +/// " + ts(( + en: "A pin on port A.", + de: "A pin on port A.", + zh: "端口A上的A管脚。", + )) + " pub struct PA { - /// " + if lang == "en" { - "The pin number." - } else if lang == "zh" { - "管脚号。" - } else { todos } + " + /// " + ts(( + en: "The pin number.", + zh: "管脚号。", + )) + " pin: u8, } @@ -131,15 +146,13 @@ impl PA { pub struct Pin { port: Port, pin: u8, - // " + if lang == "en" { - "(these fields can be packed to reduce the memory footprint)" - } else if lang == "de" { - "(Diese Felder koennen gepackt werden, um den Speicherbedarf zu - // verringern.)" - } else if lang == "zh" { - "(这些字段) - // (这些字段可以打包以减少内存占用)" - } else { todos } + " + // " + ts(( + en: "(these fields can be packed to reduce the memory footprint)", + de: "(Diese Felder koennen gepackt werden, um den Speicherbedarf zu + // verringern.)", + zh: "(这些字段) + // (这些字段可以打包以减少内存占用)", + )) + " } enum Port { @@ -150,67 +163,83 @@ enum Port { } ") -= #(if lang == "en" [Pin state should be encoded as type parameters] - else if lang == "de" [Der Pin-Zustand sollte als Typparameter kodiert werden] - else if lang == "zh" [管脚状态应该被编码成类型参数] - else { todo }) (C-PIN-STATE) += #tr(( + en: [Pin state should be encoded as type parameters], + de: [Der Pin-Zustand sollte als Typparameter kodiert werden], + zh: [管脚状态应该被编码成类型参数], +)) (C-PIN-STATE) -#if lang == "en" [ +#tr(( +en: [ Pins may be configured as input or output with different characteristics depending on the chip or family. This state should be encoded in the type system to prevent use of pins in incorrect states. -] else if lang == "de" [ +], +de: [ Je nach Chip oder Chipfamilie können Pins als Eingang oder Ausgang mit unterschiedlichen Eigenschaften konfiguriert werden. Dieser Zustand sollte im Typsystem abgebildet werden, um eine Verwendung der Pins in einem falschen Zustand zu verhindern. -] else if lang == "zh" [ +], +zh: [ 取决于芯片或者芯片系列,管脚可能被配置为具有不同特性的输出或者输入。这个状态应该编码进类型系统中以避免在错误的状态中使用管脚。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Additional, chip-specific state (eg. drive strength) may also be encoded in this way, using additional type parameters. -] else if lang == "de" [ +], +de: [ Auch zusätzliche, chip-spezifische Zustände (z. B. die Treiberstärke) lassen sich auf diese Weise mittels zusätzlicher Typparameter kodieren. -] else if lang == "zh" [ +], +zh: [ 另外,也可以用这个方法使用额外的类型参数编码芯片特定的状态(eg. 驱动强度)。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Methods for changing the pin state should be provided as `into_input` and `into_output` methods. -] else if lang == "de" [ +], +de: [ Methoden zur Änderung des Pin-Zustands sollten als `into_input` und `into_output` bereitgestellt werden. -] else if lang == "zh" [ +], +zh: [ 用来改变管脚状态的方法应该被实现成`into_input`和`into_output`方法。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Additionally, `with_{input,output}_state` methods should be provided that temporarily reconfigure a pin in a different state without moving it. -] else if lang == "de" [ +], +de: [ Zusätzlich sollten Methoden wie `with_{input,output}_state` angeboten werden, die einen Pin vorübergehend in einen anderen Zustand versetzen, ohne ihn zu verschieben (d.~h. ohne Eigentumsübergang). -] else if lang == "zh" [ +], +zh: [ 另外,应该提供`with_{input,output}_state`方法,在一个不同的状态中临时配置一个管脚而不是移动它。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ The following methods should be provided for every pin type (that is, both erased and non-erased pin types should provide the same API): -] else if lang == "de" [ +], +de: [ Für jeden Pin-Typ sollten die folgenden Methoden bereitgestellt werden (das heißt, sowohl „erased" als auch „non-erased" Pin-Typen müssen dieselbe API bieten): -] else if lang == "zh" [ +], +zh: [ 应该为每个的管脚类型提供下列的方法(也就是说,已擦除和未擦除的管脚类型应该提供一样的API): -] else { todo } +])) - `pub fn into_input(self, input: N) -> Pin` - `pub fn into_output(self, output: N) -> Pin` - ```rust @@ -228,27 +257,33 @@ enum Port { ) -> R ``` -#if lang == "en" [ +#tr(( +en: [ Pin state should be bounded by sealed traits. Users of the HAL should have no need to add their own state. The traits can provide HAL-specific methods required to implement the pin state API. -] else if lang == "de" [ +], +de: [ Der Pin-Zustand sollte durch „Sealed Traits" eingeschränkt sein. Nutzer des HAL sollten keinen eigenen Zustand hinzufügen müssen. Die Traits können HAL-spezifische Methoden bereitstellen, die für die Implementierung der Pin-Zustands-API erforderlich sind. -] else if lang == "zh" [ +], +zh: [ 管脚状态应该用sealed traits来绑定。HAL的用户不必添加他们自己的状态。这个traits能提供HAL特定的方法,实现管脚状态API需要这些方法。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Example: -] else if lang == "de" [ +], +de: [ Beispiel: -] else if lang == "zh" [ +], +zh: [ 案例: -] else { todo } +])) #raw(block: true, lang: "rust", "# use std::marker::PhantomData; @@ -325,11 +360,9 @@ impl PA1 { } } -// " + if lang == "en" { - "Same for `PA` and `Pin`, and other pin types." - } else if lang == "de" { - "Dasselbe gilt fuer `PA` und `Pin` sowie andere Pin-Typen." - } else if lang == "zh" { - "对于`PA`和`Pin`一样的,对于其它管脚类型来说也是。" - } else { todos } + " +// " + ts(( + en: "Same for `PA` and `Pin`, and other pin types.", + de: "Dasselbe gilt fuer `PA` und `Pin` sowie andere Pin-Typen.", + zh: "对于`PA`和`Pin`一样的,对于其它管脚类型来说也是。", + )) + " ") diff --git a/typ/design-patterns/hal/index.typ b/typ/design-patterns/hal/index.typ index b0674c75..60e6f16b 100644 --- a/typ/design-patterns/hal/index.typ +++ b/typ/design-patterns/hal/index.typ @@ -1,13 +1,14 @@ #import "../../config.typ": * -#h1(offset: whole, - if lang == "en" [HAL Design Patterns] - else if lang == "de" [HAL-Design-Muster] - else if lang == "zh" [HAL设计模式] - else { todo }) +#h1(offset: whole, tr(( + en: [HAL Design Patterns], + de: [HAL-Design-Muster], + zh: [HAL设计模式], +))) #let url_quide = "https://rust-lang.github.io/api-guidelines/" -#if lang == "en" [ +#tr(( +en: [ This is a set of common and recommended patterns for writing hardware abstraction layers (HALs) for microcontrollers in Rust. These patterns are intended to be used in addition to the existing @@ -18,7 +19,8 @@ - #link()[Interoperability] - #link()[Predictability] - #link()[GPIO] -] else if lang == "de" [ +], +de: [ Hierbei handelt es sich um eine Sammlung bewährter und empfohlener Muster für die Implementierung von Hardware-Abstraktionsschichten (HALs) für Mikrocontroller in Rust. Diese Muster sind als Ergänzung zu den @@ -29,7 +31,8 @@ - #link()[Interoperabilität] - #link()[Vorhersehbarkeit] - #link()[GPIO] -] else if lang == "zh" [ +], +zh: [ 这是一组关于使用Rust为微控制器写硬件抽象层的常见的和推荐的模式。当为微控制器编写HALs时,除了现有的 #link(url_quide)[Rust API 指南] 外,也可以使用这些模式。 @@ -38,4 +41,4 @@ - #link()[互用性] - #link()[可预见性] - #link()[GPIO] -] else { todo } +])) diff --git a/typ/design-patterns/hal/interoperability.typ b/typ/design-patterns/hal/interoperability.typ index b4c94ef2..38855bac 100644 --- a/typ/design-patterns/hal/interoperability.typ +++ b/typ/design-patterns/hal/interoperability.typ @@ -1,65 +1,78 @@ #import "../../config.typ": * -#h1(offset: whole*2, - if lang == "en" [Interoperability] - else if lang == "de" [Interoperabilität] - else if lang == "zh" [互用性] - else { todo }) +#h1(offset: whole*2, tr(( + en: [Interoperability], + de: [Interoperabilität], + zh: [互用性], +))) -= #(if lang == "en" [Wrapper types provide a destructor method] - else if lang == "de" [Wrapper-Typen stellen eine Destruktor-Methode bereit] - else if lang == "zh" [封装类型提供一个析构方法] - else { todo }) (C-FREE) += #tr(( + en: [Wrapper types provide a destructor method], + de: [Wrapper-Typen stellen eine Destruktor-Methode bereit], + zh: [封装类型提供一个析构方法], +)) (C-FREE) -#if lang == "en" [ +#tr(( +en: [ Any non-`Copy` wrapper type provided by the HAL should provide a `free` method that consumes the wrapper and returns back the raw peripheral (and possibly other objects) it was created from. -] else if lang == "de" [ +], +de: [ Jeder vom HAL bereitgestellte Wrapper-Typ, der nicht `Copy` implementiert, sollte eine `free`-Methode anbieten; diese verbraucht den Wrapper und gibt das rohe Peripheriegerät (sowie gegebenenfalls weitere Objekte), aus dem er erstellt wurde, wieder frei. -] else if lang == "zh" [ +], +zh: [ 任何由HAL提供的非`Copy`封装类型应该提供一个`free`方法,这个方法消费封装类且返回最初生成它的外设(可能是其它对象)。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ The method should shut down and reset the peripheral if necessary. Calling `new` with the raw peripheral returned by `free` should not fail due to an unexpected state of the peripheral. -] else if lang == "de" [ +], +de: [ Die Methode sollte das Peripheriegerät bei Bedarf deaktivieren und zurücksetzen. Ein Aufruf von `new` mit dem durch `free` zurückgegebenen rohen Peripheriegerät darf nicht aufgrund eines unerwarteten Zustands des Geräts fehlschlagen. -] else if lang == "zh" [ +], +zh: [ 如果有必要,方法应该关闭和重置外设。使用由`free`返回的原始外设去调用`new`不应该由于设备的意外状态而失败, -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ If the HAL type requires other non-`Copy` objects to be constructed (for example I/O pins), any such object should be released and returned by `free` as well. `free` should return a tuple in that case. -] else if lang == "de" [ +], +de: [ Falls für die Konstruktion des HAL-Typs weitere Objekte erforderlich sind, die nicht `Copy` implementieren (zum Beispiel I/O-Pins), sollten auch diese Objekte durch `free` freigegeben und zurückgegeben werden. In diesem Fall sollte `free` ein Tupel zurückgeben. -] else if lang == "zh" [ +], +zh: [ 如果HAL类型要求构造其它的非`Copy`对象(比如 I/O 管脚),任何这样的对象应该也由`free`返回和释放。在这种情况下`free`应该返回一个元组。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ For example: -] else if lang == "de" [ +], +de: [ Zum Beispiel: -] else if lang == "zh" [ +], +zh: [ 比如: -] else { todo } +])) ```rust # pub struct TIMER0; @@ -76,63 +89,77 @@ impl Timer { } ``` -= #(if lang == "en" [HALs reexport their register access crate] - else if lang == "de" [HALs exportieren ihr Registerzugriffs-Crate erneut] - else if lang == "zh" [HALs重新导出它们的寄存器访问crate] - else { todo }) (C-REEXPORT-PAC) += #tr(( + en: [HALs reexport their register access crate], + de: [HALs exportieren ihr Registerzugriffs-Crate erneut], + zh: [HALs重新导出它们的寄存器访问crate], +)) (C-REEXPORT-PAC) #let ln_svd2rust = link("https://github.com/rust-embedded/svd2rust")[svd2rust] -#if lang == "en" [ +#tr(( +en: [ HALs can be written on top of #ln_svd2rust;-generated PACs, or on top of other crates that provide raw register access. HALs should always reexport the register access crate they are based on in their crate root. -] else if lang == "de" [ +], +de: [ HALs können auf der Basis von mit #ln_svd2rust generierten PACs oder anderen Crates implementiert werden, die einen direkten Registerzugriff ermöglichen. HALs sollten das Crate für den Registerzugriff, auf dem sie aufbauen, stets an der Wurzel ihres eigenen Crates erneut exportieren (re-exportieren). -] else if lang == "zh" [ +], +zh: [ 可以在#ln_svd2rust;生成的PACs之上,或在其它纯寄存器访问的crates之上编写HALs。HALs需要在crate root中重新导出它们所基于的寄存器访问crate -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ A PAC should be reexported under the name `pac`, regardless of the actual name of the crate, as the name of the HAL should already make it clear what PAC is being accessed. -] else if lang == "de" [ +], +de: [ Ein PAC sollte unter dem Namen `pac` re-exportiert werden -- unabhängig vom eigentlichen Namen des Crates --, da bereits der Name der HAL verdeutlichen sollte, auf welches PAC zugegriffen wird. -] else if lang == "zh" [ +], +zh: [ 一个PAC应该被重新导出在`pac`名下,无论这个crate实际的名字是什么,因为HAL的名字应该已经明确了正被访问的是什么PAC -] else { todo } +])) -= #(if lang == "en" [Types implement the `embedded-hal` traits] - else if lang == "de" [Typen implementieren die `embedded-hal`-Traits] - else if lang == "zh" [类型实现`embedded-hal` traits] - else { todo }) (C-HAL-TRAITS) += #tr(( + en: [Types implement the `embedded-hal` traits], + de: [Typen implementieren die `embedded-hal`-Traits], + zh: [类型实现`embedded-hal` traits], +)) (C-HAL-TRAITS) #let ln_hal = link("https://github.com/rust-embedded/embedded-hal")[`embedded-hal`] -#if lang == "en" [ +#tr(( +en: [ Types provided by the HAL should implement all applicable traits provided by the #ln_hal crate. -] else if lang == "de" [ +], +de: [ Vom HAL bereitgestellte Typen sollten alle anwendbaren Traits implementieren, die vom #ln_hal;-Crate bereitgestellt werden. -] else if lang == "zh" [ +], +zh: [ HAL提供的类型应该实现所有的由#ln_hal crate提供的能用的traits。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Multiple traits may be implemented for the same type. -] else if lang == "de" [ +], +de: [ Für denselben Typ können mehrere Traits implementiert werden. -] else if lang == "zh" [ +], +zh: [ 同个类型可能实现多个traits。 -] else { todo } +])) diff --git a/typ/design-patterns/hal/naming.typ b/typ/design-patterns/hal/naming.typ index 5663f9d4..1a95b450 100644 --- a/typ/design-patterns/hal/naming.typ +++ b/typ/design-patterns/hal/naming.typ @@ -1,28 +1,32 @@ #import "../../config.typ": * -#h1(offset: whole*2, - if lang == "en" [Naming] - else if lang == "de" [Benennung] - else if lang == "zh" [命名] - else { todo }) +#h1(offset: whole*2, tr(( + en: [Naming], + de: [Benennung], + zh: [命名], +))) -= #(if lang == "en" [The crate is named appropriately] - else if lang == "de" [Das Crate trägt einen passenden Namen] - else if lang == "zh" [crate要被恰当地命名] - else { todo }) (C-CRATE-NAME) += #tr(( + en: [The crate is named appropriately], + de: [Das Crate trägt einen passenden Namen], + zh: [crate要被恰当地命名], +)) (C-CRATE-NAME) -#if lang == "en" [ +#tr(( +en: [ HAL crates should be named after the chip or family of chips they aim to support. Their name should end with `-hal` to distinguish them from register access crates. The name should not contain underscores (use dashes instead). -] else if lang == "de" [ +], +de: [ HAL-Crates sollten nach dem Chip oder der Chip-Familie benannt werden, die sie unterstützen sollen. Ihr Name sollte auf `-hal` enden, um sie von Registerzugriffs-Crates zu unterscheiden. Der Name sollte keine Unterstriche enthalten (stattdessen sind Bindestriche zu verwenden). -] else if lang == "zh" [ +], +zh: [ HAL crates应该在目标支持的芯片或者芯片系列之后被命名。它们的名字应该以`-hal`结尾,为了将它们与PAC区分开来。名字不应该包含下划线(请改用破折号)。 -] else { todo } +])) diff --git a/typ/design-patterns/hal/predictability.typ b/typ/design-patterns/hal/predictability.typ index 4dfea211..db971284 100644 --- a/typ/design-patterns/hal/predictability.typ +++ b/typ/design-patterns/hal/predictability.typ @@ -1,47 +1,56 @@ #import "../../config.typ": * -#h1(offset: whole*2, - if lang == "en" [Predictability] - else if lang == "de" [Vorhersehbarkeit] - else if lang == "zh" [可预见性] - else { todo }) +#h1(offset: whole*2, tr(( + en: [Predictability], + de: [Vorhersehbarkeit], + zh: [可预见性], +))) -= #(if lang == "en" [Constructors are used instead of extension traits] - else if lang == "de" [Anstelle von Extension Traits werden Konstruktoren verwendet] - else if lang == "zh" [使用构造函数而不是扩展traits] - else { todo }) (C-CTOR) += #tr(( + en: [Constructors are used instead of extension traits], + de: [Anstelle von Extension Traits werden Konstruktoren verwendet], + zh: [使用构造函数而不是扩展traits], +)) (C-CTOR) -#if lang == "en" [ +#tr(( +en: [ All peripherals to which the HAL adds functionality should be wrapped in a new type, even if no additional fields are required for that functionality. -] else if lang == "de" [ +], +de: [ Alle Peripheriegeräte, denen die HAL Funktionalität hinzufügt, sollten in einen neuen Typ gekapselt werden -- selbst dann, wenn für diese Funktionalität keine zusätzlichen Felder erforderlich sind. -] else if lang == "zh" [ +], +zh: [ 所有由HAL添加功能的外设应该被封装进一个新类型,即使该功能不需要额外的字段。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Extension traits implemented for the raw peripheral should be avoided. -] else if lang == "de" [ +], +de: [ Die Implementierung von Extension Traits für das rohe Peripheriegerät (Raw Peripheral) sollte vermieden werden. -] else if lang == "zh" [ +], +zh: [ 应该避免为基本外设扩展traits。 -] else { todo } +])) -= #(if lang == "en" [Methods are decorated with `#[inline]` where appropriate] - else if lang == "de" [Methoden werden dort, wo es angebracht ist, mit `#[inline]` - versehen] - else if lang == "zh" [方法在适当的地方用`#[inline]`修饰] - else { todo }) (C-INLINE) += #tr(( + en: [Methods are decorated with `#[inline]` where appropriate], + de: [Methoden werden dort, wo es angebracht ist, mit `#[inline]` + versehen], + zh: [方法在适当的地方用`#[inline]`修饰], +)) (C-INLINE) -#if lang == "en" [ +#tr(( +en: [ The Rust compiler does not by default perform full inlining across crate boundaries. As embedded applications are sensitive to unexpected code size increases, `#[inline]` should be used to guide the compiler as @@ -54,7 +63,8 @@ should be marked as `#[inline]`. This enables the compiler to compute even complicated initialization logic at compile time, provided the function inputs are known. -] else if lang == "de" [ +], +de: [ Der Rust-Compiler führt standardmäßig kein vollständiges Inlining über Crate-Grenzen hinweg durch. Da eingebettete Anwendungen empfindlich auf unerwartete Vergrößerungen des Codes reagieren, sollte `#[inline]` @@ -68,9 +78,10 @@ ermöglicht es dem Compiler, selbst komplexe Initialisierungslogik zur Kompilierzeit zu berechnen, sofern die Eingabewerte der Funktion bekannt sind. -] else if lang == "zh" [ +], +zh: [ Rust编译器默认不会越过crate边界执行完全内联。因为嵌入式应用对于不可预期的代码大小的增加很敏感,`#[inline]`应该如下所示用来指导编译器: - 所有的"小"函数应该被标记`#[inline]`。什么是"小"是主观的,但是通常所有有可能被编译成一位数的指令序列(single-digit instruction sequences)都可以被视为"小"。 - 非常有可能把一个常量数值作为参数的函数应该被标记为`#[inline]`。这让编译器在编译时就可以进行计算甚至是复杂的初始化逻辑,前提是函数输入是已知的。 -] else { todo } +])) diff --git a/typ/design-patterns/index.typ b/typ/design-patterns/index.typ index 4a70d4e9..5a62551e 100644 --- a/typ/design-patterns/index.typ +++ b/typ/design-patterns/index.typ @@ -1,15 +1,19 @@ #import "../config.typ": * -#h1(if lang == "en" [Design Patterns] - else if lang == "de" [Design-Muster] - else if lang == "zh" [设计模式] - else { todo }) +#h1(tr(( + en: [Design Patterns], + de: [Design-Muster], + zh: [设计模式], +))) -#if lang == "en" [ +#tr(( +en: [ This chapter aims to collect various useful design patterns for embedded Rust. -] else if lang == "de" [ +], +de: [ Dieses Kapitel zielt darauf ab, verschiedene nützliche Entwurfsmuster für Embedded-Rust zusammenzustellen. -] else if lang == "zh" [ +], +zh: [ 这个章节的目标是为嵌入式Rust收集不同的有用的设计模式。 -] else { todo } +])) diff --git a/typ/interoperability/c-with-rust.typ b/typ/interoperability/c-with-rust.typ index 96b59496..2ee880f3 100644 --- a/typ/interoperability/c-with-rust.typ +++ b/typ/interoperability/c-with-rust.typ @@ -1,86 +1,103 @@ #import "../config.typ": * -#h1(offset: whole, - if lang == "en" [A little C with your Rust] - else if lang == "de" [Ein bisschen C zu Ihrem Rust] - else if lang == "zh" [使用C的Rust] - else { todo }) +#h1(offset: whole, tr(( + en: [A little C with your Rust], + de: [Ein bisschen C zu Ihrem Rust], + zh: [使用C的Rust], +))) -#if lang == "en" [ +#tr(( +en: [ Using C or C++ inside of a Rust project consists of two major parts: - Wrapping the exposed C API for use with Rust - Building your C or C++ code to be integrated with the Rust code -] else if lang == "de" [ +], +de: [ Die Verwendung von C oder C++ innerhalb eines Rust-Projekts umfasst zwei wesentliche Aspekte: - Einkapselung der bereitgestellten C-API für die Verwendung mit Rust - Erstellen Ihres C- oder C++-Codes zur Integration mit dem Rust-Code -] else if lang == "zh" [ +], +zh: [ 要在一个Rust项目中使用C或者C++,主要有两个步骤: - 用Rust封装要暴露出来使用的C API - 编译要和Rust代码集成在一起的C或者C++代码 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ As C++ does not have a stable ABI for the Rust compiler to target, it is recommended to use the `C` ABI when combining Rust with C or C++. -] else if lang == "de" [ +], +de: [ Da C++ über keine stabile ABI verfügt, auf die der Rust-Compiler abzielen könnte, wird empfohlen, bei der Kombination von Rust mit C oder C++ die `C`-ABI zu verwenden. -] else if lang == "zh" [ +], +zh: [ 因为对于Rust编译器来说,C++没有一个稳定的ABI,当要将Rust和C或者C++结合时,建议优先选择`C`。 -] else { todo } +])) -= #(if lang == "en" [Defining the interface] - else if lang == "de" [Definition der Schnittstelle] - else if lang == "zh" [定义接口] - else { todo }) += #tr(( + en: [Defining the interface], + de: [Definition der Schnittstelle], + zh: [定义接口], +)) -#if lang == "en" [ +#tr(( +en: [ Before consuming C or C++ code from Rust, it is necessary to define (in Rust) what data types and function signatures exist in the linked code. In C or C++, you would include a header (`.h` or `.hpp`) file which defines this data. In Rust, it is necessary to either manually translate these definitions to Rust, or use a tool to generate these definitions. -] else if lang == "de" [ +], +de: [ Bevor C- oder C++-Code aus Rust heraus verwendet werden kann, muss (in Rust) definiert werden, welche Datentypen und Funktionssignaturen im verlinkten Code vorhanden sind. In C oder C++ würde man eine Header-Datei (`.h` oder `.hpp`) einbinden, die diese Daten definiert. In Rust ist es erforderlich, diese Definitionen entweder manuell nach Rust zu übertragen oder ein Werkzeug zu ihrer Generierung zu verwenden. -] else if lang == "zh" [ +], +zh: [ 在Rust消费C或者C++代码之前,必须定义(在Rust中定义),在要被链接的代码中存在什么数据类型和函数签名。在C或者C++中,你要包含一个头文件(`.h`或者`.hpp`),其定义了这个数据。而在Rust中,必须手动地将这些定义翻译成Rust,或者使用一个工具去生成这些定义。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ First, we will cover manually translating these definitions from C/C++ to Rust. -] else if lang == "de" [ +], +de: [ Zunächst behandeln wir die manuelle Übertragung dieser Definitionen von C/C++ nach Rust. -] else if lang == "zh" [ +], +zh: [ 首先,我们将介绍如何将这些定义从C/C++手动地转换为Rust。 -] else { todo } +])) -== #(if lang == "en" [Wrapping C functions and Datatypes] - else if lang == "de" [Einbinden von C-Funktionen und -Datentypen] - else if lang == "zh" [封装C函数和数据类型] - else { todo }) +== #tr(( + en: [Wrapping C functions and Datatypes], + de: [Einbinden von C-Funktionen und -Datentypen], + zh: [封装C函数和数据类型], +)) -#if lang == "en" [ +#tr(( +en: [ Typically, libraries written in C or C++ will provide a header file defining all types and functions used in public interfaces. An example file may look like this: -] else if lang == "de" [ +], +de: [ Typischerweise stellen in C oder C++ geschriebene Bibliotheken eine Header-Datei bereit, die alle in öffentlichen Schnittstellen verwendeten Typen und Funktionen definiert. Eine Beispieldatei könnte wie folgt aussehen: -] else if lang == "zh" [ +], +zh: [ 通常,用C或者C++写的库会提供一个头文件,头文件定义了所有的类型和用于公共接口的函数。如下是一个示例文件: -] else { todo } +])) ```c /* File: cool.h */ @@ -92,13 +109,16 @@ typedef struct CoolStruct { void cool_function(int i, char c, CoolStruct* cs); ``` -#if lang == "en" [ +#tr(( +en: [ When translated to Rust, this interface would look as such: -] else if lang == "de" [ +], +de: [ Nach Rust übertragen, sähe diese Schnittstelle folgendermaßen aus: -] else if lang == "zh" [ +], +zh: [ 当翻译成Rust时,这个接口将看起来像是: -] else { todo } +])) ```rust /* File: cool_bindings.rs */ @@ -117,35 +137,41 @@ extern "C" { } ``` -#if lang == "en" [ +#tr(( +en: [ Let's take a look at this definition one piece at a time, to explain each of the parts. -] else if lang == "de" [ +], +de: [ Schauen wir uns diese Definition Schritt für Schritt an, um die einzelnen Bestandteile zu erläutern. -] else if lang == "zh" [ +], +zh: [ 让我们一次看一个语句,来解释每个部分。 -] else { todo } +])) ```rust #[repr(C)] pub struct CoolStruct { ... } ``` -#if lang == "en" [ +#tr(( +en: [ By default, Rust does not guarantee order, padding, or the size of data included in a `struct`. In order to guarantee compatibility with C code, we include the `#[repr(C)]` attribute, which instructs the Rust compiler to always use the same rules C does for organizing data within a struct. -] else if lang == "de" [ +], +de: [ Standardmäßig garantiert Rust weder die Reihenfolge noch das Padding oder die Größe der in einer `struct` enthaltenen Daten. Um die Kompatibilität mit C-Code zu gewährleisten, verwenden wir das Attribut `#[repr(C)]`; dieses weist den Rust-Compiler an, für die Anordnung der Daten innerhalb der Struktur stets dieselben Regeln wie C anzuwenden. -] else if lang == "zh" [ +], +zh: [ 默认,Rust不会保证包含在`struct`中的数据的大小,填充,或者顺序。为了保证与C代码兼容,我们使用`#[repr(C)]`属性,它指示Rust编译器总是使用和C一样的规则去组织一个结构体中的数据。 -] else { todo } +])) ```rust @@ -153,39 +179,45 @@ pub x: cty::c_int, pub y: cty::c_int, ``` -#if lang == "en" [ +#tr(( +en: [ Due to the flexibility of how C or C++ defines an `int` or `char`, it is recommended to use primitive data types defined in `cty`, which will map types from C to types in Rust. -] else if lang == "de" [ +], +de: [ Aufgrund der Flexibilität, wie C oder C++ ein „int" oder „char" definiert, wird empfohlen, in „cty" definierte primitive Datentypen zu verwenden, die Typen von C auf Typen in Rust abbilden. -] else if lang == "zh" [ +], +zh: [ 由于C或者C++定义一个`int`或者`char`的方式很灵活,所以建议使用在`cty`中定义的基础类型,它将类型从C映射到Rust中的类型。 -] else { todo } +])) ```rust extern "C" { pub fn cool_function( ... ); } ``` -#if lang == "en" [ +#tr(( +en: [ This statement defines the signature of a function that uses the C ABI, called `cool_function`. By defining the signature without defining the body of the function, the definition of this function will need to be provided elsewhere, or linked into the final library or binary from a static library. -] else if lang == "de" [ +], +de: [ Diese Anweisung definiert die Signatur einer Funktion namens `cool_function`, die die C-ABI verwendet. Da die Signatur ohne den Funktionsrumpf definiert wird, muss die eigentliche Funktionsdefinition an anderer Stelle bereitgestellt oder aus einer statischen Bibliothek in die endgültige Bibliothek bzw. das fertige Binärprogramm eingebunden werden. -] else if lang == "zh" [ +], +zh: [ 这个语句定义了一个使用C ABI的函数的签名,叫做`cool_function`。因为只定义了签名而没有定义函数的主体,所以这个函数的定义将需要在其它地方定义,或者从一个静态库链接进最终的库或者一个二进制文件中。 -] else { todo } +])) ```rust i: cty::c_int, @@ -193,27 +225,32 @@ extern "C" { pub fn cool_function( ... ); } cs: *mut CoolStruct ``` -#if lang == "en" [ +#tr(( +en: [ Similar to our datatype above, we define the datatypes of the function arguments using C-compatible definitions. We also retain the same argument names, for clarity. -] else if lang == "de" [ +], +de: [ Ähnlich wie bei unserem obigen Datentyp definieren wir die Datentypen der Funktionsargumente mithilfe von C-kompatiblen Definitionen. Der Übersichtlichkeit halber behalten wir zudem die ursprünglichen Argumentnamen bei. -] else if lang == "zh" [ +], +zh: [ 与我们上面的数据类型一样,我们使用C兼容的定义去定义函数参数的数据类型。为了清晰可见,我们还保留了相同的参数名。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ We have one new type here, `*mut CoolStruct`. As C does not have a concept of Rust's references, which would look like this: `&mut CoolStruct`, we instead have a raw pointer. As dereferencing this pointer is `unsafe`, and the pointer may in fact be a `null` pointer, care must be taken to ensure the guarantees typical of Rust when interacting with C or C++ code. -] else if lang == "de" [ +], +de: [ Hier begegnet uns ein neuer Typ: `*mut CoolStruct`. Da C das Konzept der Rust-Referenzen (die etwa so aussehen: `&mut CoolStruct`) nicht kennt, verwenden wir stattdessen einen sogenannten „Raw Pointer" (Rohzeiger). @@ -221,21 +258,24 @@ extern "C" { pub fn cool_function( ... ); } tatsächlich um einen `null`-Zeiger handeln kann, ist bei der Interaktion mit C- oder C++-Code besondere Sorgfalt geboten, um die für Rust typischen Garantien zu wahren. -] else if lang == "zh" [ +], +zh: [ 这里我们有了一个新类型,`*mut CoolStruct` 。因为C没有Rust中像 `&mut CoolStruct` 这样的引用,替代的是一个裸指针。所以解引用这个指针是`unsafe`的,因为这个指针实际上可能是一个`null`指针,因此当与C或者C++代码交互时必须要小心对待那些Rust做出的安全保证。 -] else { todo } +])) -== #(if lang == "en" [Automatically generating the interface] - else if lang == "de" [Automatische Generierung der Schnittstelle] - else if lang == "zh" [自动产生接口] - else { todo }) +== #tr(( + en: [Automatically generating the interface], + de: [Automatische Generierung der Schnittstelle], + zh: [自动产生接口], +)) #let url_bindgen = "https://github.com/rust-lang/rust-bindgen" #let url_bindgen_manual = "https://rust-lang.github.io/rust-bindgen/" #let ln_cty = link("https://crates.io/crates/cty")[`cty`] -#if lang == "en" [ +#tr(( +en: [ Rather than manually generating these interfaces, which may be tedious and error prone, there is a tool called #link(url_bindgen)[bindgen] which will @@ -258,7 +298,8 @@ extern "C" { pub fn cool_function( ... ); } interact with C/C++ code compiled and linked as an external library. Tip: don't forget to use the #ln_cty crate if your types in the generated bindings are prefixed with `cty`. -] else if lang == "de" [ +], +de: [ Anstatt diese Schnittstellen manuell zu erstellen -- was mühsam und fehleranfällig sein kann --, gibt es ein Werkzeug namens #link(url_bindgen)[bindgen], das diese @@ -284,7 +325,8 @@ extern "C" { pub fn cool_function( ... ); } nicht, das #ln_cty;-Crate zu verwenden, falls die Typen in den generierten Bindings das Präfix `cty` aufweisen. -] else if lang == "zh" [ +], +zh: [ 有一个叫做#link(url_bindgen)[bindgen]的工具,它可以自动执行这些转换,而不用手动生成这些接口,手动进行这样的操作非常繁琐且容易出错。关于#link(url_bindgen)[bindgen]的使用指令,可以参考#link(url_bindgen_manual)[bindgen user's manual],常用的步骤大致如下: + 收集所有定义了你可能在Rust中会用到的数据类型或者接口的C或者C++头文件。 + 写一个`bindings.h`文件,其`#include "..."`每一个你在步骤一中收集的文件。 @@ -294,125 +336,148 @@ extern "C" { pub fn cool_function( ... ); } + `bindgen`将会在终端窗口输出生成的Rust代码。这个文件可能会被通过管道发送给你项目中的一个文件,比如`bindings.rs` 。你可能要在你的Rust项目中使用这个文件来与被编译和链接成一个外部库的C/C++代码交互。贴士: 如果你的类型在生成的绑定中被前缀了`cty`,不要忘记使用#ln_cty crate 。 -] else { todo } +])) -= #(if lang == "en" [Building your C/C++ code] - else if lang == "de" [Erstellen Ihres C/C++-Codes] - else if lang == "zh" [编译你的 C/C++ 代码] - else { todo }) += #tr(( + en: [Building your C/C++ code], + de: [Erstellen Ihres C/C++-Codes], + zh: [编译你的 C/C++ 代码], +)) -#if lang == "en" [ +#tr(( +en: [ As the Rust compiler does not directly know how to compile C or C++ code (or code from any other language, which presents a C interface), it is necessary to compile your non-Rust code ahead of time. -] else if lang == "de" [ +], +de: [ Da der Rust-Compiler nicht direkt weiß, wie man C- oder C++-Code (oder Code einer anderen Sprache mit C-Schnittstelle) kompiliert, muss der Nicht-Rust-Code vorab kompiliert werden. -] else if lang == "zh" [ +], +zh: [ 因为Rust编译器并不直接知道如何编译C或者C++代码(或者从其它语言来的代码,其提供了一个C接口),所以必须要静态编译你的非Rust代码。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ For embedded projects, this most commonly means compiling the C/C++ code to a static archive (such as `cool-library.a`), which can then be combined with your Rust code at the final linking step. -] else if lang == "de" [ +], +de: [ Bei Embedded-Projekten bedeutet dies meist, dass der C/C++-Code zu einem statischen Archiv (z. B. `cool-library.a`) kompiliert wird, welches dann im abschließenden Link-Schritt mit dem Rust-Code zusammengeführt werden kann. -] else if lang == "zh" [ +], +zh: [ 对于嵌入式项目,这通常意味着把C/C++代码编译成一个静态库文档(比如 `cool-library.a`),然后其能在最后链接阶段与你的Rust代码组合起来。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ If the library you would like to use is already distributed as a static archive, it is not necessary to rebuild your code. Just convert the provided interface header file as described above, and include the static archive at compile/link time. -] else if lang == "de" [ +], +de: [ Wenn die gewünschte Bibliothek bereits als statisches Archiv vorliegt, ist eine erneute Kompilierung des Codes nicht erforderlich. Es genügt, die bereitgestellte Header-Datei für die Schnittstelle wie oben beschrieben umzuwandeln und das statische Archiv beim Kompilieren bzw. Linken einzubinden. -] else if lang == "zh" [ +], +zh: [ 如果你要使用的库已经作为一个静态库文档被发布,那就没必要重新编译你的代码。只需按照上面所述转换提供的接口头文件,且在编译/链接时包含静态库文档。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ If your code exists as a source project, it will be necessary to compile your C/C++ code to a static library, either by triggering your existing build system (such as `make`, `CMake`, etc.), or by porting the necessary compilation steps to use a tool called the `cc` crate. For both of these steps, it is necessary to use a `build.rs` script. -] else if lang == "de" [ +], +de: [ Liegt der Code als Quellcode-Projekt vor, muss der C/C++-Code in eine statische Bibliothek kompiliert werden. Dies kann entweder durch Aufruf des vorhandenen Build-Systems (z. B. `make`, `CMake` usw.) oder durch Portierung der erforderlichen Kompilierungsschritte auf das sogenannte `cc`-Crate erfolgen. Für beide Vorgehensweisen ist die Verwendung eines `build.rs`-Skripts erforderlich. -] else if lang == "zh" [ +], +zh: [ 如果你的代码作为一个源项目(source project)存在,将你的C/C++代码编译成一个静态库将是必须的,要么通过使用你现存的编译系统(比如 `make`,`CMake`,等等),要么通过使用一个被叫做`cc` crate的工具移植必要的编译步骤。关于这两个,都必须使用一个`build.rs`脚本。 -] else { todo } +])) -== #(if lang == "en" [Rust `build.rs` build scripts] - else if lang == "de" [Rust-`build.rs`-Build-Skripte] - else if lang == "zh" [Rust的 `build.rs` 编译脚本] - else { todo }) +== #tr(( + en: [Rust `build.rs` build scripts], + de: [Rust-`build.rs`-Build-Skripte], + zh: [Rust的 `build.rs` 编译脚本], +)) -#if lang == "en" [ +#tr(( +en: [ A `build.rs` script is a file written in Rust syntax, that is executed on your compilation machine, AFTER dependencies of your project have been built, but BEFORE your project is built. -] else if lang == "de" [ +], +de: [ Ein `build.rs`-Skript ist eine in Rust-Syntax verfasste Datei, die auf dem Kompilierrechner ausgeführt wird -- und zwar _nachdem_ die Abhängigkeiten Ihres Projekts erstellt wurden, aber _bevor_ Ihr Projekt selbst kompiliert wird. -] else if lang == "zh" [ +], +zh: [ 一个 `build.rs` 脚本是一个用Rust语法编写的文件,它被运行在你的编译机器上,发生在你项目的依赖项被编译*之后*,但是在你的项目被编译*之前* 。 -] else { todo } +])) #let url_build_scripts = "https://doc.rust-lang.org/cargo/reference/build-scripts.html" -#if lang == "en" [ +#tr(( +en: [ The full reference may be found #link(url_build_scripts)[here]. `build.rs` scripts are useful for generating code (such as via #link(url_bindgen)[bindgen]), calling out to external build systems such as `Make`, or directly compiling C/C++ through use of the `cc` crate. -] else if lang == "de" [ +], +de: [ Die vollständige Referenz finden Sie #link(url_build_scripts)[hier]. `build.rs`-Skripte eignen sich beispielsweise zur Code-Generierung (etwa mittels #link(url_bindgen)[bindgen]), zum Aufruf externer Build-Systeme wie `Make` oder zur direkten Kompilierung von C/C++-Code unter Verwendung des `cc`-Crates. -] else if lang == "zh" [ +], +zh: [ 可能能在#link("https://doc.rust-lang.org/cargo/reference/build-scripts.html")[这里]发现完整的参考。`build.rs` 脚本能用来生成代码(比如通过#link("https://github.com/rust-lang/rust-bindgen")[bindgen]),调用外部编译系统,比如`Make`,或者直接通过使用`cc` crate来直接编译C/C++ 。 -] else { todo } +])) -== #(if lang == "en" [Triggering external build systems] - else if lang == "de" [Auslösen externer Build-Systeme] - else if lang == "zh" [使用外部编译系统] - else { todo }) +== #tr(( + en: [Triggering external build systems], + de: [Auslösen externer Build-Systeme], + zh: [使用外部编译系统], +)) #let ln_command = link("https://doc.rust-lang.org/std/process/struct.Command.html")[`std::process::Command`] -#if lang == "en" [ +#tr(( +en: [ For projects with complex external projects or build systems, it may be easiest to use #ln_command to "shell out" to your other build systems by traversing relative paths, calling a fixed command (such as `make library`), and then copying the resulting static library to the proper location in the `target` build directory. -] else if lang == "de" [ +], +de: [ Bei Projekten, die komplexe externe Projekte oder Build-Systeme einbinden, ist es oft am einfachsten, #ln_command zu verwenden, um andere Build-Systeme aufzurufen (sogenanntes @@ -420,40 +485,47 @@ extern "C" { pub fn cool_function( ... ); } festen Befehl aus (wie etwa `make library`) und kopieren anschließend die erzeugte statische Bibliothek an den entsprechenden Ort im `target`-Build-Verzeichnis. -] else if lang == "zh" [ +], +zh: [ 对于有复杂的外部项或者编译系统的项目,使用#ln_command;通过遍历相对路径来向其它编译系统"输出",调用一个固定的命令(比如 `make library`),然后拷贝最终的静态库到`target`编译文件夹中恰当的位置,可能是最简单的方法。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ While your crate may be targeting a `no_std` embedded platform, your `build.rs` executes only on machines compiling your crate. This means you may use any Rust crates which will run on your compilation host. -] else if lang == "de" [ +], +de: [ Auch wenn Ihre Crate für eine eingebettete Plattform ohne Standardbibliothek (`no_std`) gedacht ist, wird die `build.rs` ausschließlich auf dem Rechner ausgeführt, der die Crate kompiliert. Das bedeutet, dass Sie beliebige Rust-Crates verwenden können, die auf Ihrem Kompilier-Host lauffähig sind. -] else if lang == "zh" [ +], +zh: [ 虽然你的crate目标可能是一个`no_std`嵌入式平台,但你的`build.rs`只运行在负责编译你的crate的机器上。这意味着你能使用任何Rust crates,其将运行在你的编译主机上。 -] else { todo } +])) -== #(if lang == "en" [Building C/C++ code with the `cc` crate] - else if lang == "de" [Kompilieren von C/C++-Code mit dem `cc`-Crate] - else if lang == "zh" [使用`cc` crate构建C/C++代码] - else { todo }) +== #tr(( + en: [Building C/C++ code with the `cc` crate], + de: [Kompilieren von C/C++-Code mit dem `cc`-Crate], + zh: [使用`cc` crate构建C/C++代码], +)) #let url_cc = "https://github.com/alexcrichton/cc-rs" -#if lang == "en" [ +#tr(( +en: [ For projects with limited dependencies or complexity, or for projects where it is difficult to modify the build system to produce a static library (rather than a final binary or executable), it may be easier to instead utilize the #link(url_cc)[`cc` crate], which provides an idiomatic Rust interface to the compiler provided by the host. -] else if lang == "de" [ +], +de: [ Bei Projekten mit geringen Abhängigkeiten oder überschaubarer Komplexität -- oder wenn es schwierig ist, das Build-System so anzupassen, dass eine statische Bibliothek (statt einer fertigen @@ -462,21 +534,25 @@ extern "C" { pub fn cool_function( ... ); } #link(url_cc)[`cc`-Crate] zu verwenden; dieses bietet eine idiomatische Rust-Schnittstelle zu dem vom Host bereitgestellten Compiler. -] else if lang == "zh" [ +], +zh: [ 对于具有有限的依赖项或者复杂度的项目,或者对于那些难以修改编译系统去生成一个静态库(而不是一个二进制文件或者可执行文件)的项目,使用#url_cc;可能更容易,它提供了一个符合Rust语法的接口,这个接口是关于主机提供的编译器的。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ In the simplest case of compiling a single C file as a dependency to a static library, an example `build.rs` script using the #link(url_cc)[`cc` crate] would look like this: -] else if lang == "de" [ +], +de: [ Im einfachsten Fall, bei dem eine einzelne C-Datei als Abhängigkeit für eine statische Bibliothek kompiliert wird, sähe ein Beispiel für ein `build.rs`-Skript, das das #link(url_cc)[`cc`-Crate] verwendet, folgendermaßen aus: -] else if lang == "zh" [ +], +zh: [ 在把一个C文件编译成一个静态库的依赖项的最简单的场景下,可以使用#link(url_cc)[`cc` crate],示例`build.rs`脚本看起来像这样: -] else { todo } +])) ```rust fn main() { @@ -486,15 +562,18 @@ fn main() { } ``` -#if lang == "en" [ +#tr(( +en: [ The `build.rs` is placed at the root of the package. Then `cargo build` will compile and execute it before the build of the package. A static archive named `libfoo.a` is generated and placed in the `target` directory. -] else if lang == "de" [ +], +de: [ Die Datei `build.rs` befindet sich im Wurzelverzeichnis des Pakets. `cargo build` kompiliert und führt sie dann vor dem eigentlichen Build-Vorgang des Pakets aus. Dabei wird ein statisches Archiv namens `libfoo.a` erstellt und im Verzeichnis `target` abgelegt. -] else if lang == "zh" [ +], +zh: [ 要把`build.rs`放在包的根目录下.然后`cargo build`会在构建包之前编译和执行它.一个静态的名为`libfoo.a`的归档文件会生成并被放在`target`文件夹中. -] else { todo } +])) diff --git a/typ/interoperability/index.typ b/typ/interoperability/index.typ index 67f16552..cb5101ff 100644 --- a/typ/interoperability/index.typ +++ b/typ/interoperability/index.typ @@ -1,57 +1,67 @@ #import "../config.typ": * -#h1(if lang == "en" [Interoperability] - else if lang == "de" [Interoperabilität] - else if lang == "zh" [互操性] - else { todo }) +#h1(tr(( + en: [Interoperability], + de: [Interoperabilität], + zh: [互操性], +))) #let ln_ffi = link("https://doc.rust-lang.org/std/ffi/index.html")[`std::ffi`] -#if lang == "en" [ +#tr(( +en: [ Interoperability between Rust and C code is always dependent on transforming data between the two languages. For this purpose, there is a dedicated module in the `stdlib` called #ln_ffi. -] else if lang == "de" [ +], +de: [ Die Interoperabilität zwischen Rust- und C-Code hängt stets von der Umwandlung von Daten zwischen den beiden Sprachen ab. Hierfür gibt es in der Standardbibliothek (`stdlib`) ein spezielles Modul namens #ln_ffi. -] else if lang == "zh" [ +], +zh: [ Rust和C代码之间的互操性始终依赖于数据在两个语言间的转换.为了互操性,在`stdlib`中有一个专用的模块,叫作 #ln_ffi. -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ `std::ffi` provides type definitions for C primitive types, such as `char`, `int`, and `long`. It also provides some utility for converting more complex types such as strings, mapping both `&str` and `String` to C types that are easier and safer to handle. -] else if lang == "de" [ +], +de: [ `std::ffi` stellt Typdefinitionen für primitive C-Datentypen bereit, wie etwa `char`, `int` und `long`. Zudem bietet es Hilfsmittel zur Konvertierung komplexerer Typen wie Strings, indem es sowohl `&str` als auch `String` auf C-Typen abbildet, die sich einfacher und sicherer handhaben lassen. -] else if lang == "zh" [ +], +zh: [ `std::ffi`提供了与C基础类型对应的类型定义,比如`char`, `int`,和`long`. 它也提供了一些工具用于更复杂的类型之间的转换,比如字符串,可以把`&str`和`String`映射成更容易和安全处理的C类型. -] else { todo } +])) #let ln_cty = link("https://crates.io/crates/cty")[`cty`] #let ln_cstr = link("https://crates.io/crates/cstr_core")[`cstr_core`] -#if lang == "en" [ +#tr(( +en: [ As of Rust 1.30, functionalities of `std::ffi` are available in either `core::ffi` or `alloc::ffi` depending on whether or not memory allocation is involved. The #ln_cty crate and the #ln_cstr crate also offer similar functionalities. -] else if lang == "de" [ +], +de: [ Seit Rust 1.30 stehen die Funktionalitäten von `std::ffi` wahlweise in `core::ffi` oder `alloc::ffi` zur Verfügung, je nachdem, ob Speicherreservierungen erforderlich sind oder nicht. Auch die Crates #ln_cty und #ln_cstr bieten vergleichbare Funktionalitäten an. -] else if lang == "zh" [ +], +zh: [ 从Rust 1.30以来,`std::ffi`的功能也出现在`core::ffi`或者`alloc::ffi`中,取决于是否涉及到内存分配. #ln_cty;库和#ln_cstr;库也提供了相同的功能. -] else { todo } +])) #figure( kind: table, @@ -59,47 +69,57 @@ columns: 3, align: (auto,auto,auto,), table.header( - if lang == "en" [Rust type] - else if lang == "de" [Rusttyp] - else if lang == "zh" [Rust类型] - else { todo }, - if lang == "en" [Intermediate] - else if lang == "de" [Dazwischenliegend] - else if lang == "zh" [间接] - else { todo }, - if lang == "en" [C type] - else if lang == "de" [C-Typ] - else if lang == "zh" [C类型] - else { todo }, + tr(( + en: [Rust type], + de: [Rusttyp], + zh: [Rust类型], + )), + tr(( + en: [Intermediate], + de: [Dazwischenliegend], + zh: [间接], + )), + tr(( + en: [C type], + de: [C-Typ], + zh: [C类型], + )), ), `String`, `CString`, `char *`, `&str`, `CStr`, `const char *`, `()`, `c_void`, `void`, - if lang in ("en", "de") [`u32` or `u64`] - else { todo }, + tr(( + en: [`u32` or `u64`], + de: [`u32` or `u64`], + )), + )), `c_uint`, `unsigned int`, - if lang == "en" [etc] - else if lang == "de" [usw.] - else { todo }, + tr(( + en: [etc], + de: [usw.], + )), […], […], ) ) -#if lang == "en" [ +#tr(( +en: [ A value of a C primitive type can be used as one of the corresponding Rust type and vice versa, since the former is simply a type alias of the latter. For example, the following code compiles on platforms where `unsigned int` is 32-bit long. -] else if lang == "de" [ +], +de: [ Ein Wert eines primitiven C-Datentyps kann als Wert des entsprechenden Rust-Datentyps verwendet werden und umgekehrt, da der erstere lediglich ein Typalias des letzteren ist. Beispielsweise lässt sich der folgende Code auf Plattformen kompilieren, auf denen `unsigned int` 32 Bit lang ist. -] else if lang == "zh" [ +], +zh: [ 一个C基本类型的值可以被用来作为相关的Rust类型的值,反之亦然,因此前者仅仅是后者的一个类型伪名. 比如,下列的代码可以在`unsigned int`是32位宽的平台上编译. -] else { todo } +])) ```rust fn foo(num: u32) { @@ -108,72 +128,88 @@ fn foo(num: u32) { } ``` -== #(if lang == "en" [Interoperability with other build systems] - else if lang == "de" [Interoperabilität mit anderen Build-Systemen] - else if lang == "zh" [与其它编译系统的互用性] - else { todo }) +== #tr(( + en: [Interoperability with other build systems], + de: [Interoperabilität mit anderen Build-Systemen], + zh: [与其它编译系统的互用性], +)) -#if lang == "en" [ +#tr(( +en: [ A common requirement for including Rust in your embedded project is combining Cargo with your existing build system, such as make or cmake. -] else if lang == "de" [ +], +de: [ Eine häufige Voraussetzung für die Einbindung von Rust in Embedded-Projekte ist die Kombination von Cargo mit Ihrem bestehenden Build-System, beispielsweise Make oder CMake. -] else if lang == "zh" [ +], +zh: [ 在嵌入式项目中引入Rust的一个常见需求是,把Cargo结合进你现存的编译系统中,比如make或者cmake。 -] else { todo } +])) #let url_issue61 = "https://github.com/rust-embedded/book/issues/61" -#if lang == "en" [ +#tr(( +en: [ We are collecting examples and use cases for this on our issue tracker in #link(url_issue61)[issue \#61]. -] else if lang == "de" [ +], +de: [ Wir sammeln Beispiele und Anwendungsfälle hierzu in unserem Problem-Verfolgungswerkzeug unter #link(url_issue61)[Issue \#61]. -] else if lang == "zh" [ +], +zh: [ 在#link(url_issue61)[issue \#61]的issue tracker上,我们正在为这个需求收集例子和用例。 -] else { todo } +])) -== #(if lang == "en" [Interoperability with RTOSs] - else if lang == "de" [Interoperabilität mit RTOS] - else if lang == "zh" [与RTOSs的互操性] - else { todo }) +== #tr(( + en: [Interoperability with RTOSs], + de: [Interoperabilität mit RTOS], + zh: [与RTOSs的互操性], +)) -#if lang == "en" [ +#tr(( +en: [ Integrating Rust with an RTOS such as FreeRTOS or ChibiOS is still a work in progress; especially calling RTOS functions from Rust can be tricky. -] else if lang == "de" [ +], +de: [ Die Integration von Rust in ein RTOS wie FreeRTOS oder ChibiOS befindet sich noch in der Entwicklung; insbesondere der Aufruf von RTOS-Funktionen aus Rust heraus kann sich als schwierig erweisen. -] else if lang == "zh" [ +], +zh: [ 将Rust和一个RTOS集成在一起,比如FreeRTOS或者ChibiOS仍然在进行中; 尤其是从Rust调用RTOS函数可能很棘手。 -] else { todo } +])) #let url_zephyr = "https://docs.zephyrproject.org/latest/develop/languages/rust/index.html" -#if lang == "en" [ +#tr(( +en: [ Currently, the following projects publicly support Rust\<-\>RTOS interoperability: - #link(url_zephyr)[Zephyr Project] -] else if lang == "de" [ +], +de: [ Derzeit unterstützen die folgenden Projekte öffentlich die Interoperabilität zwischen Rust und RTOS: - #link(url_zephyr)[Zephyr-Projekt] -] else { todo } +])) #let url_issue62 = "https://github.com/rust-embedded/book/issues/62" -#if lang == "en" [ +#tr(( +en: [ We are collecting examples and use cases for this on our issue tracker in #link(url_issue62)[issue \#62]. -] else if lang == "de" [ +], +de: [ Wir sammeln hierfür Beispiele und Anwendungsfälle in unserem Problem-Verfolgungswerkzeug unter #link(url_issue62)[Issue \#62]. -] else if lang == "zh" [ +], +zh: [ 在#link("https://github.com/rust-embedded/book/issues/62")[issue \#62]的issue tracker上,我们正为这件事收集例子和用例。 -] else { todo } +])) diff --git a/typ/interoperability/rust-with-c.typ b/typ/interoperability/rust-with-c.typ index bfb2ec7c..db90bedd 100644 --- a/typ/interoperability/rust-with-c.typ +++ b/typ/interoperability/rust-with-c.typ @@ -1,153 +1,177 @@ #import "../config.typ": * -#h1(offset: whole, - if lang == "en" [A little Rust with your C] - else if lang == "de" [Ein bisschen Rust zu Ihrem C] - else if lang == "zh" [使用Rust的C] - else { todo }) +#h1(offset: whole, tr(( + en: [A little Rust with your C], + de: [Ein bisschen Rust zu Ihrem C], + zh: [使用Rust的C], +))) -#if lang == "en" [ +#tr(( +en: [ Using Rust code inside a C or C++ project mostly consists of two parts. - Creating a C-friendly API in Rust - Embedding your Rust project into an external build system -] else if lang == "de" [ +], +de: [ Die Verwendung von Rust-Code in einem C- oder C++-Projekt besteht größtenteils aus zwei Teilen. - Erstellung einer C-kompatiblen API in Rust - Einbindung Ihres Rust-Projekts in ein externes Build-System -] else if lang == "zh" [ +], +zh: [ 在C或者C++中使用Rust代码通常由两部分组成。 - 用Rust生成一个C友好的API - 将你的Rust项目嵌入一个外部的编译系统 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Apart from `cargo` and `meson`, most build systems don't have native Rust support. So you're most likely best off just using `cargo` for compiling your crate and any dependencies. -] else if lang == "de" [ +], +de: [ Abgesehen von `cargo` und `meson` bieten die meisten Build-Systeme keine native Rust-Unterstützung. Daher fährst du höchstwahrscheinlich am besten damit, einfach `cargo` für die Kompilierung deines Crates und aller Abhängigkeiten zu verwenden. -] else if lang == "zh" [ +], +zh: [ 除了`cargo`和`meson`,大多数编译系统没有原生Rust支持。因此你最好只用`cargo`编译你的crate和依赖。 -] else { todo } +])) -= #(if lang == "en" [Setting up a project] - else if lang == "de" [Ein Projekt einrichten] - else if lang == "zh" [设置一个项目] - else { todo }) += #tr(( + en: [Setting up a project], + de: [Ein Projekt einrichten], + zh: [设置一个项目], +)) -#if lang == "en" [ +#tr(( +en: [ Create a new `cargo` project as usual. -] else if lang == "de" [ +], +de: [ Erstellen Sie wie gewohnt ein neues `cargo`-Projekt. -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ There are flags to tell `cargo` to emit a systems library, instead of its regular rust target. This also allows you to set a different output name for your library, if you want it to differ from the rest of your crate. -] else if lang == "de" [ +], +de: [ Es gibt Flags, mit denen man `cargo` anweisen kann, eine Systembibliothek anstelle des üblichen Rust-Ziels zu erstellen. Auf diese Weise können Sie auch einen abweichenden Namen für die Bibliothek festlegen, falls dieser sich von dem des restlichen Crates unterscheiden soll. -] else if lang == "zh" [ +], +zh: [ 像往常一样创建一个新的`cargo`项目。有一些标志可以告诉`cargo`去生成一个系统库,而不是常规的rust目标文件。如果你想要它与crate的其它部分不一样,你也可以为你的库设置一个不同的输出名。 -] else { todo } +])) #raw(block: true, lang: "toml", "[lib] name = \"your_crate\" -crate-type = [\"cdylib\"] # " + if lang == "en" { - "Creates dynamic lib" - } else if lang == "de" { - "Erstellt eine dynamische Bibliothek" - } else if lang == "zh" { - "生成动态链接库" - } else { todos } + " -# crate-type = [\"staticlib\"] # " + if lang == "en" { - "Creates static lib" - } else if lang == "de" { - "Erstellt eine statische Bibliothek" - } else if lang == "zh" { - "生成静态链接库" - } else { todos } + " +crate-type = [\"cdylib\"] # " + ts(( + en: "Creates dynamic lib", + de: "Erstellt eine dynamische Bibliothek", + zh: "生成动态链接库", + )) + " +# crate-type = [\"staticlib\"] # " + ts(( + en: "Creates static lib", + de: "Erstellt eine statische Bibliothek", + zh: "生成静态链接库", + )) + " ") -= #(if lang == "en" [Building a `C` API] - else if lang == "de" [Erstellung einer C-API] - else if lang == "zh" [构建一个`C` API] - else { todo }) += #tr(( + en: [Building a `C` API], + de: [Erstellung einer C-API], + zh: [构建一个`C` API], +)) -#if lang == "en" [ +#tr(( +en: [ Because C++ has no stable ABI for the Rust compiler to target, we use `C` for any interoperability between different languages. This is no exception when using Rust inside of C and C++ code. -] else if lang == "de" [ +], +de: [ Da C++ über keine stabile ABI verfügt, auf die der Rust-Compiler abzielen könnte, nutzen wir C für die Interoperabilität zwischen verschiedenen Sprachen. Dies gilt auch für die Verwendung von Rust innerhalb von C- und C++-Code. -] else if lang == "zh" [ +], +zh: [ 因为对于Rust编译器来说,C++没有稳定的ABI,因此对于不同语言间的互操性我们使用`C`。在C和C++代码的内部使用Rust时也不例外。 -] else { todo } +])) == `#[no_mangle]` -#if lang == "en" [ +#tr(( +en: [ The Rust compiler mangles symbol names differently than native code linkers expect. As such, any function that Rust exports to be used outside of Rust needs to be told not to be mangled by the compiler. -] else if lang == "de" [ +], +de: [ Der Rust-Compiler verarbeitet Symbolnamen anders als Linker für nativen Code erwarten. Daher muss jeder Funktion, die Rust zur Verwendung außerhalb von Rust exportiert, mitgeteilt werden, dass der Compiler sie nicht verändern soll. -] else if lang == "zh" [ +], +zh: [ Rust对符号名的修饰与主机的代码链接器所期望的不同。因此,需要告知任何被Rust导出到Rust外部去使用的函数不要被编译器修饰。 -] else { todo } +])) == `extern "C"` -#if lang == "en" [ +#tr(( +en: [ By default, any function you write in Rust will use the Rust ABI (which is also not stabilized). Instead, when building outwards facing FFI APIs we need to tell the compiler to use the system ABI. -] else if lang == "de" [ +], +de: [ Standardmäßig verwendet jede in Rust geschriebene Funktion die Rust-ABI (die ebenfalls nicht stabilisiert ist). Beim Erstellen von nach außen gerichteten FFI-APIs muss der Compiler daher angewiesen werden, die System-ABI zu verwenden. -] else if lang == "zh" [ +], +zh: [ 默认,任何用Rust写的函数将使用Rust ABI(这也不稳定)。当编译面向外部的FFI APIs时,我们需要告诉编译器去使用系统ABI 。 -] else { todo } +])) #let url_external = "https://doc.rust-lang.org/reference/items/external-blocks.html" -#if lang == "en" [ +#tr(( +en: [ Depending on your platform, you might want to target a specific ABI version, which are documented #link(url_external)[here]. -] else if lang == "de" [ +], +de: [ Je nach Plattform möchten Sie möglicherweise eine bestimmte ABI-Version anvisieren; diese sind #link(url_external)[hier] dokumentiert. -] else if lang == "zh" [ +], +zh: [ 取决于你的平台,你可能想要针对一个特定的ABI版本,其记录在#link(url_external)[这里]。 -] else { todo } +])) #divider() -#if lang == "en" [ +#tr(( +en: [ Putting these parts together, you get a function that looks roughly like this. -] else if lang == "de" [ +], +de: [ Wenn man diese Teile zusammensetzt, erhält man eine Funktion, die ungefähr so ​​aussieht. -] else if lang == "zh" [ +], +zh: [ 把这些部分放在一起,你得到一个函数,其粗略看起来像是这个。 -] else { todo } +])) ```rust #[no_mangle] @@ -156,121 +180,152 @@ pub extern "C" fn rust_function() { } ``` -#if lang == "en" [ +#tr(( +en: [ Just as when using `C` code in your Rust project you now need to transform data from and to a form that the rest of the application will understand. -] else if lang == "de" [ +], +de: [ Genau wie bei der Verwendung von C-Code in Ihrem Rust-Projekt müssen Sie nun Daten in eine Form umwandeln -- und aus dieser zurück --, die der Rest der Anwendung versteht. -] else if lang == "zh" [ +], +zh: [ 就像在Rust项目中使用`C`代码时那样,现在需要把数据转换为应用中其它部分可以理解的形式。 -] else { todo } +])) -= #(if lang == "en" [Linking and greater project context] - else if lang == "de" [Verknüpfung und übergeordneter Projektkontext] - else if lang == "zh" [链接和更大的项目上下文] - else { todo }) += #tr(( + en: [Linking and greater project context], + de: [Verknüpfung und übergeordneter Projektkontext], + zh: [链接和更大的项目上下文], +)) -#if lang == "en" [ +#tr(( +en: [ So then, that's one half of the problem solved. How do you use this now? -] else if lang == "de" [ +], +de: [ Damit ist die eine Hälfte des Problems gelöst. Wie verwendet man das nun? -] else if lang == "zh" [ +], +zh: [ 问题只解决了一半。 你现在要如何使用它? -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ *This very much depends on your project and/or build system* -] else if lang == "de" [ +], +de: [ *Das hängt stark von Ihrem Projekt bzw. Ihrem Build-System ab.* -] else if lang == "zh" [ +], +zh: [ *这很大程度上取决于你的项目或者编译系统* -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ `cargo` will create a `my_lib.so`/`my_lib.dll` or `my_lib.a` file, depending on your platform and settings. This library can simply be linked by your build system. -] else if lang == "de" [ +], +de: [ `cargo` erstellt -- je nach Plattform und Einstellungen -- eine Datei namens `my_lib.so`, `my_lib.dll` oder `my_lib.a`. Diese Bibliothek kann einfach von Ihrem Build-System eingebunden (gelinkt) werden. -] else if lang == "zh" [ +], +zh: [ `cargo`将生成一个`my_lib.so`/`my_lib.dll`或者`my_lib.a`文件,取决于你的平台和配置。可以通过编译系统简单地链接这个库。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ However, calling a Rust function from C requires a header file to declare the function signatures. -] else if lang == "de" [ +], +de: [ Um jedoch eine Rust-Funktion aus C heraus aufzurufen, ist eine Header-Datei erforderlich, in der die Funktionssignaturen deklariert werden. -] else if lang == "zh" [ +], +zh: [ 然而,从C调用一个Rust函数要求一个头文件去声明函数的签名。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Every function in your Rust-ffi API needs to have a corresponding header function. -] else if lang == "de" [ +], +de: [ Für jede Funktion in Ihrer Rust-FFI-API muss eine entsprechende Deklaration im Header vorhanden sein. -] else if lang == "zh" [ +], +zh: [ 在Rust-ffi API中的每个函数需要有一个相关的头文件函数。 -] else { todo } +])) ```rust #[no_mangle] pub extern "C" fn rust_function() {} ``` -#if lang == "en" [ +#tr(( +en: [ would then become -] else if lang == "de" [ +], +de: [ würde dann werden -] else if lang == "zh" [ +], +zh: [ 将会变成 -] else { todo } +])) ```c void rust_function(); ``` -#if lang == "en" [ +#tr(( +en: [ etc. -] else if lang == "de" [ +], +de: [ usw. -] else if lang == "zh" [ +], +zh: [ 等等。 -] else { todo } +])) #let ln_cbindgen = link("https://github.com/eqrion/cbindgen")[cbindgen] -#if lang == "en" [ +#tr(( +en: [ There is a tool to automate this process, called #ln_cbindgen which analyses your Rust code and then generates headers for your C and C++ projects from it. -] else if lang == "de" [ +], +de: [ Es gibt ein Werkzeug zur Automatisierung dieses Prozesses namens #ln_cbindgen, das Ihren Rust-Code analysiert und daraus Header-Dateien für Ihre C- und C++-Projekte generiert. -] else if lang == "zh" [ +], +zh: [ 这里有个工具可以自动化这个过程,叫做#ln_cbindgen,其会分析你的Rust代码然后为C和C++项目生成头文件。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ At this point, using the Rust functions from C is as simple as including the header and calling them! -] else if lang == "de" [ +], +de: [ An diesem Punkt ist die Verwendung der Rust-Funktionen aus C heraus so einfach, wie den Header einzubinden und die Funktionen aufzurufen! -] else if lang == "zh" [ +], +zh: [ 此时从C中使用Rust函数非常简单,只需包含头文件和调用它们! -] else { todo } +])) ```c #include "my-rust-project.h" diff --git a/typ/intro/hardware.typ b/typ/intro/hardware.typ index 19ab6d8d..33b9a430 100644 --- a/typ/intro/hardware.typ +++ b/typ/intro/hardware.typ @@ -1,46 +1,58 @@ #import "../config.typ": * -#h1(offset: whole, - if lang == "en" [Meet Your Hardware] - else if lang == "de" [Lernen Sie Ihre Hardware kennen] - else if lang == "ja" [ハードウェアとの出会い] - else if lang == "uk" [Зустрічайте залізо] - else if lang == "zh" [熟悉你的硬件] - else { todo }) +#h1(offset: whole, tr(( + en: [Meet Your Hardware], + de: [Lernen Sie Ihre Hardware kennen], + ja: [ハードウェアとの出会い], + uk: [Зустрічайте залізо], + zh: [熟悉你的硬件], +))) -#if lang == "en" [ +#tr(( +en: [ Let's get familiar with the hardware we'll be working with. -] else if lang == "de" [ +], +de: [ Machen wir uns mit der Hardware vertraut, mit der wir arbeiten werden. -] else if lang == "ja" [ +], +ja: [ これから作業するハードウェアに詳しくなりましょう。 -] else if lang == "uk" [ +], +uk: [ Давайте ознайомимося з обладнанням, з яким ми будемо працювати. -] else if lang == "zh" [ +], +zh: [ 先来熟悉下我们要用的硬件。 -] else { todo } +])) #figure( image("../assets/f3.jpg") ) -= #(if lang in ("en", "zh") [STM32F3DISCOVERY (the "F3")] - else if lang == "de" [STM32F3DISCOVERY (die "F3")] - else if lang == "ja" [STM32F3DISCOVERY ("F3")] - else if lang == "uk" [STM32F3DISCOVERY (скорочено "F3")] - else { todo }) += #tr(( + en: [STM32F3DISCOVERY (the "F3")], + de: [STM32F3DISCOVERY (die "F3")], + ja: [STM32F3DISCOVERY ("F3")], + uk: [STM32F3DISCOVERY (скорочено "F3")], + zh: [STM32F3DISCOVERY (the "F3")], +)) -#if lang == "en" [ +#tr(( +en: [ What does this board contain? -] else if lang == "de" [ +], +de: [ Was beinhaltet diese Platine? -] else if lang == "ja" [ +], +ja: [ 私たちは、本書内でこのボードを"F3"と呼びます。 -] else if lang == "uk" [ +], +uk: [ Що міститься на цій платі? -] else if lang == "zh" [ +], +zh: [ 这个板子有什么? -] else { todo } +])) #let url_f303 = "https://www.st.com/en/microcontrollers/stm32f303vc.html" #let wiki_accelerometer = "https://en.wikipedia.org/wiki/Accelerometer" @@ -49,7 +61,8 @@ #let wiki_gyroscope = "https://en.wikipedia.org/wiki/Gyroscope" #let url_l3gd20 = "https://www.pololu.com/file/0J563/L3GD20.pdf" #let url_f103 = "https://www.st.com/en/microcontrollers/stm32f103cb.html" -#if lang == "en" [ +#tr(( +en: [ - A #link(url_f303)[STM32F303VCT6] microcontroller. This microcontroller has - A single-core ARM Cortex-M4F processor with hardware support for single-precision floating point operations and a maximum clock @@ -69,7 +82,8 @@ - A second microcontroller: a #link(url_f103)[STM32F103]. This microcontroller is actually part of an on-board programmer / debugger and is connected to the Mini-USB port named "USB ST-LINK". -] else if lang == "de" [ +], +de: [ - Ein #link(url_f303)[STM32F303VCT6]-Mikrocontroller. Dieser Mikrocontroller hat - Ein Single-Core-ARM-Cortex-M4F-Prozessor mit Hardware-Unterstützung @@ -94,7 +108,8 @@ Dieser Mikrocontroller ist eigentlich Teil eines On-Board-Programmers/Debuggers und mit dem Mini-USB-Anschluss namens „USB ST-LINK" verbunden. -] else if lang == "ja" [ +], +ja: [ このボードには何が搭載されているか見てみましょう。 - STM32F303VCT6マイクロコントローラが1つ。このマイクロコントローラは、次のものを搭載しています。 - 単精度浮動小数点演算をハードウェアサポートし、最大72MHzのクロック周波数で動作するシングルコアのARM @@ -114,7 +129,8 @@ - "USB USER"というラベルが付いている第2のUSBポート。このUSBポートは、メインマイクロコントローラ (STM32F303VCT6)に接続されており、アプリケーションで利用できます。 -] else if lang == "uk" [ +], +uk: [ - Мікроконтролер #link(url_f303)[STM32F303VCT6]. Цей мікроконтролер має: - Одноядерний процесор ARM Cortex-M4F з апаратною підтримкою операцій з плаваючою комою одинарної точності і максимальною тактовою частотою 72 МГц. @@ -130,7 +146,8 @@ - 8 користувацьких світлодіодів, розташованих у формі компаса. - Другий мікроконтролер: #link(url_f103)[STM32F103]. Цей мікроконтролер фактично є частиною програматора/відладчика, розміщеного на платі, і підключений до USB-порту з назвою "USB ST-LINK". -] else if lang == "zh" [ +], +zh: [ - 一个#link(url_f303)[STM32F303VCT6]微控制器。这个微控制器包含 - 一个单核的ARM Cortex-M4F 处理器,支持单精度浮点运算,72MHz的最大时钟频率。 @@ -146,40 +163,48 @@ - 一个二级微控制器: #link(url_f103)[STM32F103]。这个微控制器实际上是一个板载编程器/调试器的一部分,与名为"USB ST-LINK"的USB端口相连。 -] else { todo } +])) #let url_f3disco = "https://www.st.com/en/evaluation-tools/stm32f3discovery.html" -#if lang == "en" [ +#tr(( +en: [ For a more detailed list of features and further specifications of the board take a look at the #link(url_f3disco)[STMicroelectronics] website. -] else if lang == "de" [ +], +de: [ Eine detailliertere Liste der Funktionen und weitere technische Daten des Boards finden Sie auf der Website von #link(url_f3disco)[STMicroelectronics]. -] else if lang == "uk" [ +], +uk: [ Більш детальний перелік функцій та специфікацій плати можна знайти на сайті #link(url_f3disco)[STMicroelectronics]. -] else if lang == "zh" [ +], +zh: [ 关于所列举的功能的更多细节和开发板的更多规格请查阅#link(url_f3disco)[STMicroelectronics]网站。 -] else { todo } +])) #let url_f303_ds = "https://www.st.com/resource/en/datasheet/stm32f303vc.pdf" -#if lang == "en" [ +#tr(( +en: [ A word of caution: be careful if you want to apply external signals to the board. The microcontroller STM32F303VCT6 pins take a nominal voltage of 3.3 volts. For further information consult the #link(url_f303_ds)[6.2 Absolute maximum ratings section in the manual] -] else if lang == "de" [ +], +de: [ Ein wichtiger Hinweis: Seien Sie vorsichtig, wenn Sie externe Signale an das Board anlegen möchten. Die Pins des Mikrocontrollers STM32F303VCT6 sind für eine Nennspannung von 3,3 Volt ausgelegt. Weitere Informationen finden Sie im Abschnitt „6.2 Absolute maximum ratings" (Absolute Grenzwerte) im #link(url_f303_ds)[Datenblatt]. -] else if lang == "uk" [ +], +uk: [ Застереження: будьте обережні, подаючи на плату зовнішні сигнали. Виводи мікроконтролера STM32F303VCT6 мають номінальну напругу 3,3 вольта. Для отримання додаткової інформації зверніться до розділу #link(url_f303_ds)[6.2 Absolute maximum ratings section у Reference Manual] -] else if lang == "zh" [ +], +zh: [ 提醒一句: 如果想要为板子提供外部信号,请小心。微控制器STM32F303VCT6管脚的标称电压是3.3伏。更多信息请查看#link(url_f303_ds)[6.2 Absolute maximum ratings section in the manual]。 -] else { todo } \ No newline at end of file +])) \ No newline at end of file diff --git a/typ/intro/index.typ b/typ/intro/index.typ index 10c1c9d8..d10bdb5c 100644 --- a/typ/intro/index.typ +++ b/typ/intro/index.typ @@ -1,68 +1,82 @@ #import "../config.typ": * -#h1(if lang == "en" [Introduction] - else if lang == "de" [Einleitung] - else if lang == "ja" [導入] - else if lang == "uk" [Вступ] - else if lang == "zh" [引言] - else { todo }) +#h1(tr(( + en: [Introduction], + de: [Einleitung], + ja: [導入], + uk: [Вступ], + zh: [引言], +))) #set heading(offset: whole*2) -#if lang == "en" [ +#tr(( +en: [ Welcome to The Embedded Rust Book: An introductory book about using the Rust Programming Language on "Bare Metal" embedded systems, such as Microcontrollers. -] else if lang == "de" [ +], +de: [ Willkommen bei „Das Embedded-Rust Buch“ – einem Einführungswerk zur Verwendung der Programmiersprache Rust auf „Bare-Metal“-Embedded-Systemen, wie etwa Mikrocontrollern. -] else if lang == "ja" [ +], +ja: [ _The Embedded Rust Book_へようこそ。Rustをマイクロコントローラのような、「ベアメタル」の組込みシステムで使うための入門書です。 -] else if lang == "uk" [ +], +uk: [ Ласкаво просимо до The Embedded Rust Book: ознайомчої книги про використання мови програмування Rust у вбудованих "Bare Metal" системах, таких як мікроконтролери. -] else if lang == "zh" [ +], +zh: [ 欢迎阅读嵌入式Rust:一本关于如何在裸机(比如,微处理器)上使用Rust编程语言的入门书籍。 -] else { todo } +])) -= #(if lang == "en" [Who Embedded Rust is For] - else if lang == "de" [Für wen Embedded-Rust geeignet ist] - else if lang == "ja" [組込みRustは誰のためのもの] - else if lang == "uk" [Для кого Вбудований Rust] - else if lang == "zh" [嵌入式Rust是为谁准备的] - else { todo }) += #tr(( + en: [Who Embedded Rust is For], + de: [Für wen Embedded-Rust geeignet ist], + ja: [組込みRustは誰のためのもの], + uk: [Для кого Вбудований Rust], + zh: [嵌入式Rust是为谁准备的], +)) #let url_rustbook = "https://doc.rust-lang.org/book/ch00-00-introduction.html" -#if lang == "en" [ +#tr(( +en: [ Embedded Rust is for everyone who wants to do embedded programming while taking advantage of the higher-level concepts and safety guarantees the Rust language provides. (See also #link(url_rustbook)[Who Rust Is For]) -] else if lang == "de" [ +], +de: [ Embedded-Rust ist für alle, die eingebettete Systeme programmieren und dabei die Vorteile der fortgeschrittenen Konzepte und Sicherheitsgarantien der Rust-Sprache nutzen möchten. (Siehe auch #link(url_rustbook)[Für wen ist Rust geeignet?]) -] else if lang == "ja" [ +], +ja: [ 組込みRustは、Rustの高い抽象度と安全性のもと、組込みプログラミングをしたい人のためのものです。 (#url_rustbook)[Rustは誰のためのもの]も合わせて見て下さい) -] else if lang == "uk" [ +], +uk: [ Вбудований Rust для кожного, хто бажає програмувати вбудовані системи використовуючи переваги концепцій вищого рівня та гарантій безпеки, які надає мова Rust. (Дивіться також #link(url_rustbook)[Для кого Rust]) -] else if lang == "zh" [ +], +zh: [ 嵌入式Rust是为了那些既想要进行嵌入式编程,又想要使用Rust语言所提供的高级概念和安全保障的人们而准备的(参见#link(url_rustbook)[Who Rust Is For]) -] else { todo } - -= #(if lang == "en" [Scope] - else if lang == "de" [Umfang] - else if lang == "ja" [スコープ] - else if lang == "uk" [Область застосування] - else if lang == "zh" [本书范围] - else { todo }) - -#if lang == "en" [ +])) + += #tr(( + en: [Scope], + de: [Umfang], + ja: [スコープ], + uk: [Область застосування], + zh: [本书范围], +)) + +#tr(( +en: [ The goals of this book are: - Get developers up to speed with embedded Rust development. i.e.~How to et up a development environment. @@ -71,7 +85,8 @@ correct embedded software. - Serve as a cookbook in some cases. e.g.~How do I mix C and Rust in a single project? -] else if lang == "de" [ +], +de: [ Die Ziele dieses Buches sind: - Machen Sie Entwickler mit der Embedded-Rust-Entwicklung vertraut -- zum Beispiel damit, wie man eine Entwicklungsumgebung einrichtet. @@ -80,12 +95,14 @@ Rust optimal nutzt, um korrektere Embedded-Software zu schreiben. - Dient in einigen Fällen als Kochbuch -- zum Beispiel: Wie kombiniere ich C und Rust in einem einzigen Projekt? -] else if lang == "ja" [ +], +ja: [ この本の目的は、以下の通りです。 - 組込みRustをできる限り速く開始できるようにします。すなわち、開発環境のセットアップ方法です。 - 組込み開発におけるRustの_現在_のベストプラクティスを共有します。つまり、より正しい組込みソフトウェアを書くための、Rustの最善な利用方法です。 - いくつかのケースに対するマニュアルを提供します。例えば、1つのプロジェクト内で、C言語とRustとを混在する方法です。 -] else if lang == "uk" [ +], +uk: [ Цілі цієї книги: - Надати розробникам швидкий доступ до розробки вбудованих систем на Rust. Тобто як налаштувати середовище розробки. @@ -93,48 +110,56 @@ для розробки вбудованих систем. Тобто як найкраще використовувати можливості мови Rust для написання більш коректних вбудованих програм. - Cлугувати кулінарною книгою у деяких випадках. Наприклад як поєднати C та Rust в одному проекті? -] else if lang == "zh" [ +], +zh: [ 这本书的目的是: - 让开发者快速上手Rust嵌入式开发,比如,如何设置一个开发环境。 - 分享那些关于使用Rust进行嵌入式开发的,现存的,最好的实践经验,比如,如何最大程度上地利用好Rust语言的特性去写更正确的嵌入式软件 - 某种程度下作为工具书,比如,如何在一个项目里将C和Rust混合在一起使用 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ This book tries to be as general as possible but to make things easier for both the readers and the writers it uses the ARM Cortex-M architecture in all its examples. However, the book doesn't assume that the reader is familiar with this particular architecture and explains details particular to this architecture where required. -] else if lang == "de" [ +], +de: [ Dieses Buch ist bestrebt, so allgemein wie möglich zu bleiben; um jedoch sowohl den Lesern als auch den Autoren die Arbeit zu erleichtern, verwendet es in allen Beispielen die ARM-Cortex-M-Architektur. Dennoch setzt das Buch keine Vorkenntnisse zu dieser speziellen Architektur voraus und erläutert die entsprechenden Besonderheiten, wo immer dies erforderlich ist. -] else if lang == "ja" [ +], +ja: [ 本書は出来る限り一般的な事項を取り扱います。ただし、説明を簡単にするために、全ての例で、ARM Cortex-Mアーキテクチャを利用します。 読者は、このアーキテクチャに詳しい必要はありません。本書では、アーキテクチャ固有の詳細について、必要に応じて説明をします。 -] else if lang == "uk" [ +], +uk: [ Ця книга намагається бути якомога більш загальною, але щоб полегшити роботу як читачам, так і авторам, у ній використовується архітектура ARM Cortex-M у всіх її прикладах. Однак книга не передбачає, що читач знайомий із цією конкретною архітектурою, і пояснює деталі, пов’язані з цією архітектурою, де потрібно. -] else if lang == "zh" [ +], +zh: [ 虽然尽可能地尝试让这本书可以用于大多数场景,但是为了使读者和作者更容易理解,在所有的示例中,这本书都使用了ARM Cortex-M架构。然而,这本书并不需要读者熟悉这个架构,书中会在需要时对这个架构的特定细节进行解释。 -] else { todo } - -= #(if lang == "en" [Who This Book is For] - else if lang == "de" [Für wen dieses Buch gedacht ist] - else if lang == "ja" [この本は誰のためのもの] - else if lang == "uk" [Для кого Ця Книга] - else if lang == "zh" [这本书是为谁准备的] - else { todo }) - -#if lang == "en" [ +])) + += #tr(( + en: [Who This Book is For], + de: [Für wen dieses Buch gedacht ist], + ja: [この本は誰のためのもの], + uk: [Для кого Ця Книга], + zh: [这本书是为谁准备的], +)) + +#tr(( +en: [ This book caters towards people with either some embedded background or some Rust background, however we believe everybody curious about embedded Rust programming can get something out of this book. For those @@ -143,7 +168,8 @@ of the book and improve your reading experience. You can check out the "Other Resources" section to find resources on topics you might want to catch up on. -] else if lang == "de" [ +], +de: [ Dieses Buch richtet sich an Personen mit Vorkenntnissen im Bereich Embedded-Systeme oder Rust; wir sind jedoch der Meinung, dass jeder, der sich für die Embedded-Programmierung mit Rust interessiert, von diesem @@ -153,27 +179,32 @@ Buch zu ziehen und das Leseerlebnis zu verbessern. Im Abschnitt „Weitere Ressourcen" finden Sie Hinweise zu Themen, in die Sie sich bei Bedarf einarbeiten können. -] else if lang == "ja" [ +], +ja: [ 本書は、組込み開発か、Rustかのバックグラウンドを持つ人々に向けたものです。しかし、組込みRustに興味がある人なら、誰でも、この本から何かを得られると思います。本書による学習効果を高めるために、事前知識が不足している読者は、「仮定と前提条件」のセクションを読み、不足している知識を補うことをお勧めします。不足知識を補うリソースを見つけるために、「その他のリソース」セクションをチェックすることができます。 -] else if lang == "uk" [ +], +uk: [ Ця книга розрахована на людей, які мають досвід роботи з вбудованими програмами або з Rust, але ми віримо, що всі, хто цікавиться програмуванням у вбудованому Rust, можуть щось отримати з цієї книги. Для тих, хто не має попередніх знань, ми пропонуємо прочитати розділ «Припущення та передумови» та надолужити пропущені знання, щоб отримати більше від книги та покращити свій досвід читання. Ви можете переглянути розділ «Інші ресурси», щоб знайти ресурси на теми, які, можливо, захочете надолужити. -] else if lang == "zh" [ +], +zh: [ 这本书适合那些有一些嵌入式背景或者有Rust背景的人,然而我相信每一个对Rust嵌入式编程好奇的人都能从这本书中获得某些收获。对于那些先前没有任何经验的人,我们建议你读一下"要求和预备知识"部分。从其它资料中获取、补充缺失的知识,这样能提高你的阅读体验。你可以看看"其它资源"部分,以找到你感兴趣的那些主题的资源。 -] else { todo } +])) -== #(if lang == "en" [Assumptions and Prerequisites] - else if lang == "de" [Annahmen und Voraussetzungen] - else if lang == "ja" [仮定と前提条件] - else if lang == "uk" [Припущення та передумови] - else if lang == "zh" [要求和预备知识] - else { todo }) +== #tr(( + en: [Assumptions and Prerequisites], + de: [Annahmen und Voraussetzungen], + ja: [仮定と前提条件], + uk: [Припущення та передумови], + zh: [要求和预备知识], +)) #let url_ediion = "https://doc.rust-lang.org/edition-guide/" -#if lang == "en" [ +#tr(( +en: [ - You are comfortable using the Rust Programming Language, and have written, run, and debugged Rust applications on a desktop environment. You should also be familiar with the idioms of the @@ -187,7 +218,8 @@ - Memory Mapped Peripherals - Interrupts - Common interfaces such as I2C, SPI, Serial, etc. -] else if lang == "de" [ +], +de: [ - Sie sind sicher im Umgang mit der Programmiersprache Rust und haben bereits Rust-Anwendungen in einer Desktop-Umgebung geschrieben, ausgeführt und deren Fehler behoben. Zudem sollten Sie mit den Idiomen @@ -201,7 +233,8 @@ - Im Speicher abgebildete Peripheriegeräte - Interrupts - Gängige Schnittstellen wie I2C, SPI, seriell usw. -] else if lang == "ja" [ +], +ja: [ - Rustでのプログラミングを楽しんでおり、デスクトップ環境でRustアプリケーションを書いたり、実行したり、デバッグしたりしたことがあることを前提とします。また、本書ではRust 2018を対象とするため、2018 editionのイディオムに慣れ親しんでいる必要があります。 @@ -211,7 +244,8 @@ - メモリマップ方式のペリフェラル - 割り込み - I2C、SPI、シリアルといった一般的なインタフェース -] else if lang == "uk" [ +], +uk: [ - Вам зручно користуватися мовою програмування Rust і ви вже писали, запускали та налагоджували програми Rust у середовищі робочого столу. Ви також повинні бути знайомі з ідіомами #link(url_ediion)[2018 Edition], оскільки ця книга спрямована на Rust 2018. @@ -221,7 +255,8 @@ - Периферійні пристрої з відображенням у пам'яті - Переривання - Загальні інтерфейси, такі як I2C, SPI, Serial тощо. -] else if lang == "zh" [ +], +zh: [ - 你可以轻松地使用Rust编程语言,且在一个桌面环境上写过,运行过,调试过Rust应用。你应该也要熟悉#link(url_ediion)[2018 edition]的术语,因为这本书是面向Rust 2018的。 @@ -230,30 +265,36 @@ - 存储映射的外设(Memory Mapped Peripherals) - 中断 - I2C,SPI,串口等等常见的接口 -] else { todo } - -== #(if lang == "en" [Other Resources] - else if lang == "de" [Weitere Ressourcen] - else if lang == "ja" [その他のリソース] - else if lang == "uk" [Інші ресурси] - else if lang == "zh" [其它资源] - else { todo }) - -#if lang == "en" [ +])) + +== #tr(( + en: [Other Resources], + de: [Weitere Ressourcen], + ja: [その他のリソース], + uk: [Інші ресурси], + zh: [其它资源], +)) + +#tr(( +en: [ If you are unfamiliar with anything mentioned above or if you want more information about a specific topic mentioned in this book you might find some of these resources helpful. -] else if lang == "de" [ +], +de: [ Falls Ihnen einer der oben genannten Punkte nicht vertraut ist oder Sie mehr über ein bestimmtes, in diesem Buch erwähntes Thema erfahren möchten, könnten einige dieser Ressourcen hilfreich für Sie sein. -] else if lang == "ja" [ +], +ja: [ もしあなたが上述した何らかの事項をよく知らない場合、もしくは、本書内の特定トピックに関して、より詳細な情報を知りたい場合、これらのリソースが役に立つでしょう。 -] else if lang == "uk" [ +], +uk: [ Якщо ви не знайомі з чимось із згаданого вище або вам потрібна додаткова інформація про конкретну тему, згадану в цій книзі, деякі з цих ресурсів можуть стати в нагоді. -] else if lang == "zh" [ +], +zh: [ 如果你还不熟悉上面提到的东西或者你对这本书中提到的某个特定主题感兴趣,你也许能从这些资源中找到有用的信息。 -] else { todo } +])) #figure( kind: table, @@ -261,91 +302,122 @@ columns: (37.84%, 27.03%, 35.14%), align: (center,left,left,), table.header( - if lang == "en" [Topic] - else if lang == "de" [Thema] - else if lang == "ja" [トピック] - else if lang == "zh" [主题] - else { todo }, - if lang == "en" [Resource] - else if lang == "de" [Ressource] - else if lang == "ja" [リソース] - else if lang == "zh" [资源] - else { todo }, - if lang == "en" [Description] - else if lang == "de" [Beschreibung] - else if lang == "ja" [説明] - else if lang == "zh" [描述] - else { todo } + tr(( + en: [Topic], + de: [Thema], + ja: [トピック], + zh: [主题], + )), + tr(( + en: [Resource], + de: [Ressource], + ja: [リソース], + zh: [资源], + )), + tr(( + en: [Description], + de: [Beschreibung], + ja: [説明], + zh: [描述], + )), ), [Rust], link("https://doc.rust-lang.org/book/")[Rust Book], - if lang == "en" [ - If you are not yet comfortable with Rust, we highly suggest reading this book. - ] else if lang == "de" [ - Wenn Sie sich mit Rust noch nicht sicher fühlen, - empfehlen wir Ihnen dringend, dieses Buch zu lesen. - ] else if lang == "ja" [ - もしRustに親しんでいない場合、この本を読むことを強くお勧めします。 - ] else if lang == "zh" [ - 如果你还不熟悉Rust,我们强烈建议你读这本书. - ] else { todo }, + tr(( + en: [ + If you are not yet comfortable with Rust, we highly suggest reading this book. + ], + de: [ + Wenn Sie sich mit Rust noch nicht sicher fühlen, + empfehlen wir Ihnen dringend, dieses Buch zu lesen. + ], + ja: [ + もしRustに親しんでいない場合、この本を読むことを強くお勧めします。 + ], + zh: [ + 如果你还不熟悉Rust,我们强烈建议你读这本书. + ], + )), [Rust, Embedded], link("https://docs.rust-embedded.org/discovery/")[Discovery Book], - if lang == "en" [ - If you have never done any embedded programming, this book might be a better start - ] else if lang == "de" [ - Wenn Sie sich noch nie mit eingebetteter Programmierung beschäftigt haben, - ist dieses Buch möglicherweise der bessere Einstieg - ] else if lang == "zh" [ - 如果你从没做过嵌入式编程,这本书可能是个更好的开端. - ] else { todo }, + tr(( + en: [ + If you have never done any embedded programming, this book might be a better start + ], + de: [ + Wenn Sie sich noch nie mit eingebetteter Programmierung beschäftigt haben, + ist dieses Buch möglicherweise der bessere Einstieg + ], + zh: [ + 如果你从没做过嵌入式编程,这本书可能是个更好的开端. + ], + )), [Rust, Embedded], link("https://docs.rust-embedded.org")[Embedded Rust Bookshelf], - if lang == "en" [ - Here you can find several other resources provided by Rust's Embedded Working Group. - ] else if lang == "de" [ - Hier finden Sie weitere Ressourcen, die von der Embedded Working Group - von Rust bereitgestellt werden. - ] else if lang == "ja" [ - Rust組込みワーキンググループによるいくらかのリソースがあります。 - ] else if lang == "zh" [ - 在这里,你可以找到由Rust的嵌入式工作组提供的许多其它资源. - ] else { todo }, + tr(( + en: [ + Here you can find several other resources provided by Rust's Embedded Working Group. + ], + de: [ + Hier finden Sie weitere Ressourcen, die von der Embedded Working Group + von Rust bereitgestellt werden. + ], + ja: [ + Rust組込みワーキンググループによるいくらかのリソースがあります。 + ], + zh: [ + 在这里,你可以找到由Rust的嵌入式工作组提供的许多其它资源. + ], + )), [Rust, Embedded], link("https://docs.rust-embedded.org/embedonomicon/")[Embedonomicon], - if lang == "en" [ - The nitty gritty details when doing embedded programming in Rust. - ] else if lang == "de" [ - Die Feinheiten der Embedded-Programmierung mit Rust. - ] else if lang == "ja" [ - Rustで組込みプログラミングを行うときのより深い詳細が記載されています。 - ] else if lang == "zh" [ - 用Rust进行嵌入式编程的细节. - ] else { todo }, + tr(( + en: [ + The nitty gritty details when doing embedded programming in Rust. + ], + de: [ + Die Feinheiten der Embedded-Programmierung mit Rust. + ], + ja: [ + Rustで組込みプログラミングを行うときのより深い詳細が記載されています。 + ], + zh: [ + 用Rust进行嵌入式编程的细节. + ], + )), [Rust, Embedded], link("https://docs.rust-embedded.org/faq.html")[embedded FAQ], - if lang == "en" [ - Frequently asked questions about Rust in an embedded context. - ] else if lang == "de" [ - Häufig gestellte Fragen zu Rust im Embedded-Umfeld. - ] else if lang == "ja" [ - 組込みでRustを使う際のよくある質問と回答です。 - ] else if lang == "zh" [ - Rust在嵌入式上下文中遇到的常见问题. - ] else { todo }, + tr(( + en: [ + Frequently asked questions about Rust in an embedded context. + ], + de: [ + Häufig gestellte Fragen zu Rust im Embedded-Umfeld. + ], + ja: [ + 組込みでRustを使う際のよくある質問と回答です。 + ], + zh: [ + Rust在嵌入式上下文中遇到的常见问题. + ], + )), [Rust, Embedded], link("https://google.github.io/comprehensive-rust/bare-metal.html")[Comprehensive Rust 🦀: Bare Metal], - if lang == "en" [ - Teaching material for a 4-day class on bare-metal Rust development - ] else if lang == "de" [ - Unterrichtsmaterial für einen viertägigen Kurs zu Bare-Metal Rust development - ] else if lang == "zh" [ - 用于四天课时的裸机Rust开发课程的教学资料. - ] else { todo }, + tr(( + en: [ + Teaching material for a 4-day class on bare-metal Rust development + ], + de: [ + Unterrichtsmaterial für einen viertägigen Kurs zu Bare-Metal Rust development + ], + zh: [ + 用于四天课时的裸机Rust开发课程的教学资料. + ], + )), [Interrupts], link("https://en.wikipedia.org/wiki/Interrupt")[Interrupt], @@ -361,71 +433,81 @@ ) ) -== #(if lang == "en" [Translations] - else if lang == "de" [Übersetzungen] - else if lang == "uk" [Переклади] - else if lang == "zh" [翻译] - else { todo }) - -#{ -let jp_ln = "https://tomoyuki-nakabayashi.github.io/book/" -let jp_rep = "https://github.com/tomoyuki-nakabayashi/book" -let cn_ln = "https://xxchang.github.io/book/" -let cn_rep = "https://github.com/XxChang/book" -if lang == "en" [ +== #tr(( + en: [Translations], + de: [Übersetzungen], + uk: [Переклади], + zh: [翻译], +)) + +#let jp_ln = "https://tomoyuki-nakabayashi.github.io/book/" +#let jp_rep = "https://github.com/tomoyuki-nakabayashi/book" +#let cn_ln = "https://xxchang.github.io/book/" +#let cn_rep = "https://github.com/XxChang/book" +#tr(( +en: [ This book has been translated by generous volunteers. If you would like your translation listed here, please open a PR to add it. - #link(jp_ln)[Japanese] (#link(jp_rep)[repository]) - #link(cn_ln)[Chinese] (#link(cn_rep)[repository]) -] else if lang == "de" [ +], +de: [ Dieses Buch wurde von großzügigen Freiwilligen übersetzt. Wenn Ihre Übersetzung hier aufgeführt werden soll, erstellen Sie bitte einen Pull Request, um sie hinzuzufügen. - #link(jp_ln)[Japanisch] (#link(jp_rep)[repository]) - #link(cn_ln)[Chinesisch] (#link(cn_rep)[repository]) -] else if lang == "uk" [ +], +uk: [ Цю книгу перекладено волонтерами. Якщо ви хочете, щоб ваш переклад був тут, будь ласка, відкрийте запит на злиття, щоб додати його. - #link(jp_ln)[Японською] (#link(jp_rep)[репозиторій]) - #link(cn_ln)[Китайською] (#link(cn_rep)[репозиторій]) -] else if lang == "zh" [ +], +zh: [ 这本书是已经被一些慷慨的志愿者们翻译了。如果你想要将你的翻译列在这里,请打开一个PR去添加它。 - #link(jp_ln)[日文] (#link(jp_rep)[repository]) - #link(cn_ln)[中文] (#link(cn_rep)[repository]) -] else { todo } -} - -== #(if lang == "en" [How to Use This Book] - else if lang == "de" [Wie Sie dieses Buch nutzen] - else if lang == "ja" [この本はどう使う] - else if lang == "uk" [Як читати цю книгу] - else if lang == "zh" [如何使用这本书] - else { todo }) - -#if lang == "en" [ +])) + +== #tr(( + en: [How to Use This Book], + de: [Wie Sie dieses Buch nutzen], + ja: [この本はどう使う], + uk: [Як читати цю книгу], + zh: [如何使用这本书], +)) + +#tr(( +en: [ This book generally assumes that you're reading it front-to-back. Later chapters build on concepts in earlier chapters, and earlier chapters may not dig into details on a topic, revisiting the topic in a later chapter. -] else if lang == "de" [ +], +de: [ Dieses Buch ist im Allgemeinen so konzipiert, dass es von Anfang bis Ende gelesen wird. Spätere Kapitel bauen auf den Konzepten früherer Kapitel auf; dabei werden bestimmte Themen in den früheren Kapiteln möglicherweise noch nicht bis ins Detail behandelt, sondern erst in einem späteren Kapitel erneut aufgegriffen. -] else if lang == "ja" [ +], +ja: [ この本は、前から順番に読んでいくことを想定しています。後半の章は、前半の章で説明する概念に基づいて成り立っています。前半の章では、トピックの詳細に深入りせず、後半の章で再訪問します。 -] else if lang == "uk" [ +], +uk: [ Ця книга зазвичай передбачає, що ви читаєте її послідовно. Подальші розділи ґрунтуються на концепціях попередніх розділів, і попередні розділи можуть не заглиблюватися в деталі теми, повертаючись до неї в наступному розділі. -] else if lang == "zh" [ +], +zh: [ 这本书通常假设你是按顺序阅读的。之后的章节是建立在先前的章节中提到的概念之上的,先前章节可能不会深入一个主题的细节,因为在随后的章节将会再次重温这个主题。 -] else { todo } +])) #let url_f3disco = "http://www.st.com/en/evaluation-tools/stm32f3discovery.html" -#if lang == "en" [ +#tr(( +en: [ This book will be using the #link(url_f3disco)[STM32F3DISCOVERY] development board from STMicroelectronics for the majority of the examples contained within. This board is based on the ARM Cortex-M @@ -433,7 +515,8 @@ if lang == "en" [ based on this architecture, peripherals and other implementation details of Microcontrollers are different between different vendors, and often even different between Microcontroller families from the same vendor. -] else if lang == "de" [ +], +de: [ Für den Großteil der in diesem Buch enthaltenen Beispiele wird das Entwicklungsboard #link(url_f3disco)[STM32F3DISCOVERY] von STMicroelectronics verwendet. Dieses Board basiert auf der @@ -443,10 +526,12 @@ if lang == "en" [ Implementierungsdetails der Mikrocontroller je nach Hersteller -- und oft sogar zwischen verschiedenen Mikrocontroller-Familien desselben Herstellers. -] else if lang == "ja" [ +], +ja: [ この本は、ほとんどの例で、STマイクロエレクトロニクスの#link(url_f3disco)[STM32F3DISCOVERY]開発ボードを使用します。このボードは、ARM Cortex-Mアーキテクチャをベースとしています。基本機能はこのアーキテクチャベースのCPUでは共通です。一方、ペリフェラルとマイクロコントローラ実装の詳細は、他のベンダーと異なります。同じSTマイクロエレクトロニクスのマイクロコントローラファミリでも、違いがあります。 -] else if lang == "uk" [ +], +uk: [ Для більшості наведених у цій книзі прикладів використовуватиметься розробна плата #link(url_f3disco)[STM32F3DISCOVERY] від STMicroelectronics. Ця плата заснована на архітектурі ARM Cortex-M, і хоча основні функції однакові @@ -454,94 +539,115 @@ if lang == "en" [ деталі реалізації відрізняються у мікроконтролерах різних постачальників, і часто навіть різняться між сімействами мікроконтролерів одного й того ж постачальника. -] else if lang == "zh" [ +], +zh: [ 在大多数示例中这本书将使用#link(url_f3disco)[STM32F3DISCOVERY]开发板。这个板子是基于ARM Cortex-M架构的,且基本功能与大多数基于这个架构的CPUs功能相似。微处理器的外设和其它实现细节在不同的厂家之间是不同的,甚至来自同一个厂家,不同处理器系列之间也是不同的。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ For this reason, we suggest purchasing the #link(url_f3disco)[STM32F3DISCOVERY] development board for the purpose of following the examples in this book. -] else if lang == "de" [ +], +de: [ Aus diesem Grund empfehlen wir die Anschaffung des Entwicklungsboards #link(url_f3disco)[STM32F3DISCOVERY], um die Beispiele in diesem Buch praktisch nachvollziehen zu können. -] else if lang == "ja" [ +], +ja: [ 上記の理由から、本書内の例を理解するために、#link(url_f3disco)[STM32F3DISCOVERY]開発ボードを購入することをお勧めします。 -] else if lang == "uk" [ +], +uk: [ З цієї причини ми пропонуємо придбати плату розробки #link(url_f3disco)[STM32F3DISCOVERY] щоб повторювати приклади з цієї книги. -] else if lang == "zh" [ +], +zh: [ 因此我们建议购买#link(url_f3disco)[STM32F3DISCOVERY]开发板来尝试这本书中的例子。 -] else { todo } +])) -== #(if lang == "en" [Contributing to This Book] - else if lang == "de" [Beiträge zu diesem Buch] - else if lang == "uk" [Внесок у цю книгу] - else if lang == "zh" [贡献] - else { todo }) +== #tr(( + en: [Contributing to This Book], + de: [Beiträge zu diesem Buch], + uk: [Внесок у цю книгу], + zh: [贡献], +)) #let url_repo = "https://github.com/rust-embedded/book" #let url_team = "https://github.com/rust-embedded/wg#the-resources-team" -#if lang == "en" [ +#tr(( +en: [ The work on this book is coordinated in #link(url_repo)[this repository] and is mainly developed by the #link(url_team)[resources team]. -] else if lang == "de" [ +], +de: [ Die Arbeit an diesem Buch wird in #link(url_repo)[diesem Repository] koordiniert und hauptsächlich vom #link(url_team)[Ressourcen-Team] vorangetrieben. -] else if lang == "uk" [ +], +uk: [ Робота над цією книгою координується в #link(url_repo)[цьому сховищі] і в основному розробляється #link(url_team)[командою ресурсів]. -] else if lang == "zh" [ +], +zh: [ 这本书的工作主要在#link(url_repo)[这个仓库]里管理,且主要由#link(url_team)[resouces team]开发。 -] else { todo } +])) #let url_issues = "https://github.com/rust-embedded/book/issues/" -#if lang == "en" [ +#tr(( +en: [ If you have trouble following the instructions in this book or find that some section of the book is not clear enough or hard to follow then that's a bug and it should be reported in #link(url_issues)[the issue tracker] of this book. -] else if lang == "de" [ +], +de: [ Wenn Sie Schwierigkeiten haben, den Anweisungen in diesem Buch zu folgen, oder wenn ein Abschnitt des Buches nicht klar genug oder schwer verständlich ist, handelt es sich um einen Fehler; dieser sollte im #link(url_issues)[Problem-Verfolgungswerkzeug] des Buches gemeldet werden. -] else if lang == "uk" [ +], +uk: [ Якщо у вас виникли проблеми з дотриманням інструкцій з цієї книги або ви вважаєте, що якийсь розділ книги недостатньо зрозумілий або складний для виконання, то це помилка, і про неї слід повідомити в #link(url_issues)[трекері випуску] цієї книги. -] else if lang == "zh" [ +], +zh: [ 如果你按着这本书的操作遇到了什么麻烦,或者这本书的一些部分不够清楚,或者很难进行下去,那这本书就是有个bug,这个bug应该被报道给这本书的#link("https://github.com/rust-embedded/book/issues/")[the issue tracker] 。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Pull requests fixing typos and adding new content are very welcome! -] else if lang == "de" [ +], +de: [ Pull Requests, die Tippfehler korrigieren oder neue Inhalte hinzufügen, sind herzlich willkommen! -] else if lang == "uk" [ +], +uk: [ Запити на виправлення помилок і додавання нового матеріалу дуже вітаються! -] else if lang == "zh" [ +], +zh: [ 修改拼写错误和添加新内容的Pull requests非常欢迎! -] else { todo } +])) -= #(if lang == "en" [Re-using this material] - else if lang == "de" [Wiederverwendung dieses Materials] - else if lang == "uk" [Повторне використання цього матеріалу] - else if lang == "zh" [二次使用这个材料] - else { todo }) += #tr(( + en: [Re-using this material], + de: [Wiederverwendung dieses Materials], + uk: [Повторне використання цього матеріалу], + zh: [二次使用这个材料], +)) #{ let mit = "https://opensource.org/licenses/MIT" let apache = "http://www.apache.org/licenses/LICENSE-2.0" let legalcode = "https://creativecommons.org/licenses/by-sa/4.0/legalcode" -if lang == "en" [ +tr(( + en: [ This book is distributed under the following licenses: - The code samples and free-standing Cargo projects contained within this book are licensed under the terms of both the @@ -551,7 +657,8 @@ if lang == "en" [ are licensed under the terms of the Creative Commons #link(legalcode)[CC-BY-SA v4.0] license. -] else if lang == "de" [ +], +de: [ Dieses Buch wird unter den folgenden Lizenzen vertrieben: - Die in diesem Buch enthaltenen Codebeispiele und eigenständigen Cargo-Projekte stehen unter den Bedingungen sowohl der @@ -560,19 +667,22 @@ if lang == "en" [ - Die in diesem Buch enthaltenen Texte, Bilder und Diagramme stehen unter der Lizenz Creative Commons #link(legalcode)[CC-BY-SA v4.0]. -] else if lang == "uk" [ +], +uk: [ Ця книга розповсюджується за наступними ліцензіями: - Зразки коду та окремі проекти Cargo, що містяться в цій книзі, ліцензовано відповідно до умов #link(mit)[MIT License] та #link(apache)[Apache License v2.0]. - Текст, малюнки та діаграми, що містяться в цій книзі, ліцензовані відповідно до умов ліцензії Creative Commons #link(legalcode)[CC-BY-SA v4.0]. -] else if lang == "zh" [ +], +zh: [ 这本书根据以下许可证发布: - 本书中包含的代码示例和独立的Cargo项目均根据#link(mit)[MIT License]和#link(apache)[Apache License v2.0]发放许可的。 - 本书中包含的文档,图片和表格均根据#link(legalcode)[CC-BY-SA v4.0]发放许可的。 -] else { todo } +])) -if lang == "en" [ +tr(( + en: [ TL;DR: If you want to use our text or images in your work, you need to: - Give the appropriate credit (i.e.~mention this book on your slide, and provide a link to the relevant page) @@ -581,7 +691,8 @@ if lang == "en" [ licence - Indicate if you have changed the material in any way, and make any changes to our material available under the same licence -] else if lang == "de" [ +], +de: [ Kurz gesagt: Wenn Sie unsere Texte oder Bilder für Ihre Arbeit verwenden möchten, müssen Sie: - Geben Sie die entsprechende Quellenangabe an (d.~h. erwähnen Sie @@ -591,27 +702,33 @@ if lang == "en" [ - Geben Sie an, ob Sie das Material in irgendeiner Weise verändert haben, und stellen Sie Änderungen an unserem Material unter derselben Lizenz zur Verfügung. -] else if lang == "uk" [ +], +uk: [ TL;DR: Якщо ви маєте намір використовувати наш текст або зображення у своїй роботі, вам потрібно: - Надати відповідне посилання (тобто згадати цю книгу на своєму слайді та надати посилання на відповідну сторінку) - Надати посилання на ліцензію #link(legalcode)[CC-BY-SA v4.0] - Вказати, чи змінювали ви матеріал будь-яким чином, і зробити зміни в наші матеріали доступними за тією ж ліцензією -] else if lang == "zh" [ +], +zh: [ 总之:如果你想在你的工作中使用我们的文档或者图片,你需要: - 提供合适的授信 (i.e.~在你的幻灯片中提到本书,提供相关页面的连接) - 提供#link(legalcode)[CC-BY-SA v4.0]的许可证的链接 - 指出你是否改变了材料的内容,在同一个许可证下,可以对材料进行任何改变 -] else { todo } +])) } -#if lang == "en" [ +#tr(( +en: [ Also, please do let us know if you find this book useful! -] else if lang == "de" [ +], +de: [ Teilen Sie uns bitte auch mit, ob Sie dieses Buch nützlich finden! -] else if lang == "uk" [ +], +uk: [ Також, будь ласка, повідомте нам, якщо ви знайдете цю книгу корисною! -] else if lang == "zh" [ +], +zh: [ 也请告诉我这本书对你是否有帮助! -] else { todo } +])) diff --git a/typ/intro/install.typ b/typ/intro/install.typ index 73b6d03f..99930951 100644 --- a/typ/intro/install.typ +++ b/typ/intro/install.typ @@ -1,52 +1,64 @@ #import "../config.typ": * -#h1(offset: whole, - if lang == "en" [Installing the tools] - else if lang == "de" [Werkzeuge installieren] - else if lang == "ja" [ツールのインストール] - else if lang == "zh" [安装工具] - else { todo }) - -#if lang == "en" [ +#h1(offset: whole, tr(( + en: [Installing the tools], + de: [Werkzeuge installieren], + ja: [ツールのインストール], + zh: [安装工具], +))) + +#tr(( +en: [ This page contains OS-agnostic installation instructions for a few of the tools: -] else if lang == "de" [ +], +de: [ Diese Seite enthält betriebssystemunabhängige Installationsanleitungen für einige der Werkzeuge: -] else if lang == "ja" [ +], +ja: [ このページには、いくつかのツールのOSに依存しないインストール手順を掲載します。 -] else if lang == "zh" [ +], +zh: [ 这一页包含的工具安装指令与操作系统无关: -] else { todo } +])) -== #(if lang == "en" [Rust Toolchain] - else if lang == "de" [Rust-Werkzeuge] - else if lang == "ja" [Rustツールチェイン] - else if lang == "zh" [Rust 工具链] - else { todo }) +== #tr(( + en: [Rust Toolchain], + de: [Rust-Werkzeuge], + ja: [Rustツールチェイン], + zh: [Rust 工具链], +)) #let ln_rustup = link("https://rustup.rs") -#if lang == "en" [ +#tr(( +en: [ Install rustup by following the instructions at #ln_rustup. -] else if lang == "de" [ +], +de: [ Installieren Sie rustup, indem Sie die Anweisungen unter #ln_rustup. -] else if lang == "ja" [ +], +ja: [ #ln_rustup;の手順に従って、rustupをインストールします。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ *NOTE* Make sure you have a compiler version equal to or newer than `1.31`. `rustc -V` should return a date newer than the one shown below. -] else if lang == "de" [ +], +de: [ *HINWEIS* Stellen Sie sicher, dass Sie eine Compiler-Version verwenden, die mindestens `1.31` entspricht. Der Befehl `rustc -V` sollte ein Datum ausgeben, das neuer ist als das unten angegebene. -] else if lang == "ja" [ +], +ja: [ *注意* コンパイラのバージョンが`1.31`以上であることを確認して下さい。`rustc -v`は下記に示す日付より新しい日付を返すべきです。 -] else if lang == "zh" [ +], +zh: [ *注意* 确保你的编译器版本等于或者大于`1.31`版本。`rustc -V`应该返回一个比下列日期更新的日期。 -] else { todo } +])) ```text $ rustc -V @@ -54,14 +66,16 @@ rustc 1.31.1 (b6c32da9b 2018-12-18) ``` #let url_find = "https://developer.arm.com/ip-products/processors/cortex-m#c-7d3b69ce-5b17-4c9e-8f06-59b605713133" -#if lang == "en" [ +#tr(( +en: [ For bandwidth and disk usage concerns the default installation only supports native compilation. To add cross compilation support for the ARM Cortex-M architectures choose one of the following compilation targets. For the STM32F3DISCOVERY board used for the examples in this book, use the `thumbv7em-none-eabihf` target. #link(url_find)[Find the best Cortex-M for you.] -] else if lang == "de" [ +], +de: [ Aus Gründen der Bandbreite und der Festplattenauslastung unterstützt die Standardinstallation nur die native Kompilierung. Um die Unterstützung für die Cross-Kompilierung für die ARM-Cortex-M-Architekturen @@ -69,97 +83,120 @@ rustc 1.31.1 (b6c32da9b 2018-12-18) STM32F3DISCOVERY-Board, das für die Beispiele in diesem Buch verwendet wird, verwenden Sie das Ziel `thumbv7em-none-eabihf`. #link(url_find)[Finden Sie den für Sie am besten geeigneten Cortex-M.] -] else if lang == "ja" [ +], +ja: [ #todoupd("ja") バンド幅とディスク使用量に関する懸念から、デフォルトインストールではネイティブコンパイルのみをサポートします。 ARM Cortex-Mアーキテクチャのクロスコンパイラを追加するために、下記のコンパイルターゲットをインストールします。 -] else if lang == "zh" [ +], +zh: [ #todoupd("zh") 考虑到带宽和磁盘的使用量,默认的安装只支持主机环境的编译。为了添加对ARM Cortex-M架构交叉编译的支持,从下列编译目标中选择一个。对于这本书里使用的STM32F3DISCOVERY板子,使用`thumbv7em-none-eabihf`作为目标。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Cortex-M0, M0+, and M1 (ARMv6-M architecture): -] else if lang == "de" [ +], +de: [ Cortex-M0, M0+, and M1 (ARMv6-M-Architektur): -] else if lang == "zh" [ +], +zh: [ Cortex-M0, M0+, 和 M1 (ARMv6-M 架构): -] else { todo } +])) ```console rustup target add thumbv6m-none-eabi ``` -#if lang == "en" [ +#tr(( +en: [ Cortex-M3 (ARMv7-M architecture): -] else if lang == "de" [ +], +de: [ Cortex-M3 (ARMv7-M-Architektur): -] else if lang == "zh" [ +], +zh: [ Cortex-M3 (ARMv7-M 架构): -] else { todo } +])) ```console rustup target add thumbv7m-none-eabi ``` -#if lang == "en" [ +#tr(( +en: [ Cortex-M4 and M7 without hardware floating point (ARMv7E-M architecture): -] else if lang == "de" [ +], +de: [ Cortex-M4 und M7 ohne Hardware-Gleitkommafunktion (ARMv7E-M-Architektur): -] else if lang == "zh" [ +], +zh: [ 没有硬件浮点单元的Cortex-M4和M7 (ARMv7E-M架构) -] else { todo } +])) ```console rustup target add thumbv7em-none-eabi ``` -#if lang == "en" [ +#tr(( +en: [ Cortex-M4F and M7F with hardware floating point (ARMv7E-M architecture): -] else if lang == "de" [ +], +de: [ Cortex-M4F und M7F mit Hardware-Gleitkomma (ARMv7E-M-Architektur): -] else if lang == "zh" [ +], +zh: [ 具有硬件浮点单元的Cortex-M4F和M7F (ARMv7E-M架构) -] else { todo } +])) ```console rustup target add thumbv7em-none-eabihf ``` -#if lang == "en" [ +#tr(( +en: [ Cortex-M23 (ARMv8-M architecture): -] else if lang == "de" [ +], +de: [ Cortex-M23 (ARMv8-M-Architektur): -] else if lang == "zh" [ +], +zh: [ Cortex-M23 (ARMv8-M架构): -] else { todo } +])) ```console rustup target add thumbv8m.base-none-eabi ``` -#if lang == "en" [ +#tr(( +en: [ Cortex-M33 and M35P (ARMv8-M architecture): -] else if lang == "de" [ +], +de: [ Cortex-M33 und M35P (ARMv8-M-Architektur): -] else if lang == "zh" [ +], +zh: [ Cortex-M33和M35P (ARMv8-M架构): -] else { todo } +])) ```console rustup target add thumbv8m.main-none-eabi ``` -#if lang == "en" [ +#tr(( +en: [ Cortex-M33F and M35PF with hardware floating point (ARMv8-M architecture): -] else if lang == "de" [ +], +de: [ Cortex-M33F und M35PF mit Hardware-Gleitkomma (ARMv8-M-Architektur): -] else if lang == "zh" [ +], +zh: [ 具有硬件浮点单元的Cortex-M33F和M35PF (ARMv8-M架构): -] else { todo } +])) ```console rustup target add thumbv8m.main-none-eabihf @@ -174,55 +211,69 @@ rustup component add llvm-tools ``` #let ln_vs = link("https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools&rel=16") -#if lang == "en" [ +#tr(( +en: [ WINDOWS: prerequisite C++ Build Tools for Visual Studio 2019 is installed. #ln_vs -] else if lang == "de" [ +], +de: [ WINDOWS: Voraussetzung ist, dass die C++-Build-Tools für Visual Studio 2019 installiert sind. #ln_vs -] else if lang == "zh" [ +], +zh: [ WINDOWS: 需要预先安装 C++ Build Tools for Visual Studio 2019。#ln_vs -] else { todo } +])) == `cargo-generate` -#if lang == "en" [ +#tr(( +en: [ We'll use this later to generate a project from a template. -] else if lang == "de" [ +], +de: [ Wir werden dies später verwenden, um ein Projekt aus einer Vorlage zu generieren. -] else if lang == "zh" [ +], +zh: [ 我们随后将使用这个来从模板生成一个项目。 -] else { todo } +])) ```console cargo install cargo-generate ``` -#if lang == "en" [ +#tr(( +en: [ Note: on some Linux distros (e.g.~Ubuntu) you may need to install the packages `libssl-dev` and `pkg-config` prior to installing cargo-generate. -] else if lang == "de" [ +], +de: [ Hinweis: Bei einigen Linux-Distributionen (z. B. Ubuntu) müssen Sie möglicherweise vor der Installation von `cargo-generate` die Pakete `libssl-dev` und `pkg-config` installieren. -] else if lang == "zh" [ +], +zh: [ 注意:在某些Linux发行版上(e.g.~Ubuntu) 在安装cargo-generate之前,你可能需要安装`libssl-dev`和`pkg-config` -] else { todo } +])) -== #(if lang == "en" [OS-Specific Instructions] - else if lang == "de" [Betriebssystemspezifische Anweisungen] - else if lang == "zh" [特定于操作系统的指令] - else { todo }) +== #tr(( + en: [OS-Specific Instructions], + de: [Betriebssystemspezifische Anweisungen], + zh: [特定于操作系统的指令], +)) -#if lang == "en" [ +#tr(( +en: [ Now follow the instructions specific to the OS you are using: -] else if lang == "de" [ +], +de: [ Befolgen Sie nun die Anweisungen für das von Ihnen verwendete Betriebssystem: -] else if lang == "ja" [ +], +ja: [ 使用しているOSに特有の手順に従って下さい。 -] else if lang == "zh" [ +], +zh: [ 现在根据你使用的操作系统,来执行对应的指令: -] else { todo } +])) - #link()[Linux] - #link()[Windows] - #link()[macOS] diff --git a/typ/intro/install/linux.typ b/typ/intro/install/linux.typ index bf0aa9e0..f3d03aed 100644 --- a/typ/intro/install/linux.typ +++ b/typ/intro/install/linux.typ @@ -4,87 +4,109 @@ [Linux]) -#if lang == "en" [ +#tr(( +en: [ Here are the installation commands for a few Linux distributions. -] else if lang == "de" [ +], +de: [ Hier sind die Installationsbefehle für einige Linux-Distributionen. -] else if lang == "ja" [ +], +ja: [ いくつかのLinuxディストリビューションのインストールコマンドを示します。 -] else if lang == "zh" [ +], +zh: [ 这部分是在某些Linux发行版下的安装指令。 -] else { todo } +])) -= #(if lang in ("en", "ja") [Packages] - else if lang == "de" [Pakete] - else if lang == "zh" [依赖包] - else { todo }) += #tr(( + en: [Packages], + de: [Pakete], + ja: [Packages], + zh: [依赖包], +)) -#if lang == "en" [ +#tr(( +en: [ - Ubuntu 18.04 or newer / Debian stretch or newer -] else if lang == "de" [ +], +de: [ - Ubuntu 18.04 oder neuer / Debian Stretch oder neuer -] else if lang == "ja" [ +], +ja: [ - Ubuntu 18.04以上 / Debian stretch以降 -] else if lang == "zh" [ +], +zh: [ - Ubuntu 18.04 或者更新的版本 / Debian stretch 或者更新的版本 -] else { todo } +])) #quote(block: true)[ -#if lang == "en" [ +#tr(( +en: [ *NOTE* `gdb-multiarch` is the GDB command you'll use to debug your ARM Cortex-M programs -] else if lang == "de" [ +], +de: [ *HINWEIS* `gdb-multiarch` ist der GDB-Befehl, den Sie zum Debuggen Ihrer ARM-Cortex-M-Programme verwenden werden. -] else if lang == "ja" [ +], +ja: [ *注記* `gdb-multiarch`は、ARM Cortex-Mプログラムをデバッグするために使用するGDBのコマンドです。 -] else if lang == "zh" [ +], +zh: [ * 注意* `gdb-multiarch` 是你将用来调试你的ARM -] else { todo } +])) ] ```console sudo apt install gdb-multiarch openocd qemu-system-arm ``` -#if lang in ("en", "zh") [ - - Ubuntu 14.04 and 16.04 -] else if lang == "de" [ - - Ubuntu 14.04 und 16.04 -] else if lang == "ja" [ - - Ubuntu 14.04と16.04 -] else { todo } +#let _and_ = tr(( + en: [ and ], + de: [ und ], + ja: [と], +), default: [ and ]) + +- Ubuntu 14.04#_and_;16.04 #quote(block: true)[ -#if lang == "en" [ +#tr(( +en: [ *NOTE* `arm-none-eabi-gdb` is the GDB command you'll use to debug your ARM Cortex-M programs -] else if lang == "de" [ +], +de: [ *HINWEIS* `arm-none-eabi-gdb` ist der GDB-Befehl, den Sie zum Debuggen Ihrer ARM-Cortex-M-Programme verwenden werden. -] else if lang == "ja" [ +], +ja: [ *注記* `arm-none-eabi-gdb`は、ARM Cortex-Mプログラムをデバッグするために使用するGDBのコマンドです。 -] else if lang == "zh" [ +], +zh: [ *注意* `arm-none-eabi-gdb` 是你将用来调试你的ARM Cortex-M程序的GDB命令 -] else { todo } +])) ] ```console sudo apt install gdb-arm-none-eabi openocd qemu-system-arm ``` -#if lang == "en" [ +#tr(( +en: [ - Fedora 27 or newer -] else if lang == "de" [ +], +de: [ - Fedora 27 oder neuer -] else if lang == "ja" [ +], +ja: [ - Fedora 27以上 -] else if lang == "zh" [ +], +zh: [ - Fedora 27 或者更新的版本 -] else { todo } +])) ```console sudo dnf install gdb openocd qemu-system-arm @@ -93,51 +115,65 @@ sudo dnf install gdb openocd qemu-system-arm - Arch Linux #quote(block: true)[ -#if lang == "en" [ +#tr(( +en: [ *NOTE* `arm-none-eabi-gdb` is the GDB command you'll use to debug ARM Cortex-M programs -] else if lang == "de" [ +], +de: [ *HINWEIS* `arm-none-eabi-gdb` ist der GDB-Befehl, den Sie zum Debuggen von ARM-Cortex-M-Programmen verwenden. -] else if lang == "ja" [ +], +ja: [ *注記* `arm-none-eabi-gdb`は、ARM Cortex-Mプログラムをデバッグするために使用するGDBのコマンドです。 -] else if lang == "zh" [ +], +zh: [ *注意* `arm-none-eabi-gdb` 是你将用来调试你的ARM Cortex-M程序的GDB命令 -] else { todo } +])) ] ```console sudo pacman -S arm-none-eabi-gdb qemu-system-arm openocd ``` -= #(if lang == "en" [udev rules] - else if lang == "de" [udev-Regeln] - else if lang == "ja" [udevルール] - else if lang == "zh" [udev 规则] - else { todo }) += #tr(( + en: [udev rules], + de: [udev-Regeln], + ja: [udevルール], + zh: [udev 规则], +)) + -#if lang == "en" [ +#tr(( +en: [ This rule lets you use OpenOCD with the Discovery board without root privilege. -] else if lang == "de" [ +], +de: [ Diese Regel ermöglicht die Nutzung von OpenOCD mit dem Discovery Board ohne Root-Rechte. -] else if lang == "ja" [ +], +ja: [ このルールにより、ルート権限なしで、OpenOCDをDiscoveryボードに対して使えるようにします。 -] else if lang == "zh" [ +], +zh: [ 这个规则可以让你在不使用超级用户权限的情况下,使用OpenOCD和Discovery开发板。 -] else { todo } +])) #let file_stlink = `/etc/udev/rules.d/70-st-link.rules` -#if lang == "en" [ +#tr(( +en: [ Create the file #file_stlink with the contents shown below. -] else if lang == "de" [ +], +de: [ Erstellen Sie die Datei #file_stlink mit dem unten stehenden Inhalt. -] else if lang == "ja" [ +], +ja: [ #todoupd("ja") 下記の内容で、`/etc/udev/rules.d`ディレクトリにファイルを作成します。 -] else if lang == "zh" [ +], +zh: [ 生成包含下列内容的 #file_stlink 文件 -] else { todo } +])) ```text # STM32F3DISCOVERY rev A/B - ST-LINK/V2 @@ -147,52 +183,67 @@ ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3748", TAG+="uaccess" ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374b", TAG+="uaccess" ``` -#if lang == "en" [ +#tr(( +en: [ Then reload all the udev rules with: -] else if lang == "de" [ +], +de: [ Laden Sie anschließend alle udev-Regeln neu mit: -] else if lang == "ja" [ +], +ja: [ その後、全てのudevルールをリロードします。 -] else if lang == "zh" [ +], +zh: [ 然后重新加载所有的udev规则 -] else { todo } +])) ```console sudo udevadm control --reload-rules ``` -#if lang == "en" [ +#tr(( +en: [ If you had the board plugged to your laptop, unplug it and then plug it again. -] else if lang == "de" [ +], +de: [ Wenn Sie die Platine an Ihren Laptop angeschlossen hatten, ziehen Sie den Stecker heraus und schließen Sie ihn dann erneut an. -] else if lang == "ja" [ +], +ja: [ 既にボードをノートPCに接続している場合、一度抜いてから、もう一度接続します。 -] else if lang == "zh" [ +], +zh: [ 如果你已经把开发板插入到笔记本中了,请拔下它然后再插上它。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ You can check the permissions by running this command: -] else if lang == "de" [ +], +de: [ Sie können die Berechtigungen überprüfen, indem Sie diesen Befehl ausführen: -] else if lang == "ja" [ +], +ja: [ これらのコマンド実行することで、パーミッションを確認できます。 -] else if lang == "zh" [ +], +zh: [ 你可以通过运行这个命令检查权限: -] else { todo } +])) ```console lsusb ``` -#if lang == "en" [ +#tr(( +en: [ Which should show something like -] else if lang == "de" [ +], +de: [ Was so etwas wie -] else if lang == "zh" [ +], +zh: [ 终端可能有如下显示 -] else { todo } +])) ```text (..) @@ -203,17 +254,20 @@ Bus 001 Device 018: ID 0483:374b STMicroelectronics ST-LINK/V2.1 anzeigen sollte. ] -#if lang == "en" [ +#tr(( +en: [ Take note of the bus and device numbers. Use those numbers to create a path like `/dev/bus/usb//`. Then use this path like so: -] else if lang == "de" [ +], +de: [ Notieren Sie sich die Bus- und Gerätenummern. Verwenden Sie diese Nummern, um einen Pfad wie `/dev/bus/usb//` zu bilden. Verwenden Sie diesen Pfad anschließend wie folgt: -] else if lang == "zh" [ +], +zh: [ 记住bus和device号,使用这些数字组合成一个像是 `/dev/bus/usb//` 这样的路径。然后像这样使用这个路径: -] else { todo } +])) ```console ls -l /dev/bus/usb/001/018 @@ -232,28 +286,36 @@ user::rw- user:you:rw- ``` -#if lang == "en" [ +#tr(( +en: [ The `+` appended to permissions indicates the existence of an extended permission. The `getfacl` command tells the user `you` can make use of this device. -] else if lang == "de" [ +], +de: [ Das angehängte „+" bei den Berechtigungen weist auf eine erweiterte Berechtigung hin. Der Befehl „getfacl" teilt dem Benutzer mit, dass er dieses Gerät verwenden darf. -] else if lang == "ja" [ +], +ja: [ #todoupd("ja") パーミッションに追加された`+`は、パーミッションが拡張されたことを意味しています。 -] else if lang == "zh" [ +], +zh: [ 权限后的 `+` 指出存在一个扩展权限。`getfacl` 命令显示,`user`也就是`你`,可以使用这个设备。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Now, go to the #link()[next section]. -] else if lang == "de" [ +], +de: [ Fahren Sie nun mit dem #link()[nächsten Abschnitt] fort. -] else if lang == "ja" [ +], +ja: [ それでは、#link()[次のセクション]に進んで下さい。 -] else if lang == "zh" [ +], +zh: [ 现在,去往#link()[下个章节]. -] else { todo } +])) diff --git a/typ/intro/install/macos.typ b/typ/intro/install/macos.typ index 770a9a14..ded262df 100644 --- a/typ/intro/install/macos.typ +++ b/typ/intro/install/macos.typ @@ -7,21 +7,26 @@ #let brew_ln = link("http://brew.sh/")[Homebrew] #let ports_ln = link("https://www.macports.org/")[MacPorts] -#if lang == "en" [ +#tr(( +en: [ All the tools can be installed using #brew_ln or #ports_ln: -] else if lang == "de" [ +], +de: [ Alle Werkzeuge können mit #brew_ln oder #ports_ln installiert werden: -] else if lang == "ja" [ +], +ja: [ #todoupd("ja") 全てのツールは、#brew_ln;を使ってインストールできます。 -] else if lang == "zh" [ +], +zh: [ 所有的工具都可以使用#brew_ln;或者#ports_ln;来安装: -] else { todo } +])) -= #(if lang == "en" [Install tools with #brew_ln] - else if lang == "de" [Werkzeuge mit #brew_ln installieren] - else if lang == "zh" [使用#brew_ln;安装工具] - else { todo }) += #tr(( + en: [Install tools with #brew_ln], + de: [Werkzeuge mit #brew_ln installieren], + zh: [使用#brew_ln;安装工具], +)) ```text $ # GDB @@ -35,25 +40,29 @@ $ brew install qemu ``` #quote(block: true)[ -#if lang == "en" [ +#tr(( +en: [ *NOTE* If OpenOCD crashes you may need to install the latest version using: -] else if lang == "de" [ +], +de: [ *HINWEIS* Falls OpenOCD abstürzt, müssen Sie möglicherweise die neueste Version installieren; verwenden Sie dazu: -] else if lang == "zh" [ +], +zh: [ *注意* 如果OpenOCD崩溃了,你可能需要用以下方法安装最新版本: -] else { todo } +])) ] ```text $ brew install --HEAD openocd ``` -= #(if lang == "en" [Install tools with #ports_ln] - else if lang == "de" [Werkzeuge mit #ports_ln installieren] - else if lang == "zh" [使用#ports_ln;安装工具] - else { todo }) += #tr(( + en: [Install tools with #ports_ln], + de: [Werkzeuge mit #ports_ln installieren], + zh: [使用#ports_ln;安装工具], +)) ```text $ # GDB @@ -66,12 +75,16 @@ $ # QEMU $ sudo port install qemu ``` -#if lang == "en" [ +#tr(( +en: [ That's all! Go to the #link()[next section]. -] else if lang == "de" [ +], +de: [ Das war's! Gehen Sie zum #link()[nächsten Abschnitt]. -] else if lang == "ja" [ +], +ja: [ 以上です!#link()[次のセクション]に進んで下さい。 -] else if lang == "zh" [ +], +zh: [ 这是全部内容,请转入#link()[下个章节]. -] else { todo } +])) diff --git a/typ/intro/install/verify.typ b/typ/intro/install/verify.typ index 5151b30d..cf6bb7cc 100644 --- a/typ/intro/install/verify.typ +++ b/typ/intro/install/verify.typ @@ -1,99 +1,122 @@ #import "../../config.typ": * -#h1(offset: whole*2, - if lang == "en" [Verify Installation] - else if lang == "de" [Die Installation überprüfen] - else if lang == "ja" [インストールの確認] - else if lang == "zh" [安装验证] - else { todo }) +#h1(offset: whole*2, tr(( + en: [Verify Installation], + de: [Die Installation überprüfen], + ja: [インストールの確認], + zh: [安装验证], +))) -#if lang == "en" [ +#tr(( +en: [ In this section we check that some of the required tools / drivers have been correctly installed and configured. -] else if lang == "de" [ +], +de: [ In diesem Abschnitt überprüfen wir, ob einige der erforderlichen Werkzeuge bzw. Treiber korrekt installiert und konfiguriert wurden. -] else if lang == "ja" [ +], +ja: [ このセクションでは、必要となるツールとドライバが正しくインストールされ、設定されていることを確認します。 -] else if lang == "zh" [ +], +zh: [ 在这个章节中我们将检查工具和驱动是否已经被正确地安装和配置了。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Connect your laptop / PC to the discovery board using a Mini-USB USB cable. The discovery board has two USB connectors; use the one labeled "USB ST-LINK" that sits on the center of the edge of the board. -] else if lang == "de" [ +], +de: [ Verbinden Sie Ihren Laptop oder PC über ein Mini-USB-Kabel mit dem Discovery-Board. Das Discovery-Board verfügt über zwei USB-Anschlüsse; verwenden Sie den mit „USB ST-LINK" beschrifteten Anschluss, der sich mittig an der Platinenkante befindet. -] else if lang == "ja" [ +], +ja: [ マイクロUSBケーブルを使って、ノートPC / PCをdiscoveryボードに接続して下さい。 discoveryボードは2つのUSBコネクタを搭載しています。 ボード端の中央にある"USB ST-LINK"とラベルが付いたものを使用して下さい。 -] else if lang == "zh" [ +], +zh: [ 使用一个micro USB线缆将你的笔记本/个人电脑连接到discovery开发板上。discovery开发板有两个USB连接器;使用标记着"USB ST-LINK"的那个,它位于开发板边缘的中间位置。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Also check that the ST-LINK header is populated. See the picture below; the ST-LINK header is highlighted. -] else if lang == "de" [ +], +de: [ Überprüfen Sie außerdem, ob die ST-LINK-Stiftleiste bestückt ist. Siehe dazu die Abbildung unten; die ST-LINK-Stiftleiste ist dort hervorgehoben. -] else if lang == "ja" [ +], +ja: [ ST-LINKヘッダが装着されていることも確認します。下の写真の赤丸で囲った部分がST-LINKヘッダです。 -] else if lang == "zh" [ +], +zh: [ 也要检查下ST-LINK的短路帽是否被安装了。看下面的图;ST-LINK短路帽用红色圈起来了。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Now run the following command: -] else if lang == "de" [ +], +de: [ Führen Sie nun den folgenden Befehl aus: -] else if lang == "ja" [ +], +ja: [ それでは、次のコマンドを実行して下さい。 -] else if lang == "zh" [ +], +zh: [ 现在运行下面的命令: -] else { todo } +])) ```console openocd -f interface/stlink.cfg -f target/stm32f3x.cfg ``` #quote(block: true)[ -#if lang == "en" [ +#tr(( +en: [ *NOTE*: Old versions of openocd, including the 0.10.0 release from 2017, do not contain the new (and preferable) `interface/stlink.cfg` file; instead you may need to use `interface/stlink-v2.cfg` or `interface/stlink-v2-1.cfg`. -] else if lang == "de" [ +], +de: [ *HINWEIS*: Ältere Versionen von OpenOCD, einschließlich der Version 0.10.0 aus dem Jahr 2017, enthalten nicht die neue (und zu bevorzugende) Datei `interface/stlink.cfg`; stattdessen müssen Sie möglicherweise `interface/stlink-v2.cfg` oder `interface/stlink-v2-1.cfg` verwenden. -] else if lang == "zh" [ +], +zh: [ *注意*: 旧版的openocd, 包括从2017发布的0.10.0, 不包含新的(且更适合的)`interface/stlink.cfg`文件; 你需要使用`interface/stlink-v2.cfg` 或者 `interface/stlink-v2-1.cfg`。 -] else { todo } +])) ] -#if lang == "en" [ +#tr(( +en: [ You should get the following output and the program should block the console: -] else if lang == "de" [ +], +de: [ Sie sollten die folgende Ausgabe erhalten und das Programm sollte die Konsole blockieren: -] else if lang == "ja" [ +], +ja: [ 次の出力が得られ、プログラムはコンソールをブロックするはずです。 -] else if lang == "zh" [ +], +zh: [ 你应该看到了下面的输出,且程序应该阻塞住了控制台: -] else { todo } +])) ```text Open On-Chip Debugger 0.10.0 @@ -114,33 +137,41 @@ Info : Target voltage: 2.919881 Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints ``` -#if lang == "en" [ +#tr(( +en: [ The contents may not match exactly but you should get the last line about breakpoints and watchpoints. If you got it then terminate the OpenOCD process and move to the #link()[next section]. -] else if lang == "de" [ +], +de: [ Der Inhalt stimmt möglicherweise nicht exakt überein, aber die letzte Zeile bezüglich Breakpoints und Watchpoints sollten Sie sehen. Wenn dies der Fall ist, beenden Sie den OpenOCD-Prozess und fahren Sie mit dem #link()[nächsten Abschnitt] fort. -] else if lang == "ja" [ +], +ja: [ 確認作業とは直接関係しませんが、ブレイクポイントとウォッチポイントに関する最後の行を取得したはずです。 取得できた場合、OpenOCDプロセスを停止し、#link()[次のセクション]へ進んで下さい。 -] else if lang == "zh" [ +], +zh: [ 内容可能并不是一模一样,但是在最后一行,你应该看到了breakpoints和watchpoints,如果你看到了,那就终止OpenOCD进程然后进入#link()[下个章节] -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ If you didn't get the "breakpoints" line then try one of the following commands. -] else if lang == "de" [ +], +de: [ Falls du die Zeile „breakpoints" nicht erhalten hast, versuche einen der folgenden Befehle. -] else if lang == "ja" [ +], +ja: [ "breakpoints"の行が取得できなかった場合、次のコマンドを試してく下さい。 -] else if lang == "zh" [ +], +zh: [ 如果你没看到"breakpoints"这行,尝试下下列命令中的某一个命令。 -] else { todo } +])) ```console openocd -f interface/stlink-v2.cfg -f target/stm32f3x.cfg @@ -150,54 +181,66 @@ openocd -f interface/stlink-v2.cfg -f target/stm32f3x.cfg openocd -f interface/stlink-v2-1.cfg -f target/stm32f3x.cfg ``` -#if lang == "en" [ +#tr(( +en: [ If one of those commands works it means you got an old hardware revision of the discovery board. That won't be a problem but commit that fact to memory as you'll need to configure things a bit differently later on. You can move to the #link()[next section]. -] else if lang == "de" [ +], +de: [ Wenn einer dieser Befehle funktioniert, bedeutet das, dass Sie eine ältere Hardware-Revision des Discovery-Boards besitzen. Das stellt kein Problem dar, aber merken Sie sich diesen Umstand, da Sie die Konfiguration später etwas anders vornehmen müssen. Sie können zum #link()[nächsten Abschnitt] übergehen. -] else if lang == "ja" [ +], +ja: [ このコマンドが機能した場合、古いハードウェアリビジョンのdiscoveryボードを入手したことを意味します。 これは問題になりませんが、後で少し設定を変える必要があるので、そのことを覚えておいて下さい。 #link()[次のセクション]に進むことができます。 -] else if lang == "zh" [ +], +zh: [ 如果这些命令的某条起作用了,那意味着你使用的discovery开发板是一个旧的版本。那也不成问题,但是你要记住这件事,因为随后你的配置可能有点不同。你可以移到#link()[下个章节]了。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ If none of the commands work as a normal user then try to run them with root permission (e.g.~`sudo openocd ..`). If the commands do work with root permission then check that the #link()[udev rules] have been correctly set. -] else if lang == "de" [ +], +de: [ Wenn keiner der Befehle als normaler Benutzer funktioniert, versuchen Sie, sie mit Root-Rechten auszuführen (z. B. `sudo openocd ..`). Sollten die Befehle mit Root-Rechten funktionieren, überprüfen Sie, ob die #link()[udev-Regeln] korrekt eingerichtet wurden. -] else if lang == "ja" [ +], +ja: [ どちらのコマンドも通常ユーザとしてうまく動かなかった場合、rootパーミッションで実行してみて下さい(例えば、`sudo openocd ..`)。 コマンドがrootパーミッションで機能した場合、#link()[udevルール]が正しく設定されているか確認して下さい。 -] else if lang == "zh" [ +], +zh: [ 如果这些命令在普通用户模式下都没用,尝试下使用root模式运行它们(e.g.~`sudo openocd ..`)。如果命令在root模式下起作用,需要检查下#link()[udev rules]是否被正确地设置了。 -] else { todo } +])) #let url_issues = "https://github.com/rust-embedded/book/issues" -#if lang == "en" [ +#tr(( +en: [ If you have reached this point and OpenOCD is not working please open #link(url_issues)[an issue] and we'll help you out! -] else if lang == "de" [ +], +de: [ Wenn Sie an diesem Punkt angelangt sind und OpenOCD nicht funktioniert, eröffnen Sie bitte #link(url_issues)[eine Problemmeldung], und wir helfen Ihnen weiter! -] else if lang == "ja" [ +], +ja: [ ここまで到着していまい、OpenOCDが動いていないならば、#link(url_issues)[issue]を作って下さい。私たちがあなたを支援します。 -] else if lang == "zh" [ +], +zh: [ 如果这些都试了,OpenOCD还不工作,请打开一个#link(url_issues)[issue],我们将帮助你! -] else { todo } +])) diff --git a/typ/intro/install/windows.typ b/typ/intro/install/windows.typ index 234c70bb..73f062c3 100644 --- a/typ/intro/install/windows.typ +++ b/typ/intro/install/windows.typ @@ -7,27 +7,31 @@ = `arm-none-eabi-gdb` #let url_arm_gnu = "https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads" -#if lang == "en" [ +#tr(( +en: [ ARM provides `.exe` installers for Windows. Grab one from #link(url_arm_gnu)[here], and follow the instructions. Just before the installation process finishes tick/select the "Add path to environment variable" option. Then verify that the tools are in your `%PATH%`: -] else if lang == "de" [ +], +de: [ ARM stellt `.exe`-Installationsprogramme für Windows bereit. Laden Sie eines von #link(url_arm_gnu)[hier] herunter und befolgen Sie die Anweisungen. Aktivieren Sie kurz vor Abschluss des Installationsvorgangs die Option „Add path to environment variable". Überprüfen Sie anschließend, ob die Werkzeuge in Ihrem `%PATH%` enthalten sind: -] else if lang == "ja" [ +], +ja: [ ARMはWindows向けに`.exe`インストーラを提供しています。#link(url_arm_gnu)[here]から1つを入手して、手順に従って下さい。 インストールプロセスが終了する直前に"環境変数にパスを追加"オプションを選択します。 その後、ツールが`%PATH%`にあることを確認します。 -] else if lang == "zh" [ +], +zh: [ ARM提供了用于Windows的`.exe`安装程序。从#link(url_arm_gnu)[这里]获取, 然后按照说明操作。 在完成安装之前,勾选/选择"Add path to environment variable"选项。 然后验证环境变量是否添加到 `%PATH%`中: -] else { todo } +])) ```text $ arm-none-eabi-gdb -v @@ -39,14 +43,16 @@ GNU gdb (GNU Tools for Arm Embedded Processors 7-2018-q2-update) 8.1.0.20180315- #let url_openocd = "https://xpack.github.io/openocd/" #let bin_path = `C:\Users\USERNAME\AppData\Roaming\xPacks\@xpack-dev-tools\openocd\0.10.0-13.1\.content\bin\` -#if lang == "en" [ +#tr(( +en: [ There's no official binary release of OpenOCD for Windows but if you're not in the mood to compile it yourself, the xPack project provides a binary distribution, #link(url_openocd)[here]. Follow the provided installation instructions. Then update your `%PATH%` environment variable to include the path where the binaries were installed. (#bin_path, if you've been using the easy install) -] else if lang == "de" [ +], +de: [ Es gibt kein offizielles Binär-Release von OpenOCD für Windows, aber wenn Sie es nicht selbst kompilieren möchten, stellt das xPack-Projekt eine Binärdistribution bereit -- zu finden @@ -55,18 +61,22 @@ GNU gdb (GNU Tools for Arm Embedded Processors 7-2018-q2-update) 8.1.0.20180315- Ihre Umgebungsvariable `%PATH%`, indem Sie den Pfad hinzufügen, unter dem die Binärdateien installiert wurden (z. B. #bin_path, falls Sie die einfache Installation verwendet haben). -] else if lang == "zh" [ +], +zh: [ OpenOCD 官方没有提供Windows的二进制版本, 若你没有心情去折腾编译,#link(url_openocd)[这里]有xPack提供的一个二进制发布.。按照说明进行安装。然后更新你的`%PATH%` 环境变量,将安装目录包括进去。 (#bin_path, 如果使用简易安装) -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Verify that OpenOCD is in your `%PATH%` with: -] else if lang == "de" [ +], +de: [ Überprüfen Sie mit folgendem Befehl, ob OpenOCD in Ihrem `%PATH%` enthalten ist: -] else if lang == "zh" [ +], +zh: [ 使用以下命令验证OpenOCD是否在你的`%PATH%`环境变量中 : -] else { todo } +])) ```text $ openocd -v @@ -77,45 +87,60 @@ Open On-Chip Debugger 0.10.0 = QEMU #let url_qemu = "https://www.qemu.org/download/#windows" -#if lang == "en" [ +#tr(( +en: [ Grab QEMU from #link(url_qemu)[the official website]. -] else if lang == "de" [ +], +de: [ Lade QEMU von #link(url_qemu)[der offiziellen Website] herunter. -] else if lang == "ja" [ +], +ja: [ #link(url_qemu)[QEMU公式サイト]からQEMUを入手します。 -] else if lang == "zh" [ +], +zh: [ 从#link(url_qemu)[官网]获取QEMU。 -] else { todo } +])) -= #(if lang in ("en", "de", "zh") [ST-LINK USB driver] - else if lang == "ja" [ST-LINK USBドライバ] - else { todo }) += #tr(( + en: [ST-LINK USB driver], + de: [ST-LINK USB driver], + ja: [ST-LINK USBドライバ], + zh: [ST-LINK USB driver], +)) #let url_stlink = "http://www.st.com/en/embedded-software/stsw-link009.html" -#if lang == "en" [ +#tr(( +en: [ You'll also need to install #link(url_stlink)[this USB driver] or OpenOCD won't work. Follow the installer instructions and make sure you install the right version (32-bit or 64-bit) of the driver. -] else if lang == "de" [ +], +de: [ Sie müssen außerdem #link(url_stlink)[diesen USB-Treiber] installieren, da OpenOCD sonst nicht funktioniert. Befolgen Sie die Anweisungen des Installationsprogramms und stellen Sie sicher, dass Sie die richtige Version des Treibers (32-Bit oder 64-Bit) installieren. -] else if lang == "ja" [ +], +ja: [ #link(url_stlink)[USBドライバ]もインストールする必要があります。そうでなければOpenOCDは動きません。インストーラの手順に従って下さい。 そして、正しいドライバのバージョン(32ビットか64ビット)をインストールすることを確認して下さい。 -] else if lang == "zh" [ +], +zh: [ 你还需要安装这个 #link(url_stlink)[USB驱动] 否则OpenOCD将无法工作。按照安装程序的说明,确保你安装了正确版本(32位或64位)的驱动程序。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ That's all! Go to the #link()[next section]. -] else if lang == "de" [ +], +de: [ Das war's! Gehen Sie zum #link()[nächsten Abschnitt]. -] else if lang == "ja" [ +], +ja: [ 以上です!#link()[次のセクション]に進んで下さい。 -] else if lang == "zh" [ +], +zh: [ 以上是全部内容!转到 #link()[下个章节]。 -] else { todo } +])) diff --git a/typ/intro/no-std.typ b/typ/intro/no-std.typ index 0c1c581e..5c2f6e3c 100644 --- a/typ/intro/no-std.typ +++ b/typ/intro/no-std.typ @@ -1,15 +1,16 @@ #import "../config.typ": * -#h1(offset: whole, - if lang == "en" [A `no_std` Rust Environment] - else if lang == "de" [Eine `no_std`-Rust-Umgebung] - else if lang == "ja" [Rustの`no_std`環境] - else if lang == "zh" [一个 `no_std` Rust环境] - else { todo }) +#h1(offset: whole, tr(( + en: [A `no_std` Rust Environment], + de: [Eine `no_std`-Rust-Umgebung], + ja: [Rustの`no_std`環境], + zh: [一个 `no_std` Rust环境], +))) #let ln_st72325 = link("https://www.st.com/resource/en/datasheet/st72325j6.pdf")[ST72325xx] #let ln_rpi = link("https://en.wikipedia.org/wiki/Raspberry_Pi#Specifications")[Modell B 3+] -#if lang == "en" [ +#tr(( +en: [ The term Embedded Programming is used for a wide range of different classes of programming. Ranging from programming 8-Bit MCUs (like the #ln_st72325 with just a few KB of RAM and ROM, @@ -17,7 +18,8 @@ which has a 32/64-bit 4-core Cortex-A53 \@ 1.4 GHz and 1GB of RAM. Different restrictions/limitations will apply when writing code depending on what kind of target and use case you have. -] else if lang == "de" [ +], +de: [ Der Begriff „Embedded Programming" (Programmierung eingebetteter Systeme) umfasst ein breites Spektrum unterschiedlichster Programmierbereiche. Das Spektrum reicht von der Programmierung von @@ -28,36 +30,44 @@ und 1 GB RAM verfügt. Je nach Zielsystem und Anwendungsfall gelten bei der Programmierung unterschiedliche Einschränkungen und Rahmenbedingungen. -] else if lang == "ja" [ +], +ja: [ 組込みプログラミングという用語は、様々な分野のプログラミングに使用されます。 たった数キロバイトのRAMかROMが付随する8ビットMCU (例えば、#ln_st72325)から、32/64ビットの4コア Cortex-A53 \@ 1.4GHzと1GBのRAMが搭載されたRaspberry Pi(#ln_rpi)のようなシステムまで、幅広いです。 どのような種類の目的とユースケースがあるか、によって、コードを書くときに異なる制限/限界が課されます。 -] else if lang == "zh" [ +], +zh: [ 嵌入式编程这个词被广泛用于许多不同的编程场景中。小到RAM和ROM只有KB的8位机(像是#ln_st72325),大到一个具有32/64位4核Cortex-A53和1GB RAM的系统,比如树莓派(#ln_rpi)。当编写代码时,取决于你的目标环境和用例,将会有不同的限制和局限。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ There are two general Embedded Programming classifications: -] else if lang == "de" [ +], +de: [ Es gibt zwei grundlegende Kategorien der Programmierung eingebetteter Systeme: -] else if lang == "ja" [ +], +ja: [ 2つの一般的な組込みプログラミングの分類があります。 -] else if lang == "zh" [ +], +zh: [ 通常嵌入式编程有两类: -] else { todo } +])) -= #(if lang == "en" [Hosted Environments] - else if lang == "de" [Hosted Environments] - else if lang == "ja" [ホストされた環境] - else if lang == "zh" [主机环境下] - else { todo }) += #tr(( + en: [Hosted Environments], + de: [Hosted Environments], + ja: [ホストされた環境], + zh: [主机环境下], +)) #let ln_posix = "https://en.wikipedia.org/wiki/POSIX" -#if lang == "en" [ +#tr(( +en: [ These kinds of environments are close to a normal PC environment. What this means is that you are provided with a System Interface #link(ln_posix)[E.G. POSIX] that provides @@ -67,7 +77,8 @@ functionality. You may also have some sort of sysroot and restrictions on RAM/ROM-usage, and perhaps some special HW or I/Os. Overall it feels like coding on a special-purpose PC environment. -] else if lang == "de" [ +], +de: [ Derartige Umgebungen ähneln einer herkömmlichen PC-Umgebung. Das bedeutet, dass Ihnen eine Systemschnittstelle (z. B. #link(ln_posix)[POSIX]) zur Verfügung @@ -81,24 +92,28 @@ Ein-/Ausgabeschnittstellen (I/Os) können vorhanden sein. Insgesamt vermittelt die Programmierung den Eindruck, in einer auf einen speziellen Einsatzzweck ausgerichteten PC-Umgebung zu arbeiten. -] else if lang == "ja" [ +], +ja: [ この分類の環境は、普通のPCの環境に近いです。 これの意味するところは、#link("https://en.wikipedia.org/wiki/POSIX")[POSIX]のようなシステムインタフェースが提供されている、ということです。システムインタフェースは、ファイルシステムやネットワーク、メモリ管理、スレッドといった多様なシステムとやりとりするための基本要素を提供します。 通常、標準ライブラリは、その機能を実装するために、これらの基本要素に依存します。 また、sysrootや、RAM/ROM利用の制限、そしておそらく特別なハードウェアやIOがあるかもしれません。 全体としては、特殊な用途のPC環境でコーディングをするようなものです。 -] else if lang == "zh" [ +], +zh: [ 这类环境与一个常见的PC环境类似。意味着向你提供了一个系统接口#link(ln_posix)[比如 POSIX],使你能和不同的系统进行交互,比如文件系统,网络,内存管理,进程,等等。标准库相应地依赖这些接口去实现了它们的功能。可能有某种sysroot并限制了对RAM/ROM的使用,可能还有一些特别的硬件或者I/O。总之感觉像是在专用的PC环境上编程一样。 -] else { todo } +])) -= #(if lang == "en" [Bare Metal Environments] - else if lang == "de" [Rein physische Umgebung] - else if lang == "ja" [ベアメタル環境] - else if lang == "zh" [裸机环境下] - else { todo }) += #tr(( + en: [Bare Metal Environments], + de: [Rein physische Umgebung], + ja: [ベアメタル環境], + zh: [裸机环境下], +)) #let ln_core = link("https://doc.rust-lang.org/core/")[libcore] -#if lang == "en" [ +#tr(( +en: [ In a bare metal environment no code has been loaded before your program. Without the software provided by an OS we can not load the standard library. Instead the program, along with the crates it uses, can only @@ -109,7 +124,8 @@ these things is a memory allocator for dynamic memory allocation. If you require this or any other functionalities there are often crates which provide these. -] else if lang == "de" [ +], +de: [ In einer rein physischen Umgebung wird vor Ihrem Programm kein Code geladen. Ohne die vom Betriebssystem bereitgestellte Software kann die Standardbibliothek nicht geladen werden. Stattdessen kann das Programm @@ -121,7 +137,8 @@ immer erwünscht sind. Eine dieser Funktionen ist ein Speicherallokator für die dynamische Speicherverwaltung. Falls Sie diese oder andere Funktionalitäten benötigen, stehen Ihnen häufig entsprechende Crates zur Verfügung. -] else if lang == "ja" [ +], +ja: [ #todoupd("ja") ベアメタル環境では、高機能なOSが動作していて、私たちのコードをホスティングしてくれる、ということはありません。 これは、基本要素がないことを意味しており、それ故に、デフォルトでは標準ライブラリもありません。 @@ -129,18 +146,21 @@ no_stdなコードからは、Rustの#link("https://doc.rust-lang.org/std/")[libstd]とメモリの動的確保が使えません。 しかしながら、no_stdなコードでも#ln_core;を使うことができます。libcoreは、ほんの数種類のシンボルを提供することで、いかなる環境でも容易に利用することができます (詳細は、#ln_core;を参照して下さい)。 -] else if lang == "zh" [ +], +zh: [ 在一个裸机环境中,程序被加载前,环境中不存在代码。没有系统提供的软件,我们不能加载标准库。相反地,程序和它使用的crates只能使用硬件(裸机)去运行。使用`no-std`可以防止rust读取标准库。标准库中与平台无关的部分在#link("https://doc.rust-lang.org/core/")[libcore]中。libcore剔除了那些在一个嵌入式环境中非必要的东西。比如用于动态分配的内存分配器。如果你需要这些或者其它的某些功能,通常会有提供这些功能的crates。 -] else { todo } +])) -== #(if lang == "en" [The libstd Runtime] - else if lang == "de" [Die libstd-Laufzeitumgebung] - else if lang == "ja" [libstdランタイム] - else if lang == "zh" [libstd运行时] - else { todo }) +== #tr(( + en: [The libstd Runtime], + de: [Die libstd-Laufzeitumgebung], + ja: [libstdランタイム], + zh: [libstd运行时], +)) #let ln_std = link("https://doc.rust-lang.org/std/")[libstd] -#if lang == "en" [ +#tr(( +en: [ As mentioned before using #ln_std requires some sort of system integration, but this is not only because @@ -150,7 +170,8 @@ overflow protection, processing command line arguments, and spawning the main thread before a program's main function is invoked. This runtime also won't be available in a `no_std` environment. -] else if lang == "de" [ +], +de: [ Wie bereits erwähnt, erfordert die Verwendung von #ln_std eine gewisse Systemintegration; dies liegt jedoch nicht nur daran, dass @@ -162,22 +183,26 @@ sowie das Starten des Haupt-Threads, bevor die `main`-Funktion des Programms aufgerufen wird. In einer `no_std`-Umgebung steht diese Laufzeitumgebung nicht zur Verfügung. -] else if lang == "ja" [ +], +ja: [ 上述の通り、#ln_std;の利用には、いくらかのシステムインテグレーションが必要です。しかし、これは#ln_std;がOSの抽象にアクセスするための共通の方法を提供しているだけでなく、ランタイムも提供しているためです。 ランタイムは、とりわけ、スタックオーバーフロープロテクションの準備、コマンドライン引数の処理、メインスレッドの生成、をプログラムのメイン関数が呼び出される前に処理します。 このランタイムも、`no_std`環境では利用できません。 -] else if lang == "zh" [ +], +zh: [ 就像之前提到的,使用#ln_std;需要一些系统集成,这不仅仅是因为#ln_std;使用了一个公共的方法访问操作系统,它也提供了一个运行时环境。这个运行时环境,负责设置堆栈溢出保护,处理命令行参数,并在一个程序的主函数被激活前启动一个主线程。在一个`no_std`环境中,这个运行时环境也是不可用的。 -] else { todo } +])) -= #(if lang == "en" [Summary] - else if lang == "de" [Zusammenfassung] - else if lang == "ja" [まとめ] - else if lang == "zh" [总结] - else { todo }) += #tr(( + en: [Summary], + de: [Zusammenfassung], + ja: [まとめ], + zh: [总结], +)) #let ln_core = link("https://doc.rust-lang.org/core/")[libcore] -#if lang == "en" [ +#tr(( +en: [ `#![no_std]` is a crate-level attribute that indicates that the crate will link to the core-crate instead of the std-crate. The #ln_core crate in turn is a @@ -189,7 +214,8 @@ integration. Because of these properties no_std and #ln_core code can be used for any kind of bootstrapping (stage 0) code like bootloaders, firmware or kernels. -] else if lang == "de" [ +], +de: [ `#![no_std]` ist ein Attribut auf Crate-Ebene, das angibt, dass das Crate gegen das `core`-Crate anstatt gegen das `std`-Crate gelinkt wird. Das #ln_core;-Crate wiederum ist @@ -202,23 +228,26 @@ dieser Eigenschaften eignen sich `no_std`- und #ln_core;-Code für jegliche Art von Bootstrapping-Code (Stufe 0), wie etwa Bootloader, Firmware oder Kernel. -] else if lang == "ja" [ +], +ja: [ `#![no_std]`は、クレートレベルの属性で、そのクレートがstdクレートの代わりにcoreクレートとリンクすることを意味します。 #ln_core;クレートは、プラットフォームに依存しないstdクレートのサブセットです。libcoreクレートは、プログラムが動作するシステムについて前提を置きません。 libcoreクレートは、浮動小数点、文字列やスライスといった言語の基本要素となるAPIと、アトミック操作やSIMD命令といったプロセッサの機能を公開するAPIとを、提供します。一方、プラットフォームインテグレーションを伴うようなAPIは欠如しています。 これらの特性のため、no\_stdと#ln_core;のコードは、ブートローダー、ファームウェア、カーネルといったあらゆるブートストラップ (ステージ0)のコードにも利用できます。 -] else if lang == "zh" [ +], +zh: [ `#![no_std]`是一个crate层级的属性,它说明crate将连接至core-crate而不是std-crate。#ln_core crate是std crate的一个的子集,其与平台无关,它对程序将要运行的系统没有做要求。比如,它提供了像是floats,strings和切片的APIs,暴露了像是与原子操作和SIMD指令相关的处理器功能的APIs。然而,它缺少涉及到平台集成的那些APIs。由于这些特性,no_std和#ln_core;代码可以用于任何引导程序(stage 0)像是bootloaders,固件或者内核。 -] else { todo } +])) -== #(if lang == "en" [Overview] - else if lang == "de" [Überblick] - else if lang == "ja" [概略] - else if lang == "zh" [概述] - else { todo }) +== #tr(( + en: [Overview], + de: [Überblick], + ja: [概略], + zh: [概述], +)) #figure( kind: table, @@ -226,93 +255,115 @@ columns: (1fr, 11.11%, 6.94%), align: center, table.header( - if lang == "en" [feature] - else if lang == "de" [Eigenschaft] - else if lang == "ja" [機能] - else if lang == "zh" [特性] - else { todo }, + tr(( + en: [feature], + de: [Eigenschaft], + ja: [機能], + zh: [特性], + )), [no_std], [std], ), - if lang == "en" [heap (dynamic memory)] - else if lang == "de" [heap (dynamischer Speicher)] - else if lang == "ja" [ヒープ (動的メモリ)] - else if lang == "zh" [堆 (dynamic memory)] - else { todo }, + tr(( + en: [heap (dynamic memory)], + de: [heap (dynamischer Speicher)], + ja: [ヒープ (動的メモリ)], + zh: [堆 (dynamic memory)], + )), [\*], [✓], - if lang == "en" [collections (Vec, BTreeMap, etc)] - else if lang == "de" [collections (Vec, BTreeMap, usw.)] - else if lang == "ja" [コレクション (Vec, HashMap, など)] - else if lang == "zh" [容器 (Vec, BTreeMap, etc)] - else { todo }, + tr(( + en: [collections (Vec, BTreeMap, etc)], + de: [collections (Vec, BTreeMap, usw.)], + ja: [コレクション (Vec, HashMap, など)], + zh: [容器 (Vec, BTreeMap, etc)], + )), [\*\*], [✓], - if lang == "en" [stack overflow protection] - else if lang == "de" [Schutz vor Stack-Überlauf] - else if lang == "ja" [スタックオーバーフロープロテクション] - else if lang == "zh" [栈溢出保护] - else { todo }, + tr(( + en: [stack overflow protection], + de: [Schutz vor Stack-Überlauf], + ja: [スタックオーバーフロープロテクション], + zh: [栈溢出保护], + )), [✘], [✓], - if lang == "en" [runs init code before main] - else if lang == "de" [führt Initialisierungscode vor der main-Funktion aus] - else if lang == "ja" [main関数前の初期化コード実行] - else if lang == "zh" [在进入main之前运行的初始化代码] - else { todo }, + tr(( + en: [runs init code before main], + de: [führt Initialisierungscode vor der main-Funktion aus], + ja: [main関数前の初期化コード実行], + zh: [在进入main之前运行的初始化代码], + )), [✘], [✓], - if lang in ("en", "zh") [libstd available] - else if lang == "de" [libstd verfügbar] - else if lang == "ja" [libstdの利用] - else { todo }, + tr(( + en: [libstd available], + de: [libstd verfügbar], + ja: [libstdの利用], + zh: [libstd available], + )), [✘], [✓], - if lang in ("en", "zh") [libcore available] - else if lang == "de" [libcore verfügbar] - else if lang == "ja" [libcoreの利用] - else { todo }, + tr(( + en: [libcore available], + de: [libcore verfügbar], + ja: [libcoreの利用], + zh: [libcore available], + )), [✓], [✓], - if lang == "en" [writing firmware, kernel, or bootloader code] - else if lang == "de" [schreibt firmware, kernel, oder bootloader code] - else if lang == "ja" [ファームウェア、カーネル、ブートローダーのコードを書く] - else if lang == "zh" [编写固件,内核,或者引导程序] - else { todo }, + tr(( + en: [writing firmware, kernel, or bootloader code], + de: [schreibt firmware, kernel, oder bootloader code], + ja: [ファームウェア、カーネル、ブートローダーのコードを書く], + zh: [编写固件,内核,或者引导程序], + )), [✓], [✘], ) ) #let ln_cortex = link("https://github.com/rust-embedded/alloc-cortex-m")[alloc-cortex-m] -#if lang == "en" [ +#tr(( +en: [ \* Only if you use the `alloc` crate and use a suitable allocator like #ln_cortex. -] else if lang == "de" [ +], +de: [ \* Nur wenn Sie das `alloc`-Crate und einen geeigneten Allocator wie #ln_cortex verwenden. -] else if lang == "ja" [ +], +ja: [ \* `alloc`クレートを使い、\[alloc-cortex-m\]のような適切なアロケータを使った場合のみ -] else if lang == "zh" [ +], +zh: [ \* 只有在你使用了 `alloc` crate 并设置了一个适合的分配器后,比如#ln_cortex;后可用. -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ \*\* Only if you use the `collections` crate and configure a global default allocator. -] else if lang == "de" [ +], +de: [ \*\* Nur wenn Sie das `collections`-Crate verwenden und einen globalen Standard-Allocator konfigurieren. -] else if lang == "ja" [ +], +ja: [ \*\* `collections`クレートを使い、グローバルなデフォルトアロケータを設定した場合のみ -] else if lang == "zh" [ +], +zh: [ \*\* 只有在你使用了 `collections` crate 并配置了一个全局默认的分配器后可用. -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ \*\* HashMap and HashSet are not available due to a lack of a secure random number generator. -] else if lang == "de" [ +], +de: [ \*\* HashMap und HashSet stehen aufgrund des Fehlens eines sicheren Zufallszahlengenerators nicht zur Verfügung. -] else if lang == "zh" [ +], +zh: [ \*\* 由于缺少安全的随机数产生器,所以无法使用HashMap和HashSet. -] else { todo } +])) -= #(if lang == "en" [See Also] - else if lang == "de" [Siehe auch] - else if lang == "ja" [参照] - else if lang == "zh" [参见] - else { todo }) += #tr(( + en: [See Also], + de: [Siehe auch], + ja: [参照], + zh: [参见], +)) - #link("https://github.com/rust-lang/rfcs/blob/master/text/1184-stabilize-no_std.html")[RFC-1184] diff --git a/typ/intro/tooling.typ b/typ/intro/tooling.typ index c686e342..14d6e9af 100644 --- a/typ/intro/tooling.typ +++ b/typ/intro/tooling.typ @@ -1,31 +1,36 @@ #import "../config.typ": * -#h1(offset: whole, - if lang == "en" [Tooling] - else if lang == "de" [Werkzeuge] - else if lang == "ja" [ツール] - else if lang == "zh" [工具] - else { todo }) - -#if lang == "en" [ +#h1(offset: whole, tr(( + en: [Tooling], + de: [Werkzeuge], + ja: [ツール], + zh: [工具], +))) + +#tr(( +en: [ Dealing with microcontrollers involves using several different tools as we'll be dealing with an architecture different than your laptop's and we'll have to run and debug programs on a _remote_ device. -] else if lang == "de" [ +], +de: [ Der Umgang mit Mikrocontrollern erfordert den Einsatz verschiedener Werkzeuge, da wir es mit einer Architektur zu tun haben, die sich von der Ihres Laptops unterscheidet, und Programme auf einem _entfernten_ Gerät ausführen sowie debuggen müssen. -] else if lang == "ja" [ +], +ja: [ マイクロコントローラを扱う際は、いくつかの異なるツールを利用することになります。あなたのノートPCとは異なるアーキテクチャを扱うことになるでしょうし、_リモート_デバイス上でプログラムを実行しデバッグする必要があります。 -] else if lang == "zh" [ +], +zh: [ 与微控制器打交道需要使用几种不同的工具,因为我们要处理的架构与笔记本电脑不同,我们必须在 _远程_ -] else { todo } +])) #let ln_binutils = link("https://github.com/rust-embedded/cargo-binutils")[`cargo-binutils`] #let ln_qemu = link("https://www.qemu.org/")[`qemu-system-arm`] #let ln_generate = link("https://github.com/ashleygwilliams/cargo-generate")[`cargo-generate`] -#if lang == "en" [ +#tr(( +en: [ We'll use all the tools listed below. Any recent version should work when a minimum version is not specified, but we have listed the versions we have tested. @@ -38,7 +43,8 @@ Tested versions: 7.10, 7.11, 7.12 and 8.1 - #ln_generate or `git`. These tools are optional but will make it easier to follow along with the book. -] else if lang == "de" [ +], +de: [ Wir werden alle unten aufgeführten Werkzeuge verwenden. Sofern keine Mindestversion angegeben ist, sollte jede aktuelle Version funktionieren; wir haben jedoch die Versionen aufgelistet, die wir @@ -52,7 +58,8 @@ empfohlen. Getestete Versionen: 7.10, 7.11, 7.12 und 8.1. - #ln_generate oder `git`. Diese Werkzeuge sind optional, erleichtern es jedoch, dem Buch zu folgen. -] else if lang == "ja" [ +], +ja: [ 下記リストのツールを利用します。最小バージョンが指定されていない場合、新しいバージョンであれば機能するはずです。私たちがテストしたバージョンをリストに示しています。 - ARM Cortex-Mコンパイルサポートを追加したRust 1.31、1.31-beta以上のツールチェイン。 - #ln_binutils\~0.1.4 @@ -62,7 +69,8 @@ 7.10、7.11、7.12、8.1 - \[任意\] `git`または#ln_generate。どちらもインストールしていないのであれば、どちらをインストールしてもかまいません。 -] else if lang == "zh" [ +], +zh: [ 设备上运行和调试程序。我们将使用下面列举出来的工具。当没有指定一个最小版本时,最新的版本应该也可以用,但是我们还是列出了我们已经测过的那些版本。 - Rust 1.31, 1.31-beta, 或者一个更新的,支持ARM Cortex-M编译的工具链。 - #ln_binutils \~0.1.4 @@ -71,27 +79,33 @@ - 有ARM支持的GDB。强烈建议7.12或者更新的版本。测试版本: 7.10, 7.11 和 8.1 - #ln_generate 或者 `git`。 这些工具都是可选的,但是跟着这本书来使用它们,会更容易。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ The text below explains why we are using these tools. Installation instructions can be found on the next page. -] else if lang == "de" [ +], +de: [ Der folgende Text erläutert, warum wir diese Werkzeuge verwenden. Installationsanweisungen finden Sie auf der nächsten Seite. -] else if lang == "ja" [ +], +ja: [ 下記に、なぜこれらのツールを利用するのか、を説明します。インストール方法は、次のページにあります。 -] else if lang == "zh" [ +], +zh: [ 下面的文档将解释我们为什么使用这些工具。安装指令可以在下一页找到。 -] else { todo } +])) -= #(if lang == "en" [`cargo-generate` OR `git`] - else if lang == "de" [`cargo-generate` ODER `git`] - else if lang == "ja" [`cargo-generate`か`git`] - else if lang == "zh" [`cargo-generate` 或者 `git`] - else { todo }) += #tr(( + en: [`cargo-generate` OR `git`], + de: [`cargo-generate` ODER `git`], + ja: [`cargo-generate`か`git`], + zh: [`cargo-generate` 或者 `git`], +)) -#if lang == "en" [ +#tr(( +en: [ Bare metal programs are non-standard (`no_std`) Rust programs that require some adjustments to the linking process in order to get the memory layout of the program right. This requires some additional files @@ -99,7 +113,8 @@ those for you in a template such that you only need to fill in the missing information (such as the project name and the characteristics of your target hardware). -] else if lang == "de" [ +], +de: [ „Bare-Metal"-Programme sind Rust-Programme ohne Standardbibliothek (`no_std`), die Anpassungen am Linker-Vorgang erfordern, um das korrekte Speicherlayout des Programms zu gewährleisten. Dies macht zusätzliche @@ -107,111 +122,131 @@ erforderlich. Wir haben diese Elemente in einer Vorlage zusammengefasst, sodass Sie lediglich die noch fehlenden Angaben ergänzen müssen (etwa den Projektnamen und die Spezifikationen Ihrer Zielhardware). -] else if lang == "ja" [ +], +ja: [ ベアメタルプログラムは、非標準 (`no_std`)なRustプログラムであり、プログラムのメモリレイアウトを正しくするために、リンクプロセスをいじる必要があります。 これには、独特なファイル(リンカスクリプトなど)と設定(リンカフラグ)が必要です。 プロジェクト名やターゲットハードウェアの特徴などを、空白に入力するだけで済むように、テンプレートを用意しています。 -] else if lang == "zh" [ +], +zh: [ 裸机编程是非标准Rust编程,为了得到正确的程序的内存布局,需要对链接过程进行一些调整,这要求添加一些额外的文件(比如linker scripts)和配置(比如linker flags)。我们已经为你把这些打包进了一个模板里了,你只需要补充缺失的信息(比如项目名和目标硬件的特性)。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Our template is compatible with `cargo-generate`: a Cargo subcommand for creating new Cargo projects from templates. You can also download the template using `git`, `curl`, `wget`, or your web browser. -] else if lang == "de" [ +], +de: [ Unsere Vorlage ist mit `cargo-generate` kompatibel -- einem Cargo-Unterbefehl zum Erstellen neuer Cargo-Projekte auf Basis von Vorlagen. Alternativ können Sie die Vorlage auch mithilfe von `git`, `curl`, `wget` oder Ihrem Webbrowser herunterladen. -] else if lang == "ja" [ +], +ja: [ このテンプレートは、`cargo-generate`との互換性があります。`cargo-generate`は、テンプレートからCargoプロジェクトを作成するためのCargoのサブコマンドです。 このテンプレートは、`git`や`curl`、`wget`、ウェブブラウザを使ってダウンロードできます。 -] else if lang == "zh" [ +], +zh: [ 我们的模板兼容`cargo-generate`:一个用来从模板生成新的Cargo项目的Cargo子命令。你也能使用`git`,`curl`,`wget`,或者你的网页浏览器下载模板。 -] else { todo } +])) = `cargo-binutils` -#if lang == "en" [ +#tr(( +en: [ `cargo-binutils` is a collection of Cargo subcommands that make it easy to use the LLVM tools that are shipped with the Rust toolchain. These tools include the LLVM versions of `objdump`, `nm` and `size` and are used for inspecting binaries. -] else if lang == "de" [ +], +de: [ `cargo-binutils` ist eine Sammlung von Cargo-Unterbefehlen, die die Verwendung der mit der Rust-Toolchain ausgelieferten LLVM-Werkzeuge vereinfachen. Zu diesen Werkzeugen gehören die LLVM-Varianten von `objdump`, `nm` und `size`, die zur Untersuchung von Binärdateien dienen. -] else if lang == "ja" [ +], +ja: [ `cargo-binutils`は、Rustツールチェインとともに配布されているLLVMツールを簡単に使うためのCargoサブコマンド一式です。 これらのツールは、LLVMの`objdump`や`nm`、`size`を含んでおり、バイナリを調査するために使われます。 -] else if lang == "zh" [ +], +zh: [ `cargo-binutils`是一个Cargo命令的子集,它让我们能轻松使用Rust工具链带来的LLVM工具。这些工具包括LLVM版本的`objdump`,`nm`和`size`,用来查看二进制文件。 在GNU -] else { todo } +])) #{ let cmd = `rustup component add llvm-tools` -if lang == "en" [ +tr(( + en: [ The advantage of using these tools over GNU binutils is that (a) installing the LLVM tools is the same one-command installation (#cmd) regardless of your OS and (b) tools like `objdump` support all the architectures that `rustc` supports -- from ARM to x86_64 -- because they both share the same LLVM backend. -] else if lang == "de" [ +], +de: [ Der Vorteil dieser Werkzeuge gegenüber den GNU-Binutils liegt darin, dass (a) die Installation der LLVM-Werkzeuge unabhängig vom Betriebssystem stets mit demselben einzelnen Befehl (#cmd) erfolgt und (b) Werkzeuge wie `objdump` alle von `rustc` unterstützten Architekturen abdecken -- von ARM bis x86_64 --, da sie auf demselben LLVM-Backend basieren. -] else if lang == "ja" [ +], +ja: [ GNU binutilsではなく、これらのツールを利用する利点は次の通りです。 (a) LLVMツールのインストールは、OSに関わらず、共通のコマンド1つ(cmd)で済みます。 (b) `objdump`などのツールは、`rustc`がサポートする全てのアーキテクチャ(ARMからx86\_64まで)をサポートします。これは、同じLLVMバックエンドを共有しているためです。 -] else if lang == "zh" [ +], +zh: [ binutils之上使用这些工具的好处是,(a)无论你的操作系统是什么,安装这些LLVM工具都可以用同一条命令(#cmd)。(b)像是`objdump`这样的工具,支持所有`rustc`支持的架构--从ARM到x86\_64--因为它们都有一样的LLVM后端。 -] else { todo } +])) } = `qemu-system-arm` -#if lang == "en" [ +#tr(( +en: [ QEMU is an emulator. In this case we use the variant that can fully emulate ARM systems. We use QEMU to run embedded programs on the host. Thanks to this you can follow some parts of this book even if you don't have any hardware with you! -] else if lang == "de" [ +], +de: [ QEMU ist ein Emulator. In diesem Fall verwenden wir die Variante, die ARM-Systeme vollständig emulieren kann. Wir nutzen QEMU, um Embedded-Programme auf dem Host-System auszuführen. Auf diese Weise können Sie einige Teile dieses Buches auch dann nachvollziehen, wenn Sie keine Hardware zur Hand haben! -] else if lang == "ja" [ +], +ja: [ QEMUはエミュレータです。今回は、ARMシステムを完全にエミュレートできるものを使います。 私たちは、ホスト上で組込みプログラムを実行するためにQEMUを利用します。 このおかげで、ハードウェアを持っていなくても、この本のいくつかの部分を試すことができます。 -] else if lang == "zh" [ +], +zh: [ QEMU是一个仿真器。在这个例子里,我们使用能完全仿真ARM系统的改良版QEMU。我们使用QEMU在主机上运行嵌入式程序。多亏了它,你可以在没有任何硬件的情况下,尝试这本书的部分示例。 -] else { todo } - -#h1(offset: whole, - if lang == "en" [Tooling for Embedded Rust Debugging] - else if lang == "de" [Werkzeuge für das Debugging von Embedded Rust] - else if lang == "zh" [用于调试嵌入式Rust的工具] - else { todo }) - -= #(if lang == "en" [Overview] - else if lang == "de" [Überblick] - else if lang == "zh" [概述] - else { todo }) - -#if lang == "en" [ +])) + +#h1(offset: whole, tr(( + en: [Tooling for Embedded Rust Debugging], + de: [Werkzeuge für das Debugging von Embedded Rust], + zh: [用于调试嵌入式Rust的工具], +))) + += #tr(( + en: [Overview], + de: [Überblick], + zh: [概述], +)) + +#tr(( +en: [ Debugging embedded systems in Rust requires specialized tools including software to manage the debugging process, debuggers to inspect and control program execution, and hardware probes to facilitate interaction @@ -221,7 +256,8 @@ if lang == "en" [ and the Probe-rs Visual Studio Code extension. Additionally, it covers key hardware probes such as Rusty-probe, ST-Link, J-Link, and MCU-Link, which are integral for effective debugging and programming of embedded devices. -] else if lang == "de" [ +], +de: [ Das Debugging eingebetteter Systeme in Rust erfordert spezialisierte Werkzeuge, darunter Software zur Steuerung des Debugging-Prozesses, Debugger zur Überwachung und Kontrolle der Programmausführung sowie @@ -233,20 +269,23 @@ if lang == "en" [ werden wichtige Hardware-Probes wie Rusty-probe, ST-Link, J-Link und MCU-Link behandelt, die für das effiziente Debugging und Programmieren eingebetteter Geräte unerlässlich sind. -] else if lang == "zh" [ +], +zh: [ 在Rust中调试嵌入式系统需要用到专业的工具,这包括用于管理调试进程的软件,用于观察和控制程序执行的调试器,和用于便捷主机和嵌入式设备之间进行交互的硬件探测器.这个文档会介绍像是Probe-rs和OpenOCD这样的基础软件,以及像是GDB和Probe-rs Visual Studio Code扩展这样常见的调试器.另外,该文档会覆盖像是Rusty-probe,ST-Link,J-Link,和MCU-Link这样的硬件探测器,它们整合在一起可以高效地对嵌入式设备进行调试和编程. -] else { todo } +])) -= #(if lang == "en" [Software that drives debugging tools] - else if lang == "de" [Software, die Debugging-Werkzeuge steuert] - else if lang == "zh" [驱动调试工具的软件] - else { todo }) += #tr(( + en: [Software that drives debugging tools], + de: [Software, die Debugging-Werkzeuge steuert], + zh: [驱动调试工具的软件], +)) == Probe-rs -#if lang == "en" [ +#tr(( +en: [ Probe-rs is a modern, Rust-focused software designed to work with debuggers in embedded systems. Unlike OpenOCD, Probe-rs is built with simplicity in mind and aims to reduce the configuration burden often @@ -255,7 +294,8 @@ if lang == "en" [ hardware. Probe-rs integrates directly with Rust tooling, and integrates with Visual Studio Code through its extension, allowing developers to streamline their debugging workflow. -] else if lang == "de" [ +], +de: [ Probe-rs ist eine moderne, auf Rust ausgerichtete Software für das Debugging in Embedded-Systemen. Im Gegensatz zu OpenOCD wurde Probe-rs mit dem Fokus auf Einfachheit entwickelt und zielt darauf ab, den bei @@ -265,15 +305,17 @@ if lang == "en" [ Embedded-Hardware. Probe-rs lässt sich direkt in die Rust-Toolchain sowie -- dank einer entsprechenden Erweiterung -- in Visual Studio Code integrieren, wodurch Entwickler ihren Debugging-Workflow optimieren können. -] else if lang == "zh" [ +], +zh: [ Probe-rs是一个现代化的,以Rust开发的软件,被设计用来配合嵌入式系统中的调试器一起工作.不像OpenOCD,Probe-rs设计的时候就考虑到了简单性,目标是减少在其它调试解决方案中常见的配置重担. 它支持不同的探测器和目标架构,提供一个用于与嵌入式硬件交互的高层接口.Probe-rs直接集成了Rust工具链,并且通过扩展集成进了Visual Studio Code中,允许开发者精简它们的调试工作流程. -] else { todo } +])) == OpenOCD (Open On-Chip Debugger) -#if lang == "en" [ +#tr(( +en: [ OpenOCD is an open-source software tool used for debugging, testing, and programming embedded systems. It provides an interface between the host system and embedded hardware, supporting various transport layers like @@ -281,7 +323,8 @@ if lang == "en" [ a debugger. OpenOCD is widely supported, with extensive documentation and a large community, but may require complex configuration, especially for custom embedded setups. -] else if lang == "de" [ +], +de: [ OpenOCD ist ein Open-Source-Softwaretool für das Debugging, Testen und Programmieren eingebetteter Systeme. Es stellt eine Schnittstelle zwischen dem Host-System und der eingebetteten Hardware bereit und @@ -291,17 +334,21 @@ if lang == "en" [ Dokumentation sowie eine große Community, kann jedoch -- insbesondere bei kundenspezifischen Embedded-Konfigurationen -- eine komplexe Einrichtung erfordern. -] else if lang == "zh" [ +], +zh: [ OpenOCD是一个用于调试,测试,和编程嵌入式系统的开源软件工具.它提供了一个主机系统和嵌入式硬件之间的接口,支持不同的传输层,比如JTAG和SWD(Serial Wire Debug).OpenOCD集成了GDB,其是一个调试器.OpenOCD受到了广泛的支持,拥有大量的文档和一个庞大的社区,但是配置可能会很复杂,特别是对于自定义的嵌入式设置. -] else { todo } +])) -= #(if lang in ("en", "zh") [Debuggers] - else if lang == "de" [Debugger] - else { todo }) += #tr(( + en: [Debuggers], + de: [Debugger], + zh: [Debuggers], +)) -#if lang == "en" [ +#tr(( +en: [ A debugger allows developers to inspect and control the execution of a program in order to identify and correct errors or bugs. It provides functionalities such as setting breakpoints, stepping through code line @@ -309,7 +356,8 @@ if lang == "en" [ Debuggers are essential for thorough software development and maintenance, enabling developers to ensure that their code behaves as intended under various conditions. -] else if lang == "de" [ +], +de: [ Ein Debugger ermöglicht es Entwicklern, die Ausführung eines Programms zu untersuchen und zu steuern, um Fehler oder Bugs zu identifizieren und zu beheben. Er bietet Funktionen wie das Setzen von Haltepunkten @@ -318,11 +366,13 @@ if lang == "en" [ eine gründliche Softwareentwicklung und -wartung unerlässlich, da sie Entwicklern helfen sicherzustellen, dass sich ihr Code unter verschiedenen Bedingungen wie vorgesehen verhält. -] else if lang == "zh" [ +], +zh: [ 调试器允许开发者观察和控制一个程序的执行,以辨别和纠正错误或者bugs.它提供像是设置断点,一行一行地步进代码,和研究变量的值以及内存的状态等功能.调试器本质上是为了通过软件开发和维护,使得开发者可以确保他们的代码的行为在不同环境下就像他们预期的那样运行. -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Debuggers know how to: - Interact with the memory mapped registers. - Set Breakpoints/Watchpoints. @@ -330,7 +380,8 @@ if lang == "en" [ - Detect when the MCU has been halted for a debug event. - Continue MCU execution after a debug event has been encountered. - Erase and write to the microcontroller's FLASH. -] else if lang == "de" [ +], +de: [ Debugger bieten folgende Möglichkeiten: - Auf die im Speicher abgebildeten Register zugreifen. - Haltepunkte/Überwachungspunkte setzen. @@ -338,7 +389,8 @@ if lang == "en" [ - Erkennen, wenn die MCU aufgrund eines Debug-Ereignisses angehalten wurde. - Die MCU-Ausführung nach dem Auftreten eines Debug-Ereignisses fortsetzen. - Den Flash-Speicher des Mikrocontrollers löschen und beschreiben. -] else if lang == "zh" [ +], +zh: [ 调试器可以知道如何: - 与映射到存储上的寄存器交互. - 设置断点. @@ -346,31 +398,35 @@ if lang == "en" [ - 检测什么时候MCU因为一个调试时间被挂了起来. - 在遇到一个调试事件后继续MCU的执行. - 擦出和写入微控制器的FLASH. -] else { todo } +])) == Probe-rs Visual Studio Code Extension -#if lang == "en" [ +#tr(( +en: [ Probe-rs has a Visual Studio Code extension, providing a seamless debugging experience without extensive setup. Through this connection, developers can use Rust-specific features like pretty printing and detailed error messages, ensuring that their debugging process aligns with the Rust ecosystem. -] else if lang == "de" [ +], +de: [ Probe-rs bietet eine Visual Studio Code-Erweiterung, die ein nahtloses Debugging-Erlebnis ohne aufwendige Einrichtung ermöglicht. Dank dieser Integration können Entwickler Rust-spezifische Funktionen wie Pretty-Printing und detaillierte Fehlermeldungen nutzen und so sicherstellen, dass ihr Debugging-Prozess optimal auf das Rust-Ökosystem abgestimmt ist. -] else if lang == "zh" [ +], +zh: [ Probe-rs有一个Visual Studio Code的扩展,提供了不需要额外设置的无缝的调试体验.通过它的帮助,开发者可以使用Rust特定的特性,像是漂亮的打印和详细的错误信息,确保它们的调试过程可以与Rust的生态对齐. -] else { todo } +])) == TRACE32 -#if lang == "en" [ +#tr(( +en: [ TRACE32 is a professional debugging and tracing solution developed by Lauterbach for embedded systems. It supports a wide range of processor architectures, including ARM and RISC-V, and connects to target hardware @@ -379,7 +435,8 @@ if lang == "en" [ and real-time trace analysis. It works with standard ELF/DWARF debug information, making it compatible with Rust binaries built using conventional toolchains. -] else if lang == "de" [ +], +de: [ TRACE32 ist eine von Lauterbach für Embedded-Systeme entwickelte professionelle Debugging- und Tracing-Lösung. Sie unterstützt eine Vielzahl von Prozessorarchitekturen, darunter ARM und RISC-V, und stellt @@ -389,11 +446,12 @@ if lang == "en" [ Echtzeit-Trace-Analyse. Da das System auf Standard-ELF/DWARF-Debuginformationen basiert, ist es mit Rust-Binärdateien kompatibel, die mit herkömmlichen Werkzeugen erstellt wurden. -] else { todo } +])) == GDB (GNU Debugger) -#if lang == "en" [ +#tr(( +en: [ GDB is a versatile debugging tool that allows developers to examine the state of programs while they run or after they crash. For embedded Rust, GDB connects to the target system via OpenOCD or other debugging servers @@ -402,7 +460,8 @@ if lang == "en" [ conditional breakpoints. It can be used on a variety of platforms, and has extensive support for Rust-specific debugging needs, such as pretty printing and integration with IDEs. -] else if lang == "de" [ +], +de: [ GDB ist ein vielseitiges Debugging-Werkzeug, das es Entwicklern ermöglicht, den Zustand von Programmen während der Laufzeit oder nach einem Absturz zu untersuchen. Im Bereich Embedded Rust verbindet sich @@ -412,15 +471,19 @@ if lang == "en" [ bedingte Haltepunkte. Es ist auf einer Vielzahl von Plattformen einsetzbar und bietet umfassende Unterstützung für Rust-spezifische Debugging-Anforderungen, wie etwa Pretty-Printing und die Integration in IDEs. -] else if lang == "zh" [ +], +zh: [ GDB是一个多用途的调试工具,其允许开发者研究程序的状态,无论其正在运行中还是程序崩溃后.对于嵌入式Rust,GDB通过OpenOCD或者其它的调试服务器链接到目标系统上去和嵌入式代码交互.GDB是高度可配置的,并且支持像是远程调试,变量检测,和条件断点.它可以被用于多个平台,并对Rust特定的调试需求有广泛的支持,比如好看的打印和与IDEs集成. -] else { todo } +])) -= #(if lang in ("en", "de") [Probes] - else if lang == "zh" [探测器] - else { todo }) += #tr(( + en: [Probes], + de: [Probes], + zh: [探测器], +)) -#if lang == "en" [ +#tr(( +en: [ A hardware probe is a device used in the development and debugging of embedded systems to facilitate communication between a host computer and the target embedded device. It typically supports protocols like JTAG or @@ -429,7 +492,8 @@ if lang == "en" [ developers to set breakpoints, step through code, and inspect memory and processor registers, effectively allowing them to diagnose and fix issues in real-time. -] else if lang == "de" [ +], +de: [ Eine Hardware-Sonde ist ein Gerät, das bei der Entwicklung und dem Debugging eingebetteter Systeme eingesetzt wird, um die Kommunikation zwischen einem Host-Computer und dem eingebetteten Zielsystem zu @@ -440,30 +504,35 @@ if lang == "en" [ (Breakpoints) zu setzen, den Code schrittweise auszuführen sowie Speicher und Prozessorregister zu untersuchen; dies erlaubt es ihnen, Probleme effizient in Echtzeit zu diagnostizieren und zu beheben. -] else if lang == "zh" [ +], +zh: [ 硬件探头是一个被用于嵌入式系统的开发和调试的设备,其可以使得主机和目标嵌入式设备间的通信变得简单.它通常支持像是JTAG或者SWD这样的协议,可以编程,调试和分析嵌入式系统上的微控制器或者微处理器.硬件探头对于要设置断点,步进代码,和观察内存与处理器的寄存器的开发者来说很重要,可以让开发者们高效地实时地分析和修复问题. -] else { todo } +])) == Rusty-probe -#if lang == "en" [ +#tr(( +en: [ Rusty-probe is an open-sourced USB-based hardware debugging probe designed to work with probe-rs. The combination of Rusty-Probe and probe-rs provides an easy-to-use, cost-effective solution for developers working with embedded Rust applications. -] else if lang == "de" [ +], +de: [ Rusty-probe ist ein Open-Source-Hardware-Debugging-Interface auf USB-Basis, das für den Einsatz mit probe-rs entwickelt wurde. Die Kombination aus Rusty-Probe und probe-rs bietet Entwicklern, die mit Embedded-Rust-Anwendungen arbeiten, eine benutzerfreundliche und kostengünstige Lösung. -] else if lang == "zh" [ +], +zh: [ Rusty-probe是一个开源的基于USB的硬件调试探测器,被设计用来辅助probe-rs一起工作.Rusy-Probe和probe-rs的结合为嵌入式Rust应用的开发者提供了一个易用的,成本高效的解决方案. -] else { todo } +])) == ST-Link -#if lang == "en" [ +#tr(( +en: [ The ST-Link is a popular debugging and programming probe developed by STMicroelectronics primarily for their STM32 and STM8 microcontroller series. It supports both debugging and programming via JTAG or SWD @@ -471,7 +540,8 @@ if lang == "en" [ support from STMicroelectronics' extensive range of development boards and its integration into major IDEs, making it a convenient choice for developers working with STM microcontrollers. -] else if lang == "de" [ +], +de: [ Der ST-Link ist ein weit verbreiteter Debugging- und Programmieradapter, der von STMicroelectronics primär für die Mikrocontroller-Serien STM32 und STM8 entwickelt wurde. Er unterstützt sowohl das Debugging als auch @@ -481,13 +551,15 @@ if lang == "en" [ gängige Entwicklungsumgebungen (IDEs) ist der ST-Link weit verbreitet und stellt eine komfortable Wahl für Entwickler dar, die mit STM-Mikrocontrollern arbeiten. -] else if lang == "zh" [ +], +zh: [ ST-Link是一个由STMicroelectronics开发的常见的调试和编程探测器,其主要用于它们的STM32和STM8微控制器系列.它支持通过JTAG或者SWD接口进行调试和编程.因为STMicroelectronics的大量的开发板对其直接支持并且它集成进了主流的IDEs中,所以使得它成为使用STM微控制器的开发者的首选. -] else { todo } +])) == J-Link -#if lang == "en" [ +#tr(( +en: [ J-Link, developed by SEGGER Microcontroller, is a robust and versatile debugger supporting a wide range of CPU cores and devices beyond just ARM, such as RISC-V. Known for its high performance and reliability, @@ -495,7 +567,8 @@ if lang == "en" [ and fine-pitch JTAG interfaces. It is favored for its advanced features like unlimited breakpoints in flash memory and its compatibility with a multitude of development environments. -] else if lang == "de" [ +], +de: [ J-Link, entwickelt von SEGGER Microcontroller, ist ein robuster und vielseitiger Debugger, der neben ARM auch eine breite Palette weiterer CPU-Kerne und Bausteine -- wie etwa RISC-V -- unterstützt. J-Link ist @@ -505,21 +578,24 @@ if lang == "en" [ für seine erweiterten Funktionen, wie etwa unbegrenzte Breakpoints im Flash-Speicher, sowie für seine Kompatibilität mit einer Vielzahl von Entwicklungsumgebungen. -] else if lang == "zh" [ +], +zh: [ J-Link是由SEGGER微控制器开发的,它是一个鲁棒和功能丰富的调试器,其支持大量的CPU内核和设备,不仅仅是ARM,比如RISC-V.因其高性能和可读性而闻名,J-Link支持不同的通信接口,包括JTAG,SWD,和fine-pitch JTAG接口.它因其高级的特性而受到欢迎,比如在flash存储中的无限的断点和它与多种开发环境的兼容性. -] else { todo } +])) == MCU-Link -#if lang == "en" [ +#tr(( +en: [ MCU-Link is a debugging probe that also functions as a programmer, provided by NXP Semiconductors. It supports a variety of ARM Cortex microcontrollers and interfaces seamlessly with development tools like MCUXpresso IDE. MCU-Link is particularly notable for its versatility and affordability, making it an accessible option for hobbyists, educators, and professional developers alike. -] else if lang == "de" [ +], +de: [ MCU-Link ist ein von NXP Semiconductors bereitgestellter Debugging-Adapter, der auch als Programmiergerät fungiert. Er unterstützt eine Vielzahl von ARM-Cortex-Mikrocontrollern und lässt sich @@ -527,8 +603,9 @@ if lang == "en" [ MCU-Link zeichnet sich besonders durch seine Vielseitigkeit und Kosteneffizienz aus und ist damit eine attraktive Lösung für Hobbyanwender, Lehrkräfte und professionelle Entwickler gleichermaßen. -] else if lang == "zh" [ +], +zh: [ MCU-Link是一个调试探测器,也可以作为编程器使用,由NXP Semiconductors提供.它支持不同的ARM Cortex微控制器且可以与像是MCUXpresso IDE这样的开发工具进行无缝地交互.MCU-Link因其丰富的功能和易使用而闻名,使它成为像是爱好者,教育者,和专业的开发者们的可行的选项. -] else { todo } +])) diff --git a/typ/peripherals/a-first-attempt.typ b/typ/peripherals/a-first-attempt.typ index 67a946c6..8625e50c 100644 --- a/typ/peripherals/a-first-attempt.typ +++ b/typ/peripherals/a-first-attempt.typ @@ -1,25 +1,28 @@ #import "../config.typ": * -#h1(offset: whole, - if lang == "en" [A First Attempt] - else if lang == "de" [Ein erster Versuch in Rust] - else if lang == "zh" [Rust尝鲜] - else { todo }) - -= #(if lang == "en" [The Registers] - else if lang == "de" [Die Register] - else if lang == "zh" [寄存器] - else { todo }) +#h1(offset: whole, tr(( + en: [A First Attempt], + de: [Ein erster Versuch in Rust], + zh: [Rust尝鲜], +))) + += #tr(( + en: [The Registers], + de: [Die Register], + zh: [寄存器], +)) #let url_arm = "http://infocenter.arm.com/help/topic/com.arm.doc.dui0553a/Babieigh.html" -#if lang == "en" [ +#tr(( +en: [ Let's look at the 'SysTick' peripheral - a simple timer which comes with every Cortex-M processor core. Typically you'll be looking these up in the chip manufacturer's data sheet or _Technical Reference Manual_, but this example is common to all ARM Cortex-M cores, let's look in the #link(url_arm)[ARM reference manual]. We see there are four registers: -] else if lang == "de" [ +], +de: [ Betrachten wir die „SysTick"-Peripherie -- einen einfachen Timer, der in jedem Cortex-M-Prozessorkern enthalten ist. Normalerweise würde man hierfür im Datenblatt des Chip-Herstellers oder im _Technical @@ -27,11 +30,12 @@ ARM-Cortex-M-Kerne gilt, werfen wir einen Blick in das #link(url_arm)[ARM-Referenzhandbuch]. Wir sehen, dass es vier Register gibt: -] else if lang == "zh" [ +], +zh: [ 让我们看下 'SysTick' 外设 - 一个简单的计时器,它存在于每个Cortex-M处理器内核中。通常你能在芯片厂商的数据手册或者_技术参考手册_中看到它们,但是下面的例子对所有ARM Cortex-M核心都是通用的,让我们看下#link(url_arm)[ARM参考手册]。我们能看到这里有四个寄存器: -] else { todo } +])) #figure( kind: table, @@ -39,58 +43,76 @@ columns: 4, align: (auto,auto,auto,auto,), table.header( - if lang in ("en", "de", "zh") [Offset] - else { todo }, - if lang in ("en", "de", "zh") [Name] - else { todo }, - if lang in ("en", "zh") [Description] - else if lang == "de" [Beschreibung] - else { todo }, - if lang in ("en", "zh") [Width] - else if lang == "de" [Breite] - else { todo }, + tr(( + en: [Offset], + de: [Offset], + zh: [Offset], + )), + tr(( + en: [Name], + de: [Name], + zh: [Name], + )), + tr(( + en: [Description], + de: [Beschreibung], + zh: [Description], + )), + tr(( + en: [Width], + de: [Breite], + zh: [Width], + )), ), [0x00], [SYST_CSR], - if lang == "en" [Control and Status Register] - else if lang == "de" [Steuerung und Status Register] - else if lang == "zh" [控制和状态寄存器] - else { todo }, + tr(( + en: [Control and Status Register], + de: [Steuerung und Status Register], + zh: [控制和状态寄存器], + )), [32 bits], [0x04], [SYST_RVR], - if lang == "en" [Reload Value Register] - else if lang == "de" [Wert-Register neu laden] - else if lang == "zh" [重装载值寄存器] - else { todo }, + tr(( + en: [Reload Value Register], + de: [Wert-Register neu laden], + zh: [重装载值寄存器], + )), [32 bits], [0x08], [SYST_CVR], - if lang == "en" [Current Value Register] - else if lang == "de" [Register für den aktuellen Wert] - else if lang == "zh" [当前值寄存器] - else { todo }, + tr(( + en: [Current Value Register], + de: [Register für den aktuellen Wert], + zh: [当前值寄存器], + )), [32 bits], [0x0C], [SYST_CALIB], - if lang == "en" [Calibration Value Register] - else if lang == "de" [Kalibrierwert-Register] - else if lang == "zh" [校准值寄存器] - else { todo }, + tr(( + en: [Calibration Value Register], + de: [Kalibrierwert-Register], + zh: [校准值寄存器], + )), [32 bits], ) ) -= #(if lang == "en" [The C Approach] - else if lang == "de" [Der C-Ansatz] - else if lang == "zh" [C语言风格的方法(The C Approach)] - else { todo }) += #tr(( + en: [The C Approach], + de: [Der C-Ansatz], + zh: [C语言风格的方法(The C Approach)], +)) -#if lang == "en" [ +#tr(( +en: [ In Rust, we can represent a collection of registers in exactly the same way as we do in C - with a `struct`. -] else if lang == "de" [ +], +de: [ In Rust können wir eine Sammlung von Registern auf genau dieselbe Weise darstellen wie in C -- mit einem `struct`. -] else if lang == "zh" [ +], +zh: [ 在Rust中,我们可以像C语言一样,用一个 `struct` 表示一组寄存器。 -] else { todo } +])) ```rust #[repr(C)] @@ -102,7 +124,8 @@ struct SysTick { } ``` -#if lang == "en" [ +#tr(( +en: [ The qualifier `#[repr(C)]` tells the Rust compiler to lay this structure out like a C compiler would. That's very important, as Rust allows structure fields to be re-ordered, while C does not. You can imagine the @@ -110,7 +133,8 @@ struct SysTick { the compiler! With this qualifier in place, we have our four 32-bit fields which correspond to the table above. But of course, this `struct` is of no use by itself - we need a variable. -] else if lang == "de" [ +], +de: [ Der Qualifizierer `#[repr(C)]` weist den Rust-Compiler an, diese Struktur wie ein C-Compiler anzulegen. Das ist sehr wichtig, da Rust die Neuordnung von Strukturfeldern zulässt, C jedoch nicht. Sie können sich @@ -119,22 +143,25 @@ struct SysTick { Qualifizierer verfügen wir über unsere vier 32-Bit-Felder, die der obigen Tabelle entsprechen. Aber diese „Struktur" allein nützt natürlich nichts -- wir brauchen eine Variable. -] else if lang == "zh" [ +], +zh: [ 告诉Rust编译器像C编译器一样去布局这个结构体。这非常重要,因为Rust允许结构体字段被重新排序,而C语言不允许。你可以想象下如果这些字段被编译器悄悄地重新排了序,在调试时会给我们带来多大的麻烦!有了这个限定符,我们就有了与上表对应的四个32位的字段。但当然,这个 `struct` 本身没什么用处 - 我们需要一个变量。 -] else { todo } +])) ```rust let systick = 0xE000_E010 as *mut SysTick; let time = unsafe { (*systick).cvr }; ``` -= #(if lang == "en" [Volatile Accesses] - else if lang == "de" [Volatile-Zugriffe] - else if lang == "zh" [volatile访问(Volatile Accesses)] - else { todo }) += #tr(( + en: [Volatile Accesses], + de: [Volatile-Zugriffe], + zh: [volatile访问(Volatile Accesses)], +)) -#if lang == "en" [ +#tr(( +en: [ Now, there are a couple of problems with the approach above. + We have to use unsafe every time we want to access our Peripheral. + We've got no way of specifying which registers are read-only or @@ -142,7 +169,8 @@ let time = unsafe { (*systick).cvr }; + Any piece of code anywhere in your program could access the hardware through this structure. + Most importantly, it doesn't actually work… -] else if lang == "de" [ +], +de: [ Nun gibt es bei dem oben genannten Ansatz einige Probleme. + Wir müssen `unsafe` verwenden, wann immer wir auf unsere Peripherie zugreifen wollen. @@ -151,21 +179,24 @@ let time = unsafe { (*systick).cvr }; + Jeder Code-Abschnitt an beliebiger Stelle Ihres Programms könnte über diese Struktur auf die Hardware zugreifen. + Vor allem aber funktioniert es eigentlich nicht … -] else if lang == "zh" [ +], +zh: [ 现在,上面的方法有一堆问题。 + 每次想要访问外设,不得不使用unsafe 。 + 无法指定哪个寄存器是只读的或者读写的。 + 程序中任何地方的任何一段代码都可以通过这个结构体访问硬件。 + 最重要的是,实际上它并不能工作。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Now, the problem is that compilers are clever. If you make two writes to the same piece of RAM, one after the other, the compiler can notice this and just skip the first write entirely. In C, we can mark variables as `volatile` to ensure that every read or write occurs as intended. In Rust, we instead mark the _accesses_ as volatile, not the variable. -] else if lang == "de" [ +], +de: [ Das Problem ist nun, dass Compiler clever sind. Wenn man zwei Schreibzugriffe nacheinander auf denselben Speicherbereich durchführt, kann der Compiler dies erkennen und den ersten Schreibvorgang einfach @@ -173,9 +204,10 @@ let time = unsafe { (*systick).cvr }; kennzeichnen, um sicherzustellen, dass jeder Lese- oder Schreibvorgang wie vorgesehen ausgeführt wird. In Rust hingegen kennzeichnen wir die _Zugriffe_ als volatile, nicht die Variable. -] else if lang == "zh" [ +], +zh: [ 现在的问题是编译器很聪明。如果你往RAM同个地方写两次,一个接着一个,编译器会注意到这个行为,且完全跳过第一个写入操作。在C语言中,我们能标记变量为`volatile`去确保每个读或写操作按所想的那样发生。在Rust中,我们将_访问_操作标记为易变的(volatile),而不是将变量标记为volatile。 -] else { todo } +])) ```rust let systick = unsafe { &mut *(0xE000_E010 as *mut SysTick) }; @@ -183,18 +215,21 @@ let time = unsafe { core::ptr::read_volatile(&mut systick.cvr) }; ``` #let ln_volatile = link("https://crates.io/crates/volatile_register")[`volatile_register`] -#if lang == "en" [ +#tr(( +en: [ So, we've fixed one of our four problems, but now we have even more `unsafe` code! Fortunately, there's a third party crate which can help - #ln_volatile. -] else if lang == "de" [ +], +de: [ Wir haben also eines unserer vier Probleme gelöst, aber jetzt haben wir noch mehr `unsafe`-Code! Glücklicherweise gibt es ein Crate eines Drittanbieters, das hierbei helfen kann -- #ln_volatile. -] else if lang == "zh" [ +], +zh: [ 这样,我们已经修复了一个问题,但是现在我们有了更多的 `unsafe` 代码!幸运的是,有个第三方的crate可以帮助到我们 - #link("https://crates.io/crates/volatile_register")[`volatile_register`] -] else { todo } +])) ```rust use volatile_register::{RW, RO}; @@ -217,54 +252,64 @@ fn get_time() -> u32 { } ``` -#if lang == "en" [ +#tr(( +en: [ Now, the volatile accesses are performed automatically through the `read` and `write` methods. It's still `unsafe` to perform writes, but to be fair, hardware is a bunch of mutable state and there's no way for the compiler to know whether these writes are actually safe, so this is a good default position. -] else if lang == "de" [ +], +de: [ Die volatilen Zugriffe erfolgen nun automatisch über die Methoden `read` und `write`. Schreibzugriffe sind zwar weiterhin als `unsafe` eingestuft, doch fairerweise muss man sagen, dass Hardware im Grunde aus veränderlichem Zustand besteht und der Compiler nicht wissen kann, ob diese Schreibvorgänge tatsächlich sicher sind; daher ist dies eine sinnvolle Standardeinstellung. -] else if lang == "zh" [ +], +zh: [ 现在通过`read`和`write`方法,volatile accesses可以被自动执行。执行写操作仍然是 `unsafe` 的,但是公平地讲,硬件有一堆可变的状态,对于编译器来说没有办法知道是否这些写操作是真正安全的,因此默认就这样是个不错的选择。 -] else { todo } +])) -= #(if lang == "en" [The Rusty Wrapper] - else if lang == "de" [Der Rusty Wrapper] - else if lang == "zh" [Rust风格的封装] - else { todo }) += #tr(( + en: [The Rusty Wrapper], + de: [Der Rusty Wrapper], + zh: [Rust风格的封装], +)) -#if lang == "en" [ +#tr(( +en: [ We need to wrap this `struct` up into a higher-layer API that is safe for our users to call. As the driver author, we manually verify the unsafe code is correct, and then present a safe API for our users so they don't have to worry about it (provided they trust us to get it right!). -] else if lang == "de" [ +], +de: [ Wir müssen diese `struct` in eine API der höheren Ebene verpacken, deren Aufruf für unsere Nutzer sicher ist. Als Treiberentwickler überprüfen wir manuell die Korrektheit des `unsafe`-Codes und stellen unseren Nutzern dann eine sichere API zur Verfügung, sodass sie sich darum nicht kümmern müssen (vorausgesetzt, sie vertrauen darauf, dass wir alles richtig gemacht haben!). -] else if lang == "zh" [ +], +zh: [ 我们需要把这个`struct`封装进一个更高抽象的API中,这个API对于用户来说,可以安全地调用。作为驱动的作者,我们亲手验证不安全的代码是否正确,然后为我们的用户提供一个safe的API,因此用户们不必担心它(让他们相信我们不会出错!)。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ One example might be: -] else if lang == "de" [ +], +de: [ Ein Beispiel hierfür wäre: -] else if lang == "zh" [ +], +zh: [ 有可能可以这样写: -] else { todo } +])) ```rust use volatile_register::{RW, RO}; @@ -304,15 +349,18 @@ pub fn example_usage() -> String { } ``` -#if lang == "en" [ +#tr(( +en: [ Now, the problem with this approach is that the following code is perfectly acceptable to the compiler: -] else if lang == "de" [ +], +de: [ Das Problem bei diesem Ansatz ist nun, dass der folgende Code für den Compiler völlig zulässig ist: -] else if lang == "zh" [ +], +zh: [ 现在,这种方法带来的问题是,下列的代码完全可以被编译器接受: -] else { todo } +])) ```rust fn thread1() { @@ -326,7 +374,8 @@ fn thread2() { } ``` -#if lang == "en" [ +#tr(( +en: [ Our `&mut self` argument to the `set_reload` function checks that there are no other references to _that_ particular `SystemTimer` struct, but they don't stop the user creating a second `SystemTimer` which @@ -335,7 +384,8 @@ fn thread2() { driver instances, but once the code is spread out over multiple modules, drivers, developers, and days, it gets easier and easier to make these kinds of mistakes. -] else if lang == "de" [ +], +de: [ Das `&mut self`-Argument der Funktion `set_reload` stellt zwar sicher, dass keine weiteren Referenzen auf _genau diese_ `SystemTimer`-Struktur existieren, verhindert jedoch nicht, dass der @@ -345,7 +395,8 @@ fn thread2() { „doppelten" Treiberinstanzen zu erkennen; sobald sich der Code jedoch über mehrere Module, Treiber, Entwickler und Zeiträume hinweg erstreckt, schleichen sich derartige Fehler immer leichter ein. -] else if lang == "zh" [ +], +zh: [ 虽然 `set_reload` 函数的 `&mut self` 参数保证了没有引用到其它的`SystemTimer`结构体,但是不能阻止用户去创造第二个`SystemTimer`,其指向同个外设!如果作者足够努力的话,他能发现所有这些'重复的'驱动实例,那么按这种方式写的代码就可以工作,但是一旦代码被散播一段时间,散播给多个模块,驱动,开发者,它会越来越容易触发此类错误。 -] else { todo } +])) diff --git a/typ/peripherals/borrowck.typ b/typ/peripherals/borrowck.typ index 761d83f8..7eea5a6c 100644 --- a/typ/peripherals/borrowck.typ +++ b/typ/peripherals/borrowck.typ @@ -1,31 +1,36 @@ #import "../config.typ": * -#h1(offset: whole, - if lang == "en" [Mutable Global State] - else if lang == "de" [Veränderbarer globaler Zustand] - else if lang == "zh" [可变的全局状态] - else { todo }) +#h1(offset: whole, tr(( + en: [Mutable Global State], + de: [Veränderbarer globaler Zustand], + zh: [可变的全局状态], +))) -#if lang == "en" [ +#tr(( +en: [ Unfortunately, hardware is basically nothing but mutable global state, which can feel very frightening for a Rust developer. Hardware exists independently from the structures of the code we write, and can be modified at any time by the real world. -] else if lang == "de" [ +], +de: [ Leider ist Hardware im Grunde nichts anderes als veränderbarer globaler Zustand, was auf einen Rust-Entwickler sehr beängstigend wirken kann. Hardware existiert unabhängig von den Strukturen des Codes, den wir schreiben, und kann jederzeit durch die reale Welt verändert werden. -] else if lang == "zh" [ +], +zh: [ 不幸的是,硬件本质上是个可变的全局状态,Rust开发者可能会对此感到很害怕。因为硬件独立于我们所写的代码的结构,能被真实世界在任何时候改变。 -] else { todo } +])) -= #(if lang == "en" [What should our rules be?] - else if lang == "de" [Wie sollten unsere Regeln sein?] - else if lang == "zh" [我们应该遵循什么规则?] - else { todo }) += #tr(( + en: [What should our rules be?], + de: [Wie sollten unsere Regeln sein?], + zh: [我们应该遵循什么规则?], +)) -#if lang == "en" [ +#tr(( +en: [ How can we reliably interact with these peripherals? + Always use `volatile` methods to read or write to peripheral memory, as it can change at any time @@ -33,7 +38,8 @@ How can we reliably interact with these peripherals? accesses to these peripherals + If some software should have read-write access to a peripheral, it should hold the only reference to that peripheral -] else if lang == "de" [ +], +de: [ Wie können wir zuverlässig mit diesen Peripheriegeräten interagieren? + Verwenden Sie stets `volatile`-Methoden für Lese- oder Schreibzugriffe auf Peripheriespeicher, da sich dieser jederzeit ändern kann. @@ -42,50 +48,61 @@ How can we reliably interact with these peripherals? + Wenn eine Software Lese- und Schreibzugriff auf ein Peripheriegerät haben soll, sollte sie die einzige Referenz auf dieses Peripheriegerät besitzen. -] else if lang == "zh" [ +], +zh: [ 我们如何才能做到可靠地与这些外设交互? + 总是使用 `volatile` 方法去读或者写外设存储器。因为它随时会改变。 + 在软件中,我们应该能共享任何数量的关于这些外设的只读访问 + 如果某个软件可以读写一个外设,它应该保有对那个外设的唯一引用。 -] else { todo } +])) -= #(if lang == "en" [The Borrow Checker] - else if lang == "de" [Der Borrow-Checker] - else if lang == "zh" [借用检查器] - else { todo }) += #tr(( + en: [The Borrow Checker], + de: [Der Borrow-Checker], + zh: [借用检查器], +)) -#if lang == "en" [ +#tr(( +en: [ The last two of these rules sound suspiciously similar to what the Borrow Checker does already! -] else if lang == "de" [ +], +de: [ Die letzten beiden dieser Regeln klingen verdächtig ähnlich wie das, was der Borrow Checker bereits tut! -] else if lang == "zh" [ +], +zh: [ 这些规则最后两个听起来与借用检查器在做的事情很像! -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Imagine if we could pass around ownership of these peripherals, or offer immutable or mutable references to them? -] else if lang == "de" [ +], +de: [ Stellen Sie sich vor, wir könnten die Besitzrechte an diesen Peripheriegeräten weitergeben oder unveränderliche bzw. veränderliche Referenzen darauf anbieten? -] else if lang == "zh" [ +], +zh: [ 思考一下,我们是否可以传递这些外设的所有权,或者提供对它们的可变或者不可变的引用? -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Well, we can, but for the Borrow Checker, we need to have exactly one instance of each peripheral, so Rust can handle this correctly. Well, luckily in the hardware, there is only one instance of any given peripheral, but how can we expose that in the structure of our code? -] else if lang == "de" [ +], +de: [ Nun, das ist möglich, aber für den Borrow Checker benötigen wir genau eine Instanz jedes Peripheriegeräts, damit Rust dies korrekt verarbeiten kann. Glücklicherweise existiert in der Hardware nur eine Instanz jedes Peripheriegeräts, aber wie können wir dies in der Struktur unseres Codes sichtbar machen? -] else if lang == "zh" [ +], +zh: [ 我们当然可以,但是对于借用检查器来说,每个外设只有一个实例的话,Rust才可以正确地处理这件事。幸运的是,在硬件中,任何给定的外设,只有一个实例,但是我们该如何将它暴露在代码的结构中呢? -] else { todo } +])) diff --git a/typ/peripherals/index.typ b/typ/peripherals/index.typ index 82ad25d9..7e3af35f 100644 --- a/typ/peripherals/index.typ +++ b/typ/peripherals/index.typ @@ -1,24 +1,28 @@ #import "../config.typ": * -#h1(if lang == "en" [Peripherals] - else if lang == "de" [Peripheriegeräte] - else if lang == "zh" [外设] - else { todo }) +#h1(tr(( + en: [Peripherals], + de: [Peripheriegeräte], + zh: [外设], +))) -= #(if lang == "en" [What are Peripherals?] - else if lang == "de" [Was sind Peripheriegeräte?] - else if lang == "zh" [什么是外设?] - else { todo }) += #tr(( + en: [What are Peripherals?], + de: [Was sind Peripheriegeräte?], + zh: [什么是外设?], +)) -#if lang == "en" [ +#tr(( +en: [ Most Microcontrollers have more than just a CPU, RAM, or Flash Memory - they contain sections of silicon which are used for interacting with systems outside of the microcontroller, as well as directly and indirectly interacting with their surroundings in the world via sensors, motor controllers, or human interfaces such as a display or keyboard. These components are collectively known as Peripherals. -] else if lang == "de" [ +], +de: [ Die meisten Mikrocontroller verfügen über mehr als nur eine CPU, einen RAM oder einen Flash-Speicher -- sie enthalten Siliziumabschnitte, die für die Interaktion mit Systemen außerhalb des Mikrocontrollers sowie @@ -26,19 +30,22 @@ über Sensoren, Motorsteuerungen oder menschliche Schnittstellen wie ein Display oder eine Tastatur verwendet werden. Diese Komponenten werden zusammenfassend als Peripheriegeräte bezeichnet. -] else if lang == "zh" [ +], +zh: [ 大多数微处理器不仅仅有一个CPU,RAM,或者Flash存储器 - 它们还包含被用来与微处理器的外部系统进行交互的硅片部分,通过传感器,电机控制器,或者人机接口比如一个显示器或者键盘直接和间接地与周遭世界交互。这些组件统称为外设。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ These peripherals are useful because they allow a developer to offload processing to them, avoiding having to handle everything in software. Similar to how a desktop developer would offload graphics processing to a video card, embedded developers can offload some tasks to peripherals allowing the CPU to spend its time doing something else important, or doing nothing in order to save power. -] else if lang == "de" [ +], +de: [ Diese Peripheriegeräte sind nützlich, weil sie es einem Entwickler ermöglichen, die Verarbeitung auf sie auszulagern, sodass er sich nicht um alles in der Software kümmern muss. Ähnlich wie ein @@ -46,11 +53,13 @@ würde, können Embedded-Entwickler einige Aufgaben auf Peripheriegeräte verlagern, sodass die CPU ihre Zeit mit anderen wichtigen Dingen verbringen oder gar nichts tun kann, um Strom zu sparen. -] else if lang == "zh" [ +], +zh: [ 这些外设很有用,因为它们允许一个开发者将处理工作交给它们来做,避免了必须在软件中处理每件事。就像一个桌面开发者如何将图形处理工作让给一个显卡那样,嵌入式开发者能将一些任务让给外设去做,让CPU可以把时间放在做其它更重要的事上,或者为了省电啥事也不做。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ If you look at the main circuit board in an old-fashioned home computer from the 1970s or 1980s (and actually, the desktop PCs of yesterday are not so far removed from the embedded systems of today) you would expect @@ -59,7 +68,8 @@ - A RAM chip - A ROM chip - An I/O controller -] else if lang == "de" [ +], +de: [ Wenn Sie sich die Hauptplatine eines altmodischen Heimcomputers aus den 1970er oder 1980er Jahren ansehen (und tatsächlich sind die Desktop-PCs von gestern nicht so weit von den eingebetteten Systemen von heute @@ -68,15 +78,17 @@ - Einen RAM-Chip - Einen ROM-Chip - Einen Ein-/Ausgabe-Controller -] else if lang == "zh" [ +], +zh: [ 如果你看向来自1970s或者1980s的旧型号的家庭电脑的主板(其实,昨日的桌面PCs与今日的嵌入式系统没太大区别),你将看到: - 一个处理器 - 一个RAM芯片 - 一个ROM芯片 - 一个I/O控制器 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ The RAM chip, ROM chip and I/O controller (the peripheral in this system) would be joined to the processor through a series of parallel traces known as a 'bus'. This bus carries address information, which @@ -84,7 +96,8 @@ with, and a data bus which carries the actual data. In our embedded microcontrollers, the same principles apply - it's just that everything is packed on to a single piece of silicon. -] else if lang == "de" [ +], +de: [ Der RAM-Chip, der ROM-Chip und der I/O-Controller (das Peripheriegerät in diesem System) wären über eine Reihe paralleler Leiterbahnen, die als „Bus" bekannt sind, mit dem Prozessor verbunden. Dieser Bus trägt @@ -93,43 +106,52 @@ Daten überträgt. In unseren eingebetteten Mikrocontrollern gelten die gleichen Prinzipien -- nur dass alles auf einem einzigen Stück Silizium untergebracht ist. -] else if lang == "zh" [ +], +zh: [ RAM芯片,ROM芯片和I/O控制器(这个系统中的外设)会通过一系列并行的迹(traces)又被称为一个"总线"被加进处理器中。地址总线搬运地址信息,其用来选择处理器希望跟总线上哪个设备通信,还有一个用来搬运实际数据的数据总线。在我们的嵌入式微控制器中,应用了相同的概念 \- 只是所有的东西被打包到一片硅片上。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ However, unlike graphics cards, which typically have a Software API like Vulkan, Metal, or OpenGL, peripherals are exposed to our Microcontroller with a hardware interface, which is mapped to a chunk of the memory. -] else if lang == "de" [ +], +de: [ Im Gegensatz zu Grafikkarten, die normalerweise über eine Software-API wie Vulkan, Metal oder OpenGL verfügen, werden Peripheriegeräte unserem Mikrocontroller über eine Hardwareschnittstelle zugänglich gemacht, die einem Teil des Speichers zugeordnet ist. -] else if lang == "zh" [ +], +zh: [ 然而,不像显卡,显卡通常有像是Vulkan,Metal,或者OpenGL这样的一个软件API。外设暴露给微控制器的是一个硬件接口,其被映射到一块存储区域。 -] else { todo } +])) -= #(if lang == "en" [Linear and Real Memory Space] - else if lang == "de" [Linearer und realer Speicherraum] - else if lang == "zh" [线性的物理存储空间] - else { todo }) += #tr(( + en: [Linear and Real Memory Space], + de: [Linearer und realer Speicherraum], + zh: [线性的物理存储空间], +)) -#if lang == "en" [ +#tr(( +en: [ On a microcontroller, writing some data to some other arbitrary address, such as `0x4000_0000` or `0x0000_0000`, may also be a completely valid action. -] else if lang == "de" [ +], +de: [ Bei einem Mikrocontroller kann auch das Schreiben von Daten an eine beliebige andere Adresse -- etwa `0x4000_0000` oder `0x0000_0000` -- ein völlig zulässiger Vorgang sein. -] else if lang == "zh" [ +], +zh: [ 在一个微控制器上,随便往一些地址写一些数据,比如 `0x4000_0000` 或者 `0x0000_0000`,可能也是一个完全有效的动作。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ On a desktop system, access to memory is tightly controlled by the MMU, or Memory Management Unit. This component has two major responsibilities: enforcing access permission to sections of memory @@ -137,7 +159,8 @@ process); and re-mapping segments of the physical memory to virtual memory ranges used in software. Microcontrollers do not typically have an MMU, and instead only use real physical addresses in software. -] else if lang == "de" [ +], +de: [ Auf einem Desktop-System wird der Speicherzugriff streng von der MMU (Memory Management Unit, Speicherverwaltungseinheit) kontrolliert. Diese Komponente hat zwei Hauptaufgaben: die Durchsetzung von @@ -147,12 +170,14 @@ Speicherbereiche, die von der Software genutzt werden. Mikrocontroller verfügen in der Regel nicht über eine MMU; stattdessen verwendet die Software dort ausschließlich reale physische Adressen. -] else if lang == "zh" [ +], +zh: [ 在一个桌面系统上,访问内存被MMU,或者内存管理单元紧紧地控制着。这个组件有两个主要责任: 对部分内存加入访问权限(防止一个进程读取或者修改另一个进程的内存);重映射物理内存的段到软件中使用的虚拟内存范围上。微控制器通常没有一个MMU,反而在软件中只使用真实的物理地址。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Although 32 bit microcontrollers have a real and linear address space from `0x0000_0000`, and `0xFFFF_FFFF`, they generally only use a few hundred kilobytes of that range for actual memory. This leaves a @@ -169,7 +194,8 @@ Rather than ignore all remaining space between these two regions, Microcontroller designers instead mapped the interface for peripherals in certain memory locations. This ends up looking something like this: -] else if lang == "de" [ +], +de: [ Obwohl 32-Bit-Mikrocontroller über einen echten, linearen Adressraum von `0x0000_0000` bis `0xFFFF_FFFF` verfügen, nutzen sie für den eigentlichen Speicher meist nur einige hundert Kilobyte dieses Bereichs. @@ -188,42 +214,48 @@ diesen beiden Bereichen ungenutzt zu lassen, haben die Entwickler der Mikrocontroller die Schnittstellen für Peripheriegeräte bestimmten Speicheradressen zugeordnet. Das Ergebnis sieht dann ungefähr so ​​aus: -] else if lang == "zh" [ +], +zh: [ 虽然32位微控制器有一个从`0x0000_0000`到`0xFFFF_FFFF`的线性的物理地址空间,但是它们通常只使用几百KiB的实际内存。有相当大部分的地址空间保留着。在早期的章节中,我们说到RAM被放置在地址`0x2000_0000`处。如果我们的RAM是64KiB大小(i.e.~最大地址为0xFFFF),那么地址 `0x2000_0000`到`0x2000_FFFF`与我们的RAM有关。当我们写入一个位于地址`0x2000_1234`的变量时,内部发生的是,一些逻辑发现了地址的上部(这个例子里是0x2000),然后激活RAM,以便能操作地址的下部(这个例子里是0x1234)。在一个Cortex-M上,我们也也会把Flash ROM映射进地址 `0x000_0000` 到地址 `0x0007_FFFF` 上 (如果我们有一个512KiB Flash ROM)。微控制器设计者没有忽略这两个区域间的所有剩余空间,反而将外设的接口映射到这些地址上。最后看起来像这样: -] else { todo } +])) #box(image("../assets/nrf52-memory-map.png")) #link("http://infocenter.nordicsemi.com/pdf/nRF52832_PS_v1.1.pdf")[Nordic nRF52832 Datasheet (pdf)] -= #(if lang == "en" [Memory Mapped Peripherals] - else if lang == "de" [Im Speicher abgebildete Peripheriegeräte] - else if lang == "zh" [存储映射的外设] - else { todo }) += #tr(( + en: [Memory Mapped Peripherals], + de: [Im Speicher abgebildete Peripheriegeräte], + zh: [存储映射的外设], +)) -#if lang == "en" [ +#tr(( +en: [ Interaction with these peripherals is simple at a first glance - write the right data to the correct address. For example, sending a 32 bit word over a serial port could be as direct as writing that 32 bit word to a certain memory address. The Serial Port Peripheral would then take over and send out the data automatically. -] else if lang == "de" [ +], +de: [ Die Interaktion mit diesen Peripheriegeräten ist auf den ersten Blick einfach: Schreiben Sie die richtigen Daten an die richtige Adresse. Beispielsweise könnte das Senden eines 32-Bit-Worts über eine serielle Schnittstelle genauso direkt sein wie das Schreiben dieses 32-Bit-Worts an eine bestimmte Speicheradresse. Das Serial-Port-Peripheriegerät würde dann übernehmen und die Daten automatisch versenden. -] else if lang == "zh" [ +], +zh: [ 乍一看,与这些外设交互很简单 - 将正确的数据写入正确的地址。比如,在一个串行端口上发送一个32位字,可以直接把那个32位字写入某个存储地址。串行端口外设然后能自动获取和发出数据。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Configuration of these peripherals works similarly. Instead of calling a function to configure a peripheral, a chunk of memory is exposed which serves as the hardware API. Write `0x8000_0000` to a SPI Frequency @@ -231,7 +263,8 @@ per second. Write `0x0200_0000` to the same address, and the SPI port will send data at 125 Kilobits per second. These configuration registers look a little bit like this: -] else if lang == "de" [ +], +de: [ Die Konfiguration dieser Peripheriegeräte funktioniert ähnlich. Anstatt eine Funktion zum Konfigurieren eines Peripheriegeräts aufzurufen, wird ein Teil des Speichers verfügbar gemacht, der als Hardware-API dient. @@ -239,23 +272,27 @@ und der SPI-Port sendet Daten mit 8 Megabit pro Sekunde. Schreiben Sie „0x0200_0000" an dieselbe Adresse und der SPI-Port sendet Daten mit 125 Kilobit pro Sekunde. Diese Konfigurationsregister sehen in etwa so aus: -] else if lang == "zh" [ +], +zh: [ 这些外设的配置工作相似。不是调用一个函数去配置一个外设,而是暴露一块地址空间作为硬件API。向一个SPI频率控制寄存器写入 `0x8000_0000`,SPI端口将会按照每秒8MB的速度发送数据。向同个地址写入 `0x0200_0000`,SPI端口将会按照每秒125KiB的速度发送数据。这些配置寄存器看起来有点像这个: -] else { todo } +])) #box(image("../assets/nrf52-spi-frequency-register.png")) #link("http://infocenter.nordicsemi.com/pdf/nRF52832_PS_v1.1.pdf")[Nordic nRF52832 Datasheet (pdf)] -#if lang == "en" [ +#tr(( +en: [ This interface is how interactions with the hardware are made, no matter what language is used, whether that language is Assembly, C, or Rust. -] else if lang == "de" [ +], +de: [ Über diese Schnittstelle erfolgen die Interaktionen mit der Hardware, unabhängig davon, welche Sprache verwendet wird -- sei es Assembler, C oder Rust. -] else if lang == "zh" [ +], +zh: [ 这个接口是关于如何与硬件交互的,其与被使用的语言无关,无论这个语言是汇编,C,或者Rust。 -] else { todo } +])) diff --git a/typ/peripherals/singletons.typ b/typ/peripherals/singletons.typ index e258b4c5..6f87db7c 100644 --- a/typ/peripherals/singletons.typ +++ b/typ/peripherals/singletons.typ @@ -1,43 +1,54 @@ #import "../config.typ": * -#h1(offset: whole, - if lang in ("en", "de") [Singletons] - else if lang == "zh" [单例] - else { todo }) +#h1(offset: whole, tr(( + en: [Singletons], + de: [Singletons], + zh: [单例], +))) #quote(block: true)[ -#if lang == "en" [ +#tr(( +en: [ In software engineering, the singleton pattern is a software design pattern that restricts the instantiation of a class to one object. -] else if lang == "de" [ +], +de: [ In der Softwaretechnik ist das Singleton-Muster ein Entwurfsmuster, das die Instanziierung einer Klasse auf ein einziges Objekt beschränkt. -] else if lang == "zh" [ +], +zh: [ 在软件工程中,单例模式是一个软件设计模式,其限制了一个类到一个对象的实例化。 -] else { todo } +])) #let url_singleton = "https://en.wikipedia.org/wiki/Singleton_pattern" -#if lang == "en" [ +#tr(( +en: [ _Wikipedia: #link(url_singleton)[Singleton Pattern]_ -] else if lang == "de" [ +], +de: [ _Wikipedia: #link(url_singleton)[Singleton-Muster]_ -] else if lang == "zh" [ +], +zh: [ _Wikipedia: #link(url_singleton)[Singleton Pattern]_ -] else { todo } +])) ] -= #(if lang == "en" [But why can't we just use global variable(s)?] - else if lang == "de" [Aber warum können wir nicht einfach globale Variable(n) verwenden?] - else if lang == "zh" [为什么不可以使用全局变量?] - else { todo }) += #tr(( + en: [But why can't we just use global variable(s)?], + de: [Aber warum können wir nicht einfach globale Variable(n) verwenden?], + zh: [为什么不可以使用全局变量?], +)) -#if lang == "en" [ +#tr(( +en: [ We could make everything a public static, like this -] else if lang == "de" [ +], +de: [ Wir könnten alles als „public static" definieren, etwa so: -] else if lang == "zh" [ +], +zh: [ 可以像这样,我们可以使每个东西都变成公共静态的(public static): -] else { todo } +])) ```rust static mut THE_SERIAL_PORT: SerialPort = SerialPort; @@ -49,40 +60,47 @@ fn main() { } ``` -#if lang == "en" [ +#tr(( +en: [ But this has a few problems. It is a mutable global variable, and in Rust, these are always unsafe to interact with. These variables are also visible across your whole program, which means the borrow checker is unable to help you track references and ownership of these variables. -] else if lang == "de" [ +], +de: [ Dies bringt jedoch einige Probleme mit sich. Es handelt sich um eine veränderbare globale Variable, und der Zugriff auf solche Variablen ist in Rust stets „unsafe". Zudem sind diese Variablen im gesamten Programm sichtbar, was bedeutet, dass der Borrow Checker nicht dabei helfen kann, die Referenzen und die Ownership dieser Variablen zu verfolgen. -] else if lang == "zh" [ +], +zh: [ 但是这个带来了一些问题。它是一个可变的全局变量,在Rust,与这些变量交互总是unsafe的。这些变量在你所有的程序间也是可见的,意味着借用检查器不能帮你跟踪这些变量的引用和所有权。 -] else { todo } +])) -= #(if lang == "en" [How do we do this in Rust?] - else if lang == "de" [Wie machen wir das in Rust?] - else if lang == "zh" [在Rust中要怎么做?] - else { todo }) += #tr(( + en: [How do we do this in Rust?], + de: [Wie machen wir das in Rust?], + zh: [在Rust中要怎么做?], +)) -#if lang == "en" [ +#tr(( +en: [ Instead of just making our peripheral a global variable, we might instead decide to make a structure, in this case called `PERIPHERALS`, which contains an `Option` for each of our peripherals. -] else if lang == "de" [ +], +de: [ Anstatt unser Peripheriegerät einfach als globale Variable zu definieren, könnten wir uns stattdessen dazu entschließen, eine Struktur -- in diesem Fall mit dem Namen `PERIPHERALS` -- zu erstellen, die für jedes unserer Peripheriegeräte ein `Option` enthält. -] else if lang == "zh" [ +], +zh: [ 与其只是让我们的外设变成一个全局变量,我们不如创造一个结构体,在这个例子里其被叫做 `PERIPHERALS`,这个全局变量对于我们的每个外设,它都有一个与之对应的 `Option` . -] else { todo } +])) ```rust struct Peripherals { @@ -99,121 +117,133 @@ static mut PERIPHERALS: Peripherals = Peripherals { }; ``` -#if lang == "en" [ +#tr(( +en: [ This structure allows us to obtain a single instance of our peripheral. If we try to call `take_serial()` more than once, our code will panic! -] else if lang == "de" [ +], +de: [ Diese Struktur ermöglicht es uns, eine einzelne Instanz unserer Peripheriekomponente zu erhalten. Wenn wir versuchen, `take_serial()` mehr als einmal aufzurufen, wird unser Code eine Panic auslösen! -] else if lang == "zh" [ +], +zh: [ 这个结构体允许我们获得一个外设的实例。如果我们尝试调用`take_serial()`获得多个实例,我们的代码将会抛出运行时恐慌(panic)! -] else { todo } +])) #raw(block: true, lang: "rust", "fn main() { let serial_1 = unsafe { PERIPHERALS.take_serial() }; - // " + if lang == "en" { - "This panics!" - } else if lang == "de" { - "Dies loest eine panic aus!" - } else if lang == "zh" { - "这里造成运行时恐慌!" - } else { todos } + " + // " + ts(( + en: "This panics!", + de: "Dies loest eine panic aus!", + zh: "这里造成运行时恐慌!", + )) + " // let serial_2 = unsafe { PERIPHERALS.take_serial() }; } ") -#if lang == "en" [ +#tr(( +en: [ Although interacting with this structure is `unsafe`, once we have the `SerialPort` it contained, we no longer need to use `unsafe`, or the `PERIPHERALS` structure at all. -] else if lang == "de" [ +], +de: [ Obwohl die Interaktion mit dieser Struktur als `unsafe` gilt, benötigen wir -- sobald wir über die darin enthaltene `SerialPort`-Instanz verfügen -- weder `unsafe`-Code noch die `PERIPHERALS`-Struktur selbst. -] else if lang == "zh" [ +], +zh: [ 虽然与这个结构体交互是`unsafe`,然而一旦我们获得了它包含的 `SerialPort`,我们将不再需要使用`unsafe`,或者`PERIPHERALS`结构体。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ This has a small runtime overhead because we must wrap the `SerialPort` structure in an option, and we'll need to call `take_serial()` once, however this small up-front cost allows us to leverage the borrow checker throughout the rest of our program. -] else if lang == "de" [ +], +de: [ Dies bringt einen geringen Laufzeit-Overhead mit sich, da wir die `SerialPort`-Struktur in eine `Option` einbetten und einmalig `take_serial()` aufrufen müssen; dieser geringe anfängliche Aufwand ermöglicht es uns jedoch, den Borrow-Checker im weiteren Verlauf des Programms voll zu nutzen. -] else if lang == "zh" [ +], +zh: [ 这个带来了少量的运行时开销,因为我们必须打包 `SerialPort` 结构体进一个option中,且我们将需要调用一次 `take_serial()`,但是这种少量的前期成本,能使我们在接下来的程序中使用借用检查器(borrow checker) 。 -] else { todo } +])) -= #(if lang == "en" [Existing library support] - else if lang == "de" [Vorhandene Bibliotheksunterstützung] - else if lang == "zh" [已存在的库支持] - else { todo }) += #tr(( + en: [Existing library support], + de: [Vorhandene Bibliotheksunterstützung], + zh: [已存在的库支持], +)) -#if lang == "en" [ +#tr(( +en: [ Although we created our own `Peripherals` structure above, it is not necessary to do this for your code. the `cortex_m` crate contains a macro called `singleton!()` that will perform this action for you. -] else if lang == "de" [ +], +de: [ Obwohl wir oben unsere eigene `Peripherals`-Struktur erstellt haben, ist dies für Ihren Code nicht erforderlich; das `cortex_m`-Crate enthält ein Makro namens `singleton!()`, das diese Aufgabe für Sie übernimmt. -] else if lang == "zh" [ +], +zh: [ 虽然我们在上面生成了我们自己的 `Peripherals` 结构体,但这并不是必须的。`cortex_m` crate 包含一个被叫做 `singleton!()` 的宏,它可以为你完成这个任务。 -] else { todo } +])) #raw(block: true, lang: "rust", "use cortex_m::singleton; fn main() { - // " + if lang == "en" { - "OK if `main` is executed only once" - } else if lang == "de" { - "In Ordnung, wenn `main` nur einmal ausgefuehrt wird." - } else if lang == "zh" { - "OK 如果 `main` 只被执行一次" - } else { todos } + " + // " + ts(( + en: "OK if `main` is executed only once", + de: "In Ordnung, wenn `main` nur einmal ausgefuehrt wird.", + zh: "OK 如果 `main` 只被执行一次", + )) + " let x: &'static mut bool = singleton!(: bool = false).unwrap(); } ") #let url_sinmacro = "https://docs.rs/cortex-m/latest/cortex_m/macro.singleton.html" -#if lang in ("en", "zh") [ - #link(url_sinmacro)[cortex_m docs] -] else if lang == "de" [ - #link(url_sinmacro)[cortex_m Dokumente] -] else { todo } +#tr(( + en: link(url_sinmacro)[cortex_m docs], + de: link(url_sinmacro)[cortex_m Dokumente], + zh: link(url_sinmacro)[cortex_m docs], +)) #let ln_rtic = link("https://github.com/rtic-rs/cortex-m-rtic")[`cortex-m-rtic`] -#if lang == "en" [ +#tr(( +en: [ Additionally, if you use #ln_rtic, the entire process of defining and obtaining these peripherals are abstracted for you, and you are instead handed a `Peripherals` structure that contains a non-`Option` version of all of the items you define. -] else if lang == "de" [ +], +de: [ Wenn Sie zudem #ln_rtic verwenden, wird der gesamte Prozess der Definition und Bereitstellung dieser Peripheriekomponenten für Sie abstrahiert; stattdessen erhalten Sie eine `Peripherals`-Struktur, die eine Version aller von Ihnen definierten Elemente enthält, die nicht als `Option` vorliegen. -] else if lang == "zh" [ +], +zh: [ 另外,如果你使用 #ln_rtic,它将获取和定义这些外设的整个过程抽象了出来,你将获得一个`Peripherals`结构体,其包含了所有你定义了的项的一个非 `Option` 的版本。 -] else { todo } +])) #raw(block: true, lang: "rust", @@ -224,54 +254,52 @@ const APP: () = { fn init(cx: init::Context) { static mut X: u32 = 0; - // " + if lang == "en" { - "Cortex-M peripherals" - } else if lang == "de" { - "Cortex-M-Peripherie" - } else if lang == "zh" { - "Cortex-M外设" - } else { todos } + " + // " + ts(( + en: "Cortex-M peripherals", + de: "Cortex-M-Peripherie", + zh: "Cortex-M外设", + )) + " let core: cortex_m::Peripherals = cx.core; - // " + if lang == "en" { - "Device specific peripherals" - } else if lang == "de" { - "Geraetespezifische Peripheriegeraete" - } else if lang == "zh" { - "设备特定的外设" - } else { todos } + " + // " + ts(( + en: "Device specific peripherals", + de: "Geraetespezifische Peripheriegeraete", + zh: "设备特定的外设", + )) + " let device: lm3s6965::Peripherals = cx.device; } } ") -= #(if lang == "en" [But why?] - else if lang == "de" [Aber warum?] - else if lang == "zh" [为什么?] - else { todo }) += #tr(( + en: [But why?], + de: [Aber warum?], + zh: [为什么?], +)) -#if lang == "en" [ +#tr(( +en: [ But how do these Singletons make a noticeable difference in how our Rust code works? -] else if lang == "de" [ +], +de: [ Aber wie bewirken diese Singletons einen spürbaren Unterschied in der Funktionsweise unseres Rust-Codes? -] else if lang == "zh" [ +], +zh: [ 但是这些单例模式是如何使我们的Rust代码在工作方式上产生很大不同的? -] else { todo } +])) #raw(block: true, lang: "rust", "impl SerialPort { const SER_PORT_SPEED_REG: *mut u32 = 0x4000_1000 as _; fn read_speed( - &self // " + if lang == "en" { - "<------ This is really, really important" - } else if lang == "de" { - "<------ Das ist wirklich, wirklich wichtig." - } else if lang == "zh" { - "<------ 这个真的真的很重要" - } else { todos } + " + &self // " + ts(( + en: "<------ This is really, really important", + de: "<------ Das ist wirklich, wirklich wichtig.", + zh: "<------ 这个真的真的很重要", + )) + " ) -> u32 { unsafe { ptr::read_volatile(Self::SER_PORT_SPEED_REG) @@ -280,89 +308,98 @@ const APP: () = { } ") -#if lang == "en" [ +#tr(( +en: [ There are two important factors in play here: - Because we are using a singleton, there is only one way or place to obtain a `SerialPort` structure - To call the `read_speed()` method, we must have ownership or a reference to a `SerialPort` structure -] else if lang == "de" [ +], +de: [ Hier spielen zwei wichtige Faktoren eine Rolle: - Da wir ein Singleton verwenden, gibt es nur eine Möglichkeit bzw. einen Ort, um eine `SerialPort`-Struktur zu erhalten. - Um die Methode `read_speed()` aufzurufen, benötigen wir den Besitz einer `SerialPort`-Struktur oder eine Referenz darauf. -] else if lang == "zh" [ +], +zh: [ 这里有两个重要因素: - 因为我们正在使用一个单例模式,所以我们只有一种方法或者地方去获得一个 `SerialPort` 结构体。 - 为了调用 `read_speed()` 方法,我们必须拥有一个 `SerialPort` 结构体的所有权或者一个引用。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ These two factors put together means that it is only possible to access the hardware if we have appropriately satisfied the borrow checker, meaning that at no point do we have multiple mutable references to the same hardware! -] else if lang == "de" [ +], +de: [ Zusammengenommen bedeuten diese beiden Faktoren, dass ein Zugriff auf die Hardware nur möglich ist, wenn wir die Anforderungen des Borrow-Checkers ordnungsgemäß erfüllt haben -- das heißt, dass zu keinem Zeitpunkt mehrere veränderbare Referenzen auf dieselbe Hardware existieren! -] else if lang == "zh" [ +], +zh: [ 这两个因素放在一起意味着,只有当我们满足了借用检查器的条件时,我们才有可能访问硬件,也意味着在任何时候不可能存在多个对同一个硬件的可变引用(&mut)! -] else { todo } +])) #raw(block: true, lang: "rust", "fn main() { - // " + if lang == "en" { - "missing reference to `self`! Won't work." - } else if lang == "de" { - "Verweis auf `self` fehlt! Das wird nicht funktionieren." - } else if lang == "zh" { - "缺少对`self`的引用!将不会工作。" - } else { todos } + " + // " + ts(( + en: "missing reference to `self`! Won't work.", + de: "Verweis auf `self` fehlt! Das wird nicht funktionieren.", + zh: "缺少对`self`的引用!将不会工作。", + )) + " // SerialPort::read_speed(); let serial_1 = unsafe { PERIPHERALS.take_serial() }; - // " + if lang == "en" { - "you can only read what you have access to" - } else if lang == "de" { - "Sie koennen nur lesen, worauf Sie Zugriff haben" - } else if lang == "zh" { - "你只能读取你有权访问的内容" - } else { todos } + " + // " + ts(( + en: "you can only read what you have access to", + de: "Sie koennen nur lesen, worauf Sie Zugriff haben", + zh: "你只能读取你有权访问的内容", + )) + " let _ = serial_1.read_speed(); } ") -= #(if lang == "en" [Treat your hardware like data] - else if lang == "de" [Behandle deine Hardware wie Daten] - else if lang == "zh" [像对待数据一样对待硬件] - else { todo }) += #tr(( + en: [Treat your hardware like data], + de: [Behandle deine Hardware wie Daten], + zh: [像对待数据一样对待硬件], +)) -#if lang == "en" [ +#tr(( +en: [ Additionally, because some references are mutable, and some are immutable, it becomes possible to see whether a function or method could potentially modify the state of the hardware. For example, -] else if lang == "de" [ +], +de: [ Da es zudem sowohl veränderbare als auch unveränderbare Referenzen gibt, lässt sich erkennen, ob eine Funktion oder Methode potenziell den Zustand der Hardware ändern könnte. Zum Beispiel: -] else if lang == "zh" [ +], +zh: [ 另外,因为一些引用是可变的,一些是不可变的,就可以知道一个函数或者方法是否有能力修改硬件的状态。比如, -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ This is allowed to change hardware settings: -] else if lang == "de" [ +], +de: [ Dies darf Hardware-Einstellungen ändern: -] else if lang == "zh" [ +], +zh: [ 这个函数可以改变硬件的配置: -] else { todo } +])) ```rust fn setup_spi_port( @@ -373,13 +410,16 @@ fn setup_spi_port( } ``` -#if lang == "en" [ +#tr(( +en: [ This isn't: -] else if lang == "de" [ +], +de: [ Das nicht: -] else if lang == "zh" [ +], +zh: [ 这个不行: -] else { todo } +])) ```rust fn read_button(gpio: &GpioPin) -> bool { @@ -387,19 +427,22 @@ fn read_button(gpio: &GpioPin) -> bool { } ``` -#if lang == "en" [ +#tr(( +en: [ This allows us to enforce whether code should or should not make changes to hardware at *compile time*, rather than at runtime. As a note, this generally only works across one application, but for bare metal systems, our software will be compiled into a single application, so this is not usually a restriction. -] else if lang == "de" [ +], +de: [ Dies ermöglicht es uns, bereits zur *Kompilierzeit* -- und nicht erst zur Laufzeit -- festzulegen, ob Code Änderungen an der Hardware vornehmen darf oder nicht. Zu beachten ist, dass dies im Allgemeinen nur innerhalb einer einzelnen Anwendung funktioniert; da unsere Software für Bare-Metal-Systeme jedoch als eine einzige Anwendung kompiliert wird, stellt dies üblicherweise keine Einschränkung dar. -] else if lang == "zh" [ +], +zh: [ 这允许我们在*编译时*而不是运行时强制代码是否应该或者不应该对硬件进行修改。要注意,这通常在只有一个应用的情况下起作用,但是对于裸机系统来说,我们的软件将被编译进一个单一应用中,因此这通常不是一个限制。 -] else { todo } +])) diff --git a/typ/portability/index.typ b/typ/portability/index.typ index 2c12288a..5af0ca83 100644 --- a/typ/portability/index.typ +++ b/typ/portability/index.typ @@ -1,76 +1,93 @@ #import "../config.typ": * -#h1(if lang == "en" [Portability] - else if lang == "de" [Portabilität] - else if lang == "zh" [可移植性] - else { todo }) +#h1(tr(( + en: [Portability], + de: [Portabilität], + zh: [可移植性], +))) -#if lang == "en" [ +#tr(( +en: [ In embedded environments portability is a very important topic: Every vendor and even each family from a single manufacturer offers different peripherals and capabilities and similarly the ways to interact with the peripherals will vary. -] else if lang == "de" [ +], +de: [ In eingebetteten Systemen ist Portabilität ein sehr wichtiges Thema: Jeder Hersteller und sogar jede Produktfamilie eines einzelnen Herstellers bietet unterschiedliche Peripheriegeräte und Funktionen an, und dementsprechend variieren auch die Interaktionsmöglichkeiten mit den Peripheriegeräten. -] else if lang == "zh" [ +], +zh: [ 在嵌入式环境中,可移植性是一个非常重要的主题: 每个供应商甚至同个制造商的不同系列间,都提供了不同的外设和功能。同样地,与外设交互的方式也将会不一样。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ A common way to equalize such differences is via a layer called Hardware Abstraction layer or *HAL*. -] else if lang == "de" [ +], +de: [ Eine gängige Methode, diese Unterschiede auszugleichen, ist die sogenannte Hardware-Abstraktionsschicht (HAL). -] else if lang == "zh" [ +], +zh: [ 通过一个被叫做硬件抽象层或者*HAL*的层去均等化这种差异是一种常见的方法。 -] else { todo } +])) #quote(block: true)[ -#if lang == "en" [ +#tr(( +en: [ Hardware abstractions are sets of routines in software that emulate some platform-specific details, giving programs direct access to the hardware resources. -] else if lang == "de" [ +], +de: [ Hardware-Abstraktionen sind Sammlungen von Software-Routinen, die bestimmte plattformspezifische Details emulieren und Programmen so direkten Zugriff auf die Hardwareressourcen ermöglichen. -] else if lang == "zh" [ +], +zh: [ 在软件中硬件抽象是一组函数,其模仿了一些平台特定的细节,让程序可以直接访问硬件资源。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ They often allow programmers to write device-independent, high performance applications by providing standard operating system (OS) calls to hardware. -] else if lang == "de" [ +], +de: [ Sie ermöglichen es Programmierern häufig, geräteunabhängige Hochleistungsanwendungen zu schreiben, indem sie standardisierte Betriebssystemaufrufe für den Hardwarezugriff bereitstellen. -] else if lang == "zh" [ +], +zh: [ 通过向硬件提供标准的操作系统(OS)调用,它可以让程序员编写独立于设备的高性能应用。 -] else { todo } +])) #let url_hal = "https://en.wikipedia.org/wiki/Hardware_abstraction" -#if lang == ("en", "de", "zh") [ - _Wikipedia: #link(url_hal)[Hardware Abstraction Layer]_ -] else { todo } +#tr(( + en: [_Wikipedia: #link(url_hal)[Hardware Abstraction Layer]_], + de: [_Wikipedia: #link(url_hal)[Hardware Abstraction Layer]_], + zh: [_Wikipedia: #link(url_hal)[Hardware Abstraction Layer]_], +)) ] -#if lang == "en" [ +#tr(( +en: [ Embedded systems are a bit special in this regard since we typically do not have operating systems and user installable software but firmware images which are compiled as a whole as well as a number of other constraints. So while the traditional approach as defined by Wikipedia could potentially work it is likely not the most productive approach to ensure portability. -] else if lang == "de" [ +], +de: [ Eingebettete Systeme nehmen hier eine Sonderstellung ein, da wir es typischerweise nicht mit Betriebssystemen und vom Benutzer installierbarer Software zu tun haben, sondern mit Firmware-Images, die @@ -79,25 +96,31 @@ beschriebene Ansatz könnte zwar theoretisch funktionieren, ist aber wahrscheinlich nicht der effizienteste Weg, um Portabilität zu gewährleisten. -] else if lang == "zh" [ +], +zh: [ 在这方面,嵌入式系统有点特别,因为通常没有操作系统和用户可安装的软件,而只有固件镜像,其作为一个整体被编译且伴着许多约束。因此虽然维基百科定义的传统方法可能有用,但是它不是确保可移植性最有效的方法。 -] else { todo } +])) #let ln_hal = link("https://crates.io/crates/embedded-hal")[embedded-hal] -#if lang == "en" [ +#tr(( +en: [ How do we do this in Rust? Enter *#ln_hal*… -] else if lang == "de" [ +], +de: [ Wie gehen wir dabei in Rust vor? Hier kommt *#ln_hal* ins Spiel… -] else if lang == "zh" [ +], +zh: [ 在Rust中我们要怎么实现这个目标呢?让我们进入*embedded-hal*… -] else { todo } +])) -== #(if lang == "en" [What is #ln_hal?] - else if lang == "de" [Was ist #ln_hal?] - else if lang == "zh" [什么是embedded-hal?] - else { todo }) +== #tr(( + en: [What is #ln_hal?], + de: [Was ist #ln_hal?], + zh: [什么是embedded-hal?], +)) -#if lang == "en" [ +#tr(( +en: [ In a nutshell it is a set of traits which define implementation contracts between *HAL implementations*, *drivers* and *applications (or firmwares)*. Those contracts include both @@ -105,7 +128,8 @@ *HAL implementation* provides a certain capability) and methods (i.e.~if you can construct a type implementing a trait it is guaranteed that you have the methods specified in the trait available). -] else if lang == "de" [ +], +de: [ Kurz gesagt handelt es sich um eine Reihe von Merkmalen, die Implementierungsverträge zwischen *HAL-Implementierungen*, *Treibern* und *Anwendungen (oder Firmware)* definieren. @@ -115,25 +139,30 @@ Methoden (d.~h., wenn ein Typ, der ein Merkmal implementiert, erstellt werden kann, ist garantiert, dass die im Merkmal spezifizierten Methoden verfügbar sind). -] else if lang == "zh" [ +], +zh: [ 简而言之,它是一组traits,其定义了*HAL implementations*,*驱动*,*应用(或者固件)* 之间的实现约定(implementation contracts)。这些约定包括功能(即约定,如果为某个类型实现了某个trait,*HAL implementation*就提供了某个功能)和方法(即,如果构造一个实现了某个trait的类型,约定保障类型肯定有在trait中指定的方法)。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ A typical layering might look like this: -] else if lang == "de" [ +], +de: [ Ein typischer Schichtaufbau könnte wie folgt aussehen: -] else if lang == "zh" [ +], +zh: [ 典型的分层可能如下所示: -] else { todo } +])) #box(image("../assets/rust_layers.svg")) -#if lang == "en" [ +#tr(( +en: [ Some of the defined traits in *#ln_hal* are: - GPIO (input and output pins) - Serial communication @@ -141,7 +170,8 @@ - SPI - Timers/Countdowns - Analog Digital Conversion -] else if lang == "de" [ +], +de: [ Einige der in *#ln_hal* definierten Traits sind: - GPIO (Eingangs- und Ausgangspins) - Serielle Kommunikation @@ -149,7 +179,8 @@ - SPI - Timers/Countdowns - Analog-Digital-Umsetzung -] else if lang == "zh" [ +], +zh: [ 一些在*embedded-hal*中被定义的traits: - GPIO (input and output pins) - Serial communication @@ -157,9 +188,10 @@ - SPI - Timers/Countdowns - Analog Digital Conversion -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ The main reason for having the *embedded-hal* traits and crates implementing and using them is to keep complexity in check. If you consider that an application might have to implement the use of the @@ -173,7 +205,8 @@ traits will make the implementation complexity approach *M+N*. Of course there're additional benefits to be had, such as less trial-and-error due to a well-defined and ready-to-use APIs. -] else if lang == "de" [ +], +de: [ Der Hauptgrund für die Existenz der *embedded-hal*-Traits und der sie implementierenden sowie nutzenden Crates liegt darin, die Komplexität beherrschbar zu halten. Bedenkt man, dass eine Anwendung @@ -189,32 +222,40 @@ hingegen *M + N* an. Natürlich ergeben sich daraus weitere Vorteile, wie etwa ein geringerer Aufwand durch „Trial-and-Error" dank wohldefinierter und sofort einsatzbereiter APIs. -] else if lang == "zh" [ +], +zh: [ 使用*embedded-hal* traits和依赖*embedded-hal*的crates的主要原因是为了控制复杂性。如果发现一个应用可能必须要实现对硬件外设的使用,以及需要实现应用程序和其它硬件组件间潜在的驱动,那么其应该很容易被看作是可复用性有限的。用数学语言来说就是,如果*M*是外设HAL implementations的数量,*N*是驱动的数量,那么如果我们要为每个应用重新发明轮子我们最终会有*M\*N*个实现,然而通过使用*embedded-hal*的traits提供的 _API_ 将会使实现复杂性变成*M+N* 。当然还有其它好处,比如由于API定义良好,开箱即用,导致试错减少。 -] else { todo } +])) -== #(if lang == "en" [Users of the #ln_hal] - else if lang == "de" [Nutzer von #ln_hal] - else if lang == "zh" [embedded-hal的用户] - else { todo }) +== #tr(( + en: [Users of the #ln_hal], + de: [Nutzer von #ln_hal], + zh: [embedded-hal的用户], +)) -#if lang == "en" [ +#tr(( +en: [ As said above there are three main users of the HAL: -] else if lang == "de" [ +], +de: [ Wie bereits erwähnt, gibt es drei Hauptnutzer einer HAL: -] else if lang == "zh" [ +], +zh: [ 像上面所说的,HAL有三个主要用户: -] else { todo } +])) -=== #(if lang in ("en", "zh") [HAL implementation] - else if lang == "de" [HAL-Implementierung] - else { todo }) +=== #tr(( + en: [HAL implementation], + de: [HAL-Implementierung], + zh: [HAL implementation], +)) -#if lang == "en" [ +#tr(( +en: [ A HAL implementation provides the interfacing between the hardware and the users of the HAL traits. Typical implementations consist of three parts: @@ -223,7 +264,8 @@ often providing various configuration options (speed, operation mode, use pins, etc.) - one or more `trait` `impl` of *#ln_hal* traits for that type -] else if lang == "de" [ +], +de: [ Eine HAL-Implementierung stellt die Schnittstelle zwischen der Hardware und den Nutzern der HAL-Traits bereit. Typische Implementierungen bestehen aus drei Teilen: @@ -233,49 +275,56 @@ Betriebsmodus, verwendete Pins usw.) bereitstellen. - eine oder mehrere `trait`-Implementierungen (`impl`) von *#ln_hal*-Traits für diesen Typ -] else if lang == "zh" [ +], +zh: [ HAL implentation提供硬件和HAL traits的用户之间的接口。典型的实现由三部分组成: - 一个或者多个硬件特定的类型 - 生成和初始化这个类型的函数,函数经常提供不同的配置选项(速度,操作模式,使用的管脚,etc 。) - 与这个类型有关的一个或者多个 *embedded-hal* traits 的 `trait` `impl` -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Such a *HAL implementation* can come in various flavours: - Via low-level hardware access, e.g.~via registers - Via operating system, e.g.~by using the `sysfs` under Linux - Via adapter, e.g.~a mock of types for unit testing - Via driver for hardware adapters, e.g.~I2C multiplexer or GPIO expander -] else if lang == "de" [ +], +de: [ Eine solche *HAL-Implementierung* kann in verschiedenen Ausprägungen vorliegen: - Über hardwarenahen Zugriff, z. B. über Register - Über das Betriebssystem, z. B. durch Verwendung von `sysfs` unter Linux - Über einen Adapter, z. B. ein Mock von Typen für Unit-Tests - Via-Treiber für Hardware-Adapter, z. B. I2C-Multiplexer oder GPIO-Expander -] else if lang == "zh" [ +], +zh: [ 这样的一个 *HAL implementation* 可以有多个方法来实现: - 通过低级硬件访问,比如通过寄存器。 - 通过操作系统,比如通过使用Linux下的 `sysfs` - 通过适配器,比如一个与单元测试有关的类型的仿真 - 通过相关硬件适配器的驱动,e.g.~I2C多路复用器或者GPIO扩展器(I2C multiplexer or GPIO expander) -] else { todo } +])) -=== #(if lang == "en" [Driver] - else if lang == "de" [Treiber] - else if lang == "zh" [驱动] - else { todo }) +=== #tr(( + en: [Driver], + de: [Treiber], + zh: [驱动], +)) -#if lang == "en" [ +#tr(( +en: [ A driver implements a set of custom functionality for an internal or external component, connected to a peripheral implementing the #ln_hal traits. Typical examples for such drivers include various sensors (temperature, magnetometer, accelerometer, light), display devices (LED arrays, LCD displays) and actuators (motors, transmitters). -] else if lang == "de" [ +], +de: [ Ein Treiber implementiert eine Reihe benutzerdefinierter Funktionen für eine interne oder externe Komponente, die mit einem Peripheriegerät verbunden ist, das die #ln_hal;-Traits @@ -283,35 +332,41 @@ Sensoren (Temperatur-, Magnetometer-, Beschleunigungs- und Lichtsensoren), Anzeigegeräte (LED-Arrays, LCD-Displays) und Aktoren (Motoren, Sender). -] else if lang == "zh" [ +], +zh: [ 驱动为一个外部或者内部组件实现了一组自定义的功能,被连接到一个实现了embedded-hal traits的外设上。这种驱动的典型的例子包括多种传感器(温度计,磁力计,加速度计,光照计),显示设备(LED阵列,LCD显示屏)和执行器(电机,发送器)。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ A driver has to be initialized with an instance of type that implements a certain `trait` of the #ln_hal which is ensured via trait bound and provides its own type instance with a custom set of methods allowing to interact with the driven device. -] else if lang == "de" [ +], +de: [ Ein Treiber muss mit einer Instanz eines Typs initialisiert werden, der ein bestimmtes `Trait` von #ln_hal implementiert. Dies wird durch Trait-Bound sichergestellt. Der Treiber stellt eine eigene Typinstanz mit einem benutzerdefinierten Satz von Methoden bereit, die die Interaktion mit dem angesteuerten Gerät -] else if lang == "zh" [ +], +zh: [ 必须使用实现了embedded-hal的某个`trait`的类型的实例来初始化驱动,这是通过trait bound来确保的,驱动也提供了它自己的类型实例,这个实例具有一组自定义的方法,这些方法允许与被驱动的设备交互。 -] else { todo } +])) -=== #(if lang == "en" [Application] - else if lang == "de" [Anwendung] - else if lang == "zh" [应用] - else { todo }) +=== #tr(( + en: [Application], + de: [Anwendung], + zh: [应用], +)) -#if lang == "en" [ +#tr(( +en: [ The application binds the various parts together and ensures that the desired functionality is achieved. When porting between different systems, this is the part which requires the most adaptation efforts, @@ -323,7 +378,8 @@ configuration or there are different trade-offs to be made in the use of internal peripherals (e.g.~multiple timers with different capabilities are available or peripherals conflict with others). -] else if lang == "de" [ +], +de: [ Die Anwendung verknüpft die verschiedenen Komponenten miteinander und stellt sicher, dass die gewünschte Funktionalität erreicht wird. Bei der Portierung zwischen verschiedenen Systemen ist dies der Teil, der den @@ -337,7 +393,8 @@ Nutzung interner Peripherie getroffen werden (etwa wenn mehrere Timer mit unterschiedlichen Fähigkeiten zur Verfügung stehen oder Konflikte zwischen verschiedenen Peripherieeinheiten auftreten). -] else if lang == "zh" [ +], +zh: [ 应用把多个部分结合在一起并确保需要的功能被实现。当在不同的系统间移植时,这部分的适配是花费最多精力的地方,因为应用需要通过HAL implementation正确地初始化真实的硬件,而且不同硬件的初始化也不相同,甚至有时候差别非常大。用户的选择也在其中扮演了非常重大的角色,因为组件能被物理连接到不同的端口,硬件总线有时候需要外部硬件去匹配配置,或者用户在内部外设的使用上有不同的考量。 -] else { todo } +])) diff --git a/typ/start/exceptions.typ b/typ/start/exceptions.typ index f2dc0cac..b59eb535 100644 --- a/typ/start/exceptions.typ +++ b/typ/start/exceptions.typ @@ -1,123 +1,135 @@ #import "../config.typ": * -#h1(offset: whole, - if lang == "en" [Exceptions] - else if lang == "de" [Ausnahmen (Exceptions)] - else if lang == "ja" [例外] - else if lang == "zh" [异常] - else { todo }) +#h1(offset: whole, tr(( + en: [Exceptions], + de: [Ausnahmen (Exceptions)], + ja: [例外], + zh: [异常], +))) -#if lang == "en" [ +#tr(( +en: [ Exceptions, and interrupts, are a hardware mechanism by which the processor handles asynchronous events and fatal errors (e.g.~executing an invalid instruction). Exceptions imply preemption and involve exception handlers, subroutines executed in response to the signal that triggered the event. -] else if lang == "de" [ +], +de: [ Ausnahmen und Interrupts sind ein Hardwaremechanismus, mit dem der Prozessor asynchrone Ereignisse und schwerwiegende Fehler (z. B. die Ausführung einer ungültigen Anweisung) behandelt. Ausnahmen implizieren Präemption und erfordern Ausnahmebehandlungsroutinen, die als Reaktion auf das auslösende Signal ausgeführt werden. -] else if lang == "ja" [ +], +ja: [ 例外と割り込みは、プロセッサが非同期イベントと致命的なエラー(例えば、不正な命令の実行)を扱うためのハードウェアの仕組みです。 例外はプリエンプションを意味し、例外ハンドラを呼び出します。例外ハンドラは、イベントを引き起こした信号に応答して実行されるサブルーチンです。 -] else if lang == "zh" [ +], +zh: [ 异常和中断,是处理器用来处理异步事件和致命错误(e.g.~执行一个无效的指令)的一种硬件机制。异常意味着抢占并涉及到异常处理程序,即响应触发事件的信号的子程序。 -] else { todo } +])) #let ln_ex = link("https://docs.rs/cortex-m-rt-macros/latest/cortex_m_rt_macros/attr.exception.html")[`exception`] -#if lang == "en" [ +#tr(( +en: [ The `cortex-m-rt` crate provides an #ln_ex attribute to declare exception handlers. -] else if lang == "de" [ +], +de: [ Die `cortex-m-rt`-Crate bietet das Attribut #ln_ex zur Deklaration von Ausnahmebehandlungsroutinen. -] else if lang == "ja" [ +], +ja: [ `cortex-m-rt`クレートは、例外ハンドラを宣言するために、#ln_ex;アトリビュートを提供しています。 -] else if lang == "zh" [ +], +zh: [ `cortex-m-rt` crate提供了一个 #ln_ex 属性去声明异常处理程序。 -] else { todo } +])) #raw(block: true, lang: "rust", -"// " + if lang == "en" { - "Exception handler for the SysTick (System Timer) exception" - } else if lang == "de" { - "Ausnahmebehandlungsroutine fuer die SysTick-Ausnahme (System-Timer)" - } else if lang == "ja" { - "SysTick(システムタイマ)例外のための例外ハンドラ" - } else if lang == "zh" { - "SysTick (System计时器)异常的异常处理函数" - } else { todos } + " +"// " + ts(( + en: "Exception handler for the SysTick (System Timer) exception", + de: "Ausnahmebehandlungsroutine fuer die SysTick-Ausnahme (System-Timer)", + ja: "SysTick(システムタイマ)例外のための例外ハンドラ", + zh: "SysTick (System计时器)异常的异常处理函数", + )) + " #[exception] fn SysTick() { // .. } ") -#if lang == "en" [ +#tr(( +en: [ Other than the `exception` attribute exception handlers look like plain functions but there's one more difference: `exception` handlers can _not_ be called by software. Following the previous example, the statement `SysTick();` would result in a compilation error. -] else if lang == "de" [ +], +de: [ Abgesehen vom `exception`-Attribut sehen Exception-Handler wie gewöhnliche Funktionen aus, doch es gibt noch einen weiteren Unterschied: `exception`-Handler können _nicht_ per Software aufgerufen werden. Bezogen auf das vorherige Beispiel würde die Anweisung `SysTick();` zu einem Kompilierfehler führen. -] else if lang == "ja" [ +], +ja: [ `exception`属性の他は、例外ハンドラは普通の関数のように見えます。しかし、もう1つ違いがあります。 `exception`ハンドラはソフトウェアから呼び出すことが_できません_。前述の例では、`SysTick();`というステートメントは、 コンパイルエラーになります。 -] else if lang == "zh" [ +], +zh: [ 除了 `exception` 属性,异常处理函数看起来和普通函数一样,但是有一个很大的不同: `exception` 处理函数 _不能_ 被软件调用。在先前的例子中,语句 `SysTick();` 将会导致一个编译错误。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ This behavior is pretty much intended and it's required to provide a feature: `static mut` variables declared _inside_ `exception` handlers are _safe_ to use. -] else if lang == "de" [ +], +de: [ Dieses Verhalten ist durchaus beabsichtigt und notwendig, um eine bestimmte Eigenschaft zu gewährleisten: `static mut`-Variablen, die _innerhalb_ von `exception`-Handlern deklariert werden, sind _sicher_ in der Verwendung. -] else if lang == "ja" [ +], +ja: [ この動作は、非常に意図的なものです。 これは`exception`ハンドラ_内_で宣言された`static mut`変数の利用を_安全_にする、という機能を提供するためのものです。 -] else if lang == "zh" [ +], +zh: [ 这么做是有目的的,因为异常处理函数必须具有一个特性: 在异常处理函数中被声明为`static mut`的变量能被安全(safe)地使用。 -] else { todo } +])) #raw(block: true, lang: "rust", "#[exception] fn SysTick() { static mut COUNT: u32 = 0; - // " + if lang == "en" { - "`COUNT` has transformed to type `&mut u32` and it's safe to use" - } else if lang == "de" { - "`COUNT` wurde in den Typ `&mut u32` umgewandelt und ist sicher in der - // Verwendung." - } else if lang == "ja" { - "`COUNT`は`&mut u32`の型をもっており、その利用は安全です" - } else if lang == "zh" { - "`COUNT` 被转换到了 `&mut u32` 类型且它用起来是安全的" - } else { todos } + " + // " + ts(( + en: "`COUNT` has transformed to type `&mut u32` and it's safe to use", + de: "`COUNT` wurde in den Typ `&mut u32` umgewandelt und ist sicher in der + // Verwendung.", + ja: "`COUNT`は`&mut u32`の型をもっており、その利用は安全です", + zh: "`COUNT` 被转换到了 `&mut u32` 类型且它用起来是安全的", + )) + " *COUNT += 1; ") #let url_re = "https://en.wikipedia.org/wiki/Reentrancy_(computing)" -#if lang == "en" [ +#tr(( +en: [ As you may know, using `static mut` variables in a function makes it #link(url_re)[_non-reentrant_]. It's undefined behavior to call a non-reentrant function, directly or indirectly, from more than one exception / interrupt handler or from `main` and one or more exception / interrupt handlers. -] else if lang == "de" [ +], +de: [ Wie Ihnen vielleicht bekannt ist, führt die Verwendung von `static mut`-Variablen in einer Funktion dazu, dass diese #link(url_re)[_nicht wiedereintrittsfähig_] @@ -125,22 +137,26 @@ fn SysTick() { wiedereintrittsfähige Funktion direkt oder indirekt aus mehr als einem Ausnahme- oder Interrupt-Handler oder aus `main` und einem oder mehreren Ausnahme- oder Interrupt-Handlern heraus aufzurufen. -] else if lang == "ja" [ +], +ja: [ ご存知かもしれませんが、`static mut`変数を関数内で使うことは、その関数を_再入不可能_にします。 直接的または間接的に、複数の例外・割り込みハンドラから、もしくは、`main`と1つ以上の例外・割り込みハンドラから、 再進入不可能な関数を呼び出すことは、未定義動作です。 -] else if lang == "zh" [ +], +zh: [ 就像你可能已经知道的那样,在一个函数里使用`static mut`变量,会让函数变成#link(url_re)[_非可重入函数(non-reentrancy)_]。从多个异常/中断处理函数,或者从`main`函数和多个异常/中断处理函数中,直接或者间接地调用一个非可重入(non-reentrancy)函数是未定义的行为。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Safe Rust must never result in undefined behavior so non-reentrant functions must be marked as `unsafe`. Yet I just told that `exception` handlers can safely use `static mut` variables. How is this possible? This is possible because `exception` handlers can _not_ be called by software thus reentrancy is not possible. These handlers are called by the hardware itself which is assumed to be physically non-concurrent. -] else if lang == "de" [ +], +de: [ Safe Rust darf niemals zu undefiniertem Verhalten führen; daher müssen nicht-wiedereintrittsfähige Funktionen als `unsafe` gekennzeichnet werden. Dennoch habe ich gerade erwähnt, dass Ausnahme-Handler @@ -150,31 +166,36 @@ fn SysTick() { (Reentrancy) stattfinden kann. Diese Handler werden von der Hardware selbst aufgerufen, bei der davon ausgegangen wird, dass sie physisch keine Nebenläufigkeit aufweist. -] else if lang == "ja" [ +], +ja: [ #todoupd("ja") 安全なRustは、決して未定義動作になりません。そのため、再入不可能な関数は、`unsafe`とマークされなければなりません。 それでも、`exception`ハンドラは`static mut`な変数を安全に使える、と述べました。これが可能なのは、どうしてでしょうか。 `exception`ハンドラはソフトウェアから呼び出すことが_できない_ため、再入する可能性はありません。だから、安全に使えるのです。 -] else if lang == "zh" [ +], +zh: [ #todoupd("zh") 安全的Rust不能导致未定义的行为出现,所以非可重入函数必须被标记为 `unsafe`。然而,我刚说了`exception`处理函数能安全地使用`static mut`变量。这怎么可能?因为`exception`处理函数 _不_ 能被软件调用因此重入(reentrancy)不会发生,所以这才变得可能。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ As a result, in the context of exception handlers in embedded systems, the absence of concurrent invocations of the same handler ensures that there are no reentrancy issues, even if the handler uses static mutable variables. -] else if lang == "de" [ +], +de: [ Im Kontext von Ausnahme-Handlern in eingebetteten Systemen stellt das Ausbleiben gleichzeitiger Aufrufe desselben Handlers folglich sicher, dass keine Probleme mit der Wiedereintrittsfähigkeit auftreten -- selbst dann nicht, wenn der Handler veränderbare statische Variablen verwendet -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ In a multicore system, where multiple processor cores are executing code concurrently, the potential for reentrancy issues becomes relevant again, even within exception handlers. While each core may have its own @@ -185,7 +206,8 @@ fn SysTick() { that access to shared resources is properly coordinated among the cores. This typically involves the use of techniques such as locks, semaphores, or atomic operations to prevent data races and maintain data integrity -] else if lang == "de" [ +], +de: [ In einem Multicore-System, in dem mehrere Prozessorkerne gleichzeitig Code ausführen, gewinnt die Problematik der Wiedereintrittsfähigkeit (Reentrancy) erneut an Bedeutung -- selbst innerhalb von @@ -199,16 +221,18 @@ fn SysTick() { wird. Dies geschieht typischerweise durch den Einsatz von Techniken wie Locks, Semaphoren oder atomaren Operationen, um Race Conditions zu vermeiden und die Datenintegrität zu wahren. -] else { todo } +])) #quote(block: true)[ -#if lang == "en" [ +#tr(( +en: [ Note that the `exception` attribute transforms definitions of static variables inside the function by wrapping them into `unsafe` blocks and providing us with new appropriate variables of type `&mut` of the same name. Thus we can dereference the reference via `*` to access the values of the variables without needing to wrap them in an `unsafe` block. -] else if lang == "de" [ +], +de: [ Beachten Sie, dass das Attribut `exception` Definitionen statischer Variablen innerhalb der Funktion umwandelt, indem es sie in `unsafe`-Blöcke einschließt und uns neue, passende Variablen des Typs @@ -216,54 +240,64 @@ fn SysTick() { wir die Referenz mittels `*` dereferenzieren, um auf die Werte der Variablen zuzugreifen, ohne sie selbst in einen `unsafe`-Block einschließen zu müssen. -] else if lang == "zh" [ +], +zh: [ 注意,`exception`属性,通过将静态变量封装进`unsafe`块中并为我们提供了名字相同的,类型为 `&mut` 的,合适的新变量,转换了函数中静态变量的定义。因此我们可以通过 `*` 解引用访问变量的值而不需要将它们打包进一个 `unsafe` 块中。 -] else { todo } +])) ] -= #(if lang == "en" [A complete example] - else if lang == "de" [Ein vollständiges Beispiel] - else if lang == "ja" [完全な例] - else if lang == "zh" [一个完整的例子] - else { todo }) += #tr(( + en: [A complete example], + de: [Ein vollständiges Beispiel], + ja: [完全な例], + zh: [一个完整的例子], +)) -#if lang == "en" [ +#tr(( +en: [ Here's an example that uses the system timer to raise a `SysTick` exception roughly every second. The `SysTick` exception handler keeps track of how many times it has been called in the `COUNT` variable and then prints the value of `COUNT` to the host console using semihosting. of the variables without needing to wrap them in an `unsafe` block. -] else if lang == "de" [ +], +de: [ Hier ist ein Beispiel, das den System-Timer verwendet, um etwa jede Sekunde eine `SysTick`-Exception auszulösen. Der `SysTick`-Exception-Handler protokolliert in der Variablen `COUNT`, wie oft er aufgerufen wurde, und gibt anschließend den Wert von `COUNT` mittels Semihosting auf der Host-Konsole aus. -] else if lang == "ja" [ +], +ja: [ `SysTick`例外を大体1秒毎に発生させるシステムタイマの例を使います。 `SysTick`例外ハンドラは、呼び出された回数を`COUNT`変数に記録し、 セミホスティングを使ってホストコンソールに`COUNT`の値を出力します。 -] else if lang == "zh" [ +], +zh: [ 这里有个例子,使用系统计时器大概每秒抛出一个 `SysTick` 异常。异常处理函数使用 `COUNT` 变量追踪它自己被调用了多少次,然后使用半主机模式(semihosting)打印 `COUNT` 的值到主机控制台上。 -] else { todo } +])) #quote(block: true)[ -#if lang == "en" [ +#tr(( +en: [ *NOTE*: You can run this example on any Cortex-M device; you can also run it on QEMU -] else if lang == "de" [ +], +de: [ *HINWEIS*: Sie können dieses Beispiel auf jedem Cortex-M-Gerät ausführen; es lässt sich auch unter QEMU ausführen. -] else if lang == "ja" [ +], +ja: [ *注記*:この例は、どのCortex-Mデバイスでも実行できます。QEMU上でも実行可能です。 -] else if lang == "zh" [ +], +zh: [ *注意*: 你能在任何Cortex-M设备上运行这个例子;你也能在QEMU运行它。 -] else { todo } +])) ] #raw(block: true, lang: "rust", @@ -287,27 +321,21 @@ fn main() -> ! { let p = cortex_m::Peripherals::take().unwrap(); let mut syst = p.SYST; - // " + if lang == "en" { - "configures the system timer to trigger a SysTick exception every second" - } else if lang == "de" { - "konfiguriert den System-Timer so, dass er jede Sekunde eine SysTick- - // Exception ausloest" - } else if lang == "ja" { - "毎秒SysTick例外を起こすためのシステムタイマを設定します" - } else if lang == "zh" { - "配置系统的计时器每秒去触发一个SysTick异常" - } else { todos } + " + // " + ts(( + en: "configures the system timer to trigger a SysTick exception every second", + de: "konfiguriert den System-Timer so, dass er jede Sekunde eine SysTick- + // Exception ausloest", + ja: "毎秒SysTick例外を起こすためのシステムタイマを設定します", + zh: "配置系统的计时器每秒去触发一个SysTick异常", + )) + " syst.set_clock_source(SystClkSource::Core); - // " + if lang == "en" { - "this is configured for the LM3S6965 which has a default CPU clock of 12 MHz" - } else if lang == "de" { - "Dies ist für den LM3S6965 konfiguriert, der einen Standard-CPU-Takt von - // 12 MHz hat" - } else if lang == "ja" { - "デフォルトのCPUクロックが12MHzのLM3S6965向けの設定です" - } else if lang == "zh" { - "这是关于LM3S6965的配置,其有一个12MHz的默认CPU时钟" - } else { todos } + " + // " + ts(( + en: "this is configured for the LM3S6965 which has a default CPU clock of 12 MHz", + de: "Dies ist für den LM3S6965 konfiguriert, der einen Standard-CPU-Takt von + // 12 MHz hat", + ja: "デフォルトのCPUクロックが12MHzのLM3S6965向けの設定です", + zh: "这是关于LM3S6965的配置,其有一个12MHz的默认CPU时钟", + )) + " syst.set_reload(12_000_000); syst.clear_current(); syst.enable_counter(); @@ -323,15 +351,12 @@ fn SysTick() { *COUNT += 1; - // " + if lang == "en" { - "Lazy initialization" - } else if lang == "de" { - "Verzoegerte Initialisierung" - } else if lang == "ja" { - "遅延初期化" - } else if lang == "zh" { - "惰性初始化(Lazy initialization)" - } else { todos } + " + // " + ts(( + en: "Lazy initialization", + de: "Verzoegerte Initialisierung", + ja: "遅延初期化", + zh: "惰性初始化(Lazy initialization)", + )) + " if STDOUT.is_none() { *STDOUT = hio::hstdout().ok(); } @@ -340,30 +365,24 @@ fn SysTick() { write!(hstdout, \"{}\", *COUNT).ok(); } - // " + if lang == "en" { - "IMPORTANT omit this `if` block if running on real hardware or your - // debugger will end in an inconsistent state" - } else if lang == "de" { - "WICHTIG: Lassen Sie diesen `if`-Block weg, wenn Sie das Programm auf + // " + ts(( + en: "IMPORTANT omit this `if` block if running on real hardware or your + // debugger will end in an inconsistent state", + de: "WICHTIG: Lassen Sie diesen `if`-Block weg, wenn Sie das Programm auf // echter Hardware ausfuehren, da sich Ihr Debugger sonst in - // einem inkonsistenten Zustand befinden wird." - } else if lang == "ja" { - "重要。実際のハードウェアで実行するときは`if`ブロックを削除して下さい。そうでなければ、 - // デバッガが不整合な状態に陥るでしょう。" - } else if lang == "zh" { - "重要信息 如果运行在真正的硬件上,去掉这个 `if` 块, + // einem inkonsistenten Zustand befinden wird.", + ja: "重要。実際のハードウェアで実行するときは`if`ブロックを削除して下さい。そうでなければ、 + // デバッガが不整合な状態に陥るでしょう。", + zh: "重要信息 如果运行在真正的硬件上,去掉这个 `if` 块, // 否则你的调试器将会以一种不一致的状态结束" - } else { todos } + " + )) + " if *COUNT == 9 { - // " + if lang == "en" { - "This will terminate the QEMU process" - } else if lang == "de" { - "Dies beendet den QEMU-Prozess." - } else if lang == "ja" { - "QEMUプロセスを終了します" - } else if lang == "zh" { - "这将终结QEMU进程" - } else { todos } + " + // " + ts(( + en: "This will terminate the QEMU process", + de: "Dies beendet den QEMU-Prozess.", + ja: "QEMUプロセスを終了します", + zh: "这将终结QEMU进程", + )) + " debug::exit(debug::EXIT_SUCCESS); } } @@ -387,47 +406,56 @@ $ cargo run --release 123456789 ``` -#if lang == "en" [ +#tr(( +en: [ If you run this on the Discovery board you'll see the output on the OpenOCD console. Also, the program will _not_ stop when the count reaches 9. -] else if lang == "de" [ +], +de: [ Wenn Sie dies auf dem Discovery-Board ausführen, sehen Sie die Ausgabe in der OpenOCD-Konsole. Außerdem hält das Programm _nicht_ an, wenn der Zählerstand 9 erreicht. -] else if lang == "ja" [ +], +ja: [ Discoveryボードでこのコードを実行すると、OpenOCDコンソールに出力を確認できるでしょう。 プログラムは、カウントが9に到達しても停止_しません_。 -] else if lang == "zh" [ +], +zh: [ 如果你在Discovery开发板上运行这个例子,你将会在OpenOCD控制台上看到输出。还有,当计数到达9的时候,程序将 _会_ 停止。 -] else { todo } +])) -= #(if lang == "en" [The default exception handler] - else if lang == "de" [Der Standard-Exception-Handler] - else if lang == "ja" [デフォルト例外ハンドラ] - else if lang == "zh" [默认异常处理函数] - else { todo }) += #tr(( + en: [The default exception handler], + de: [Der Standard-Exception-Handler], + ja: [デフォルト例外ハンドラ], + zh: [默认异常处理函数], +)) -#if lang == "en" [ +#tr(( +en: [ What the `exception` attribute actually does is _override_ the default exception handler for a specific exception. If you don't override the handler for a particular exception it will be handled by the `DefaultHandler` function, which defaults to: -] else if lang == "de" [ +], +de: [ Das `exception`-Attribut bewirkt eigentlich, dass der Standard-Exception-Handler für eine bestimmte Exception _überschrieben_ wird. Wenn Sie den Handler für eine bestimmte Exception nicht überschreiben, wird sie von der Funktion `DefaultHandler` behandelt, die standardmäßig Folgendes tut: -] else if lang == "ja" [ +], +ja: [ `exception`アトリビュートが実際に行っていることは、特定の例外を処理するデフォルト例外ハンドラの_オーバーライド_です。 特定の例外について、ハンドラをオーバーライドしない場合、`DefaultHandler`関数がその例外を処理します。 DefaultHandler関数は下記の通りです。 -] else if lang == "zh" [ +], +zh: [ `exception` 属性真正做的是,_覆盖_ 了一个特定异常的默认异常处理函数。如果你不覆盖一个特定异常的处理函数,它将会被 `DefaultHandler` 函数处理,其默认的是: -] else { todo } +])) ```rust fn DefaultHandler() { @@ -435,151 +463,177 @@ fn DefaultHandler() { } ``` -#if lang == "en" [ +#tr(( +en: [ This function is provided by the `cortex-m-rt` crate and marked as `#[no_mangle]` so you can put a breakpoint on "DefaultHandler" and catch _unhandled_ exceptions. -] else if lang == "de" [ +], +de: [ Diese Funktion wird vom `cortex-m-rt`-Crate bereitgestellt und ist mit `#[no_mangle]` markiert, sodass Sie einen Breakpoint auf „DefaultHandler" setzen und _unbehandelte_ Exceptions abfangen können. -] else if lang == "ja" [ +], +ja: [ この関数は、`cortex-m-rt`クレートによって提供されており、`#[no_mangle]`とマークされています。 そのため、「DefaultHandler」にブレイクポイントを設定することができ、_未処理の_例外を捕捉することができます。 -] else if lang == "zh" [ +], +zh: [ 这个函数是 `cortex-m-rt` crate提供的,且被标记为 `#[no_mangle]` 因此你能在 "DefaultHandler" 上放置一个断点并捕获 _unhandled_ 异常。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ It's possible to override this `DefaultHandler` using the `exception` attribute: -] else if lang == "de" [ +], +de: [ Es ist möglich, diesen `DefaultHandler` mithilfe des `exception`-Attributs zu überschreiben: -] else if lang == "ja" [ +], +ja: [ `exception`アトリビュートを使うことで、`DefaultHandler`をオーバーライドできます。 -] else if lang == "zh" [ +], +zh: [ 可以使用 `exception` 属性覆盖这个 `DefaultHandler`: -] else { todo } +])) #raw(block: true, lang: "rust", "#[exception] fn DefaultHandler(irqn: i16) { - // " + if lang == "en" { - "custom default handler" - } else if lang == "de" { - "benutzerdefinierter Standard-Handler" - } else if lang == "ja" { - "カスタムデフォルトハンドラ" - } else if lang == "zh" { - "自定义默认处理函数" - } else { todos } + " + // " + ts(( + en: "custom default handler", + de: "benutzerdefinierter Standard-Handler", + ja: "カスタムデフォルトハンドラ", + zh: "自定义默认处理函数", + )) + " } ") -#if lang == "en" [ +#tr(( +en: [ The `irqn` argument indicates which exception is being serviced. A negative value indicates that a Cortex-M exception is being serviced; and zero or a positive value indicate that a device specific exception, AKA interrupt, is being serviced. -] else if lang == "de" [ +], +de: [ Das Argument `irqn` gibt an, welche Exception gerade bearbeitet wird. Ein negativer Wert zeigt an, dass eine Cortex-M-Exception bearbeitet wird, während ein Wert von null oder ein positiver Wert darauf hinweist, dass eine gerätespezifische Exception -- auch als Interrupt bezeichnet -- bearbeitet wird. -] else if lang == "ja" [ +], +ja: [ `irqn`引数は、どの例外が処理されているかを示します。負の値は、Cortex-Mの例外が処理されていることを意味します。 ゼロまたは正の値は、デバイス固有の例外、すなわち、割り込みが処理されていること、を示しています。 -] else if lang == "zh" [ +], +zh: [ `irqn` 参数指出了被服务的是哪个异常。一个负数值指出了被服务的是一个Cortex-M异常;0或者一个正数值指出了被服务的是一个设备特定的异常,也就是中断。 -] else { todo } +])) -= #(if lang == "en" [The hard fault handler] - else if lang == "de" [Der "Schwere Fehler"-Handler] - else if lang == "ja" [ハードフォールトハンドラ] - else if lang == "zh" [硬错误(Hard Fault)处理函数] - else { todo }) += #tr(( + en: [The hard fault handler], + de: [Der "Schwere Fehler"-Handler], + ja: [ハードフォールトハンドラ], + zh: [硬错误(Hard Fault)处理函数], +)) -#if lang == "en" [ +#tr(( +en: [ The `HardFault` exception is a bit special. This exception is fired when the program enters an invalid state so its handler can _not_ return as that could result in undefined behavior. Also, the runtime crate does a bit of work before the user defined `HardFault` handler is invoked to improve debuggability. -] else if lang == "de" [ +], +de: [ Die `HardFault`-Exception nimmt eine Sonderstellung ein. Sie wird ausgelöst, wenn das Programm in einen ungültigen Zustand gerät; daher darf ihr Handler nicht zurückkehren, da dies zu undefiniertem Verhalten führen könnte. Zudem führt die Runtime-Crate einige Vorarbeiten durch, bevor der benutzerdefinierte `HardFault`-Handler aufgerufen wird, um die Fehlersuche (Debugging) zu erleichtern. -] else if lang == "ja" [ +], +ja: [ `HardFault`例外は、少し特別です。この例外は、プログラムが不正な状態になった場合に発生します。 そのため、このハンドラはリターンすることができず、未定義動作を引き起こす可能性があります。 ランタイムクレートは、デバッグ性を向上するために、ユーザ定義の`HardFault`ハンドラが呼び出される前に、少し仕事をします。 -] else if lang == "zh" [ +], +zh: [ `HardFault`异常有点特别。当程序进入一个无法工作的状态时,这个异常被触发,因此它的处理函数 _不能_ 返回,因为这么做可能导致一个未定义的行为。在用户定义的 `HardFault` 处理函数被调用之前,运行时crate还做了一些工作以改进调试功能。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ The result is that the `HardFault` handler must have the following signature: `fn(&ExceptionFrame) -> !`. The argument of the handler is a pointer to registers that were pushed into the stack by the exception. These registers are a snapshot of the processor state at the moment the exception was triggered and are useful to diagnose a hard fault. -] else if lang == "de" [ +], +de: [ Daraus ergibt sich, dass der `HardFault`-Handler folgende Signatur aufweisen muss: `fn(&ExceptionFrame) -> !`. Das Argument des Handlers ist ein Zeiger auf Register, die beim Auftreten der Exception auf den Stack gesichert wurden. Diese Register stellen eine Momentaufnahme des Prozessorzustands zum Zeitpunkt der Auslösung der Exception dar und sind für die Diagnose eines Hard Faults hilfreich. -] else if lang == "ja" [ +], +ja: [ その結果、`HardFault`ハンドラは、`fn(&ExceptionFrame) -> !`のシグネチャを持つ必要があります。 ハンドラの引数は、例外によってスタックにプッシュされたレジスタへのポインタです。 これらのレジスタは、例外が発生した瞬間のプロセッサステートのスナップショットで、ハードフォールトの原因を突き止めるのに便利です。 -] else if lang == "zh" [ +], +zh: [ 结果是,`HardFault`处理函数必须有下列的签名: `fn(&ExceptionFrame) -> !` 。处理函数的参数是一个指针,它指向被异常推入栈中的寄存器。这些寄存器是异常被触发那刻,处理器状态的一个记录,能被用来分析一个硬错误。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Here's an example that performs an illegal operation: a read to a nonexistent memory location. -] else if lang == "de" [ +], +de: [ Hier ist ein Beispiel für eine unzulässige Operation: der Lesezugriff auf eine nicht existierende Speicheradresse. -] else if lang == "ja" [ +], +ja: [ 不正な操作を行う例を示します。存在しないメモリ位置への読み込みです。 -] else if lang == "zh" [ +], +zh: [ 这里有个执行不合法操作的案例: 读取一个不存在的存储位置。 -] else { todo } +])) #quote(block: true)[ -#if lang == "en" [ +#tr(( +en: [ *NOTE*: This program won't work, i.e.~it won't crash, on QEMU because `qemu-system-arm -machine lm3s6965evb` doesn't check memory loads and will happily return `0`on reads to invalid memory. -] else if lang == "de" [ +], +de: [ *HINWEIS*: Dieses Programm wird unter QEMU nicht fehlschlagen (d.~h. es stürzt nicht ab), da `qemu-system-arm -machine lm3s6965evb` keine Überprüfung von Speicherzugriffen durchführt und beim Lesen von ungültigem Speicher problemlos den Wert `0` zurückgibt. -] else if lang == "ja" [ +], +ja: [ *注記*:このプログラムは、QEMU上ではうまく動きません。つまり、クラッシュしません。 `qemu-system-arm -machine lm3s6965evb`はメモリの読み込みをチェックしないため、 無効なメモリを読み込むと、幸いにも、`0`を返します。 -] else if lang == "zh" [ +], +zh: [ *注意*: 这个程序在QEMU上不能起作用,i.e.~它不会崩溃,因为 `qemu-system-arm -machine lm3s6965evb` 不对读取存储的操作进行检查,且读取无效存储时将会开心地返回 `0`。 -] else { todo } +])) ] @@ -597,15 +651,12 @@ use cortex_m_semihosting::hio; #[entry] fn main() -> ! { - // " + if lang == "en" { - "read a nonexistent memory location" - } else if lang == "de" { - "liest einen nicht existierenden Speicherbereich" - } else if lang == "ja" { - "存在しないメモリ位置を読み込みます" - } else if lang == "zh" { - "读取一个无效的存储位置" - } else { todos } + " + // " + ts(( + en: "read a nonexistent memory location", + de: "liest einen nicht existierenden Speicherbereich", + ja: "存在しないメモリ位置を読み込みます", + zh: "读取一个无效的存储位置", + )) + " unsafe { ptr::read_volatile(0x3FFF_0000 as *const u32); } @@ -623,18 +674,22 @@ fn HardFault(ef: &ExceptionFrame) -> ! { } ") -#if lang == "en" [ +#tr(( +en: [ The `HardFault` handler prints the `ExceptionFrame` value. If you run this you'll see something like this on the OpenOCD console. -] else if lang == "de" [ +], +de: [ Der `HardFault`-Handler gibt den Wert des `ExceptionFrame` aus. Wenn Sie dies ausführen, sehen Sie auf der OpenOCD-Konsole eine Ausgabe wie diese. -] else if lang == "ja" [ +], +ja: [ `HardFault`ハンドラは、`ExceptionFrame`の値を表示します。実行すると、 OpenOCDコンソールに次のような表示が見えるでしょう。 -] else if lang == "zh" [ +], +zh: [ `HardFault`处理函数打印了`ExceptionFrame`值。如果你运行这个,你将会看到下面的东西打印到OpenOCD控制台上。 -] else { todo } +])) ```text $ openocd @@ -651,27 +706,35 @@ ExceptionFrame { } ``` -#if lang == "en" [ +#tr(( +en: [ The `pc` value is the value of the Program Counter at the time of the exception and it points to the instruction that triggered the exception. -] else if lang == "de" [ +], +de: [ Der Wert `pc` ist der Wert des Programmzählers zum Zeitpunkt der Ausnahme und verweist auf die Anweisung, die die Ausnahme ausgelöst hat. -] else if lang == "ja" [ +], +ja: [ `pc`の値は、例外発生時のプログラムカウンタの値で、例外を引き起こした命令を指しています。 -] else if lang == "zh" [ +], +zh: [ `pc`值是异常时程序计数器(Program Counter)的值,它指向触发了异常的指令。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ If you look at the disassembly of the program: -] else if lang == "de" [ +], +de: [ Wenn Sie sich den Disassembler des Programms ansehen: -] else if lang == "ja" [ +], +ja: [ プログラムのディスアセンブル結果を見ます。 -] else if lang == "zh" [ +], +zh: [ 如果你看向程序的反汇编: -] else { todo } +])) ```text $ cargo objdump --bin app --release -- -d --no-show-raw-insn --print-imm-hex @@ -683,12 +746,14 @@ ResetTrampoline: 800094c: b #-0x4 ``` -#if lang == "en" [ +#tr(( +en: [ You can lookup the value of the program counter `0x0800094a` in the disassembly. You'll see that a load operation (`ldr r0, [r0]` ) caused the exception. The `r0` field of `ExceptionFrame` will tell you the value of register `r0` was `0x3fff_fffe` at that time. -] else if lang == "de" [ +], +de: [ Den Wert des Programmzählers `0x0800094a` können Sie in der Disassemblierung nachschlagen. @@ -697,11 +762,13 @@ ResetTrampoline: Das Feld `r0` von `ExceptionFrame` gibt an, dass der Wert des Registers `r0` zu diesem Zeitpunkt `0x3fff_fffe` war. -] else if lang == "ja" [ +], +ja: [ #todoupd("ja") ロード命令(`ldr r0, [r0]`)が例外を発生させたことがわかります。そして、この時の`r0`レジスタの値は、 `0x3fff_fffe`です。この値は、`ExceptionFrame`の`r0`フィールドと一致します。 -] else if lang == "zh" [ +], +zh: [ 你可以在反汇编中搜索程序计数器`0x0800094a`的值。你将会看到一个读取操作(`ldr r0, [r0]`)导致了异常。`ExceptionFrame`的`r0`字段将告诉你,那时寄存器`r0`的值是`0x3fff_fffe` 。 -] else { todo } +])) diff --git a/typ/start/hardware.typ b/typ/start/hardware.typ index ab861bfa..81a2fe53 100644 --- a/typ/start/hardware.typ +++ b/typ/start/hardware.typ @@ -1,34 +1,41 @@ #import "../config.typ": * -#h1(offset: whole, - if lang in ("en", "de") [Hardware] - else if lang == "ja" [ハードウェア] - else if lang == "zh" [硬件] - else { todo }) +#h1(tr(( + en: [Hardware], + de: [Hardware], + ja: [ハードウェア], + zh: [硬件], +))) -#if lang == "en" [ +#tr(( +en: [ By now you should be somewhat familiar with the tooling and the development process. In this section we'll switch to real hardware; the process will remain largely the same. Let's dive in. -] else if lang == "de" [ +], +de: [ Mittlerweile sollten Sie mit den Werkzeugen und dem Entwicklungsprozess einigermaßen vertraut sein. In diesem Abschnitt wechseln wir zur echten Hardware; der Prozess bleibt im Großen und Ganzen gleich. Tauchen wir ein. -] else if lang == "ja" [ +], +ja: [ ここまでで、ツールと開発プロセスにある程度慣れたはずです。このセクションでは、実際のハードウェアに切り替えます。 開発プロセスは、ほとんど同じままです。飛び込みましょう。 -] else if lang == "zh" [ +], +zh: [ 现在你应该有点熟悉工具和开发过程了。在这部分我们将切换到真正的硬件上;步骤非常相似。让我们深入下去。 -] else { todo } +])) -= #(if lang == "en" [Know your hardware] - else if lang == "de" [Kennen Sie Ihre Hardware] - else if lang == "ja" [ハードウェアを知る] - else if lang == "zh" [认识你的硬件] - else { todo }) += #tr(( + en: [Know your hardware], + de: [Kennen Sie Ihre Hardware], + ja: [ハードウェアを知る], + zh: [认识你的硬件] +)) -#if lang == "en" [ +#tr(( +en: [ Before we begin you need to identify some characteristics of the target device as these will be used to configure the project: - The ARM core. e.g.~Cortex-M3. @@ -38,7 +45,8 @@ KiB of Flash and 32 KiB of RAM. - Where are Flash memory and RAM mapped in the address space? e.g.~RAM is commonly located at address `0x2000_0000`. -] else if lang == "de" [ +], +de: [ Bevor wir beginnen, müssen Sie einige Eigenschaften des Zielgeräts identifizieren da diese zur Konfiguration des Projekts verwendet werden: - Der ARM-Kern, z.B. Cortex-M3. @@ -48,7 +56,8 @@ Flash und 32 KiB RAM. - Wo werden Flash-Speicher und RAM im Adressraum abgebildet? zB RAM ist befindet sich üblicherweise an der Adresse `0x2000_0000`. -] else if lang == "ja" [ +], +ja: [ 始める前に、プロジェクトの設定に利用するターゲットデバイスのいくつかの特徴を確認する必要があります。 - ARMコア、例えばCortex-M3です。 - そのARMコアはFPUを搭載していますか?Cortex-M4#[*F*]とCortex-M7#[*F*]は、搭載しています。 @@ -56,27 +65,33 @@ 例えば、フラッシュは256KiBでRAMは32KiBです。 - フラッシュメモリとRAMは、アドレス空間のどこにマッピングされていますか? 例えば、RAMは、通常`0x2000_0000`番地に位置します。 -] else if lang == "zh" [ +], +zh: [ 在我们开始之前,你需要了解下你的目标设备的一些特性,因为你将用它们来配置项目: - ARM 内核。比如 Cortex-M3 。 - ARM 内核包括一个FPU吗?Cortex-M4#[*F*]和Cortex-M7#[*F*]有。 - 目标设备有多少Flash和RAM?比如 256KiB的Flash和32KiB的RAM。 - Flash和RAM映射在地址空间的什么位置?比如 RAM通常位于 `0x2000_0000` 地址处。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ You can find this information in the data sheet or the reference manual of your device. -] else if lang == "de" [ +], +de: [ Diese Informationen finden Sie im Datenblatt oder im Referenzhandbuch Ihres/Ihrer Microcontrollers/Microcontrollerplatine. -] else if lang == "ja" [ +], +ja: [ これらの情報は、デバイスのデータシートかリファレンスマニュアルに掲載されています。 -] else if lang == "zh" [ +], +zh: [ 你可以在你的设备的数据手册和参考手册上找到这些信息。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ In this section we'll be using our reference hardware, the STM32F3DISCOVERY. This board contains an STM32F303VCT6 microcontroller. This microcontroller has: @@ -84,7 +99,8 @@ - 256 KiB of Flash located at address 0x0800_0000. - 40 KiB of RAM located at address 0x2000_0000. (There's another RAM region but for simplicity we'll ignore it). -] else if lang == "de" [ +], +de: [ In diesem Abschnitt verwenden wir unsere Referenzhardware, die STM32F3DISCOVERY. Diese Platine enthält einen Mikrocontroller STM32F303VCT6. Dieser Mikrocontroller verfügt über: @@ -92,37 +108,44 @@ - 256 KiB Flash befinden sich an der Adresse 0x0800_0000. - 440 KiB RAM an der Adresse 0x2000_0000. (Es gibt noch eine weitere RAM-Region, aber der Einfachheit halber ignorieren wir das). -] else if lang == "ja" [ +], +ja: [ このセクションでは、私たちのリファレンスハードウェアであるSTM32F3DISCOVERYを使用します。 このボードは、STM32F303VCT6マイクロコントローラを1つ搭載しています。このマイクロコントローラは以下のものを持っています。 - 単精度FPUを含むCortex-M4Fコアが1つ - 0x0800_0000番地に配置された256KiBのフラッシュメモリ - - 0x2000_0000番地に配置された40KiBのRAM。(別のRAM領域もありますが、説明の簡単化のため、取り扱いません) -] else if lang == "zh" [ + - 0x2000_0000番地に配置された40KiBのRAM。(別のRAM領域もありますが、説明の簡単化のため、取り扱 +], +zh: [ 这部分,要使用我们的参考硬件,STM32F3DISCOVERY。这个板子包含一个STM32F303VCT6微控制器。这个微控制器拥有: - 一个Cortex-M4F核心,它包含一个单精度FPU。 - 位于 0x0800_0000 地址的256KiB的Flash。 - 位于 0x2000_0000 地址的40KiB的RAM。(这里还有其它的RAM区域,但是为了方便起见,我们将忽略它)。 -] else { todo } +])) -= #(if lang == "en" [Configuring] - else if lang == "de" [Konfigurieren] - else if lang == "ja" [設定] - else if lang == "zh" [配置] - else { todo }) += #tr(( + en: [Configuring], + de: [Konfigurieren], + ja: [設定], + zh: [配置], +)) -#if lang == "en" [ +#tr(( +en: [ We'll start from scratch with a fresh template instance. Refer to the - #link( )[previous section on QEMU] for a refresher on how to do this without `cargo-generate`. -] else if lang == "de" [ + #link()[previous section on QEMU] for a refresher on how to do this without `cargo-generate`. +], +de: [ Wir beginnen bei Null mit einer neuen Vorlageninstanz. Zur Auffrischung siehe #link()[vorheriger Abschnitt zu QEMU], wie man das ohne `cargo-generate` macht. -] else if lang == "ja" [ +], +ja: [ テンプレートの新しいインスタンスを使って、スクラッチから書いていきましょう。 `cargo-generate`を使用しない方法については、#link()[前セクションのQEMU]を参照して下さい。 -] else if lang == "zh" [ +], +zh: [ 我们将使用一个新的模板实例从零开始。对于新手,请参考#link()[先前的QEMU]章节,了解如何在没有`cargo-generate`的情况下完成配置。 -] else { todo } +])) ```text $ cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart @@ -133,85 +156,87 @@ $ cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart $ cd app ``` -#if lang == "en" [ +#tr(( +en: [ Step number one is to set a default compilation target in `.cargo/config.toml`. -] else if lang == "de" [ +], +de: [ Schritt Nummer eins besteht darin, ein Standardkompilierungsziel in `.cargo/config.toml` festzulegen. -] else if lang == "ja" [ +], +ja: [ 第一ステップは、`.cargo/config`にデフォルトコンパイルターゲットを設定することです。 -] else if lang == "zh" [ +], +zh: [ 第一步是在`.cargo/config.toml`中设置一个默认编译目标。 -] else { todo } +])) ```console tail -n5 .cargo/config.toml ``` -#if lang in ("en", "zh") [ - ```toml - # Pick ONE of these compilation targets - # target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ - # target = "thumbv7m-none-eabi" # Cortex-M3 - # target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU) - target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) - ``` -] else if lang == "de" [ - ```toml - # Waehlen Sie EINES von diesen Kompilierungszielen aus - # target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ - # target = "thumbv7m-none-eabi" # Cortex-M3 - # target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU) - target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) - ``` -] else if lang == "ja" [ - ```toml - [build] - # 以下のコンパイルターゲットから1つを選びます - # target = "thumbv6m-none-eabi" # Cortex-M0およびCortex-M0+ - # target = "thumbv7m-none-eabi" # Cortex-M3 - # target = "thumbv7em-none-eabi" # Cortex-M4およびCortex-M7 (no FPU) - target = "thumbv7em-none-eabihf" # Cortex-M4FおよびCortex-M7F (with FPU) - ``` -] else { todo } - -#if lang == "en" [ +#let _and_ = ts(( + en: " and ", + ja: "および" +), default: " and ") +#raw(block: true, lang: "toml", +"# " + ts(( + en: "Pick ONE of these compilation targets", + de: "Waehlen Sie EINES von diesen Kompilierungszielen aus", + ja: "以下のコンパイルターゲットから1つを選びます" + )) + " +# target = \"thumbv6m-none-eabi\" # Cortex-M0"+_and_+"Cortex-M0+ +# target = \"thumbv7m-none-eabi\" # Cortex-M3 +# target = \"thumbv7em-none-eabi\" # Cortex-M4"+_and_+"Cortex-M7 (no FPU) +target = \"thumbv7em-none-eabihf\" # Cortex-M4F"+_and_+"Cortex-M7F (with FPU) +") + +#tr(( +en: [ We'll use `thumbv7em-none-eabihf` as that covers the Cortex-M4F core. -] else if lang == "de" [ +], +de: [ Wir verwenden `thumbv7em-none-eabihf`, da es den Cortex-M4F-Kern abdeckt. -] else if lang == "ja" [ +], +ja: [ Cortex-M4Fコアを対象とするものとして、`thumbv7em-none-eabihf`を使います。 -] else if lang == "zh" [ +], +zh: [ 我们将使用 `thumbv7em-none-eabihf`,因为它包括了Cortex-M4F内核. -] else { todo } +])) -#quote(block: true)[ -#if lang == "en" [ +#quote(block: true, tr(( +en: [ *NOTE*: As you may remember from the previous chapter, we have to install all targets and this is a new one. So don't forget to run the installation process `rustup target add thumbv7em-none-eabihf` for this target. -] else if lang == "de" [ +], +de: [ *HINWEIS*: Wie Sie sich vielleicht aus dem vorherigen Kapitel erinnern, müssen wir alle Ziele installieren, dies ist ein neues Ziel. Vergessen Sie also nicht, die Installationsprozess `rustup target add thumbv7em-none-eabihf` für dieses Ziel auszuführen. -] else if lang == "zh" [ +], +zh: [ *注意*:你可能还记得先前的章节,我们必须要安装所有的目标平台,这个平台是一个新的. 所以,不要忘了为这个平台运行安装步骤 `rustup target add thumbv7em-none-eabihf` . -] else { todo } -] +]))) -#if lang == "en" [ +#tr(( +en: [ The second step is to enter the memory region information into the `memory.x` file. -] else if lang == "de" [ +], +de: [ Der zweite Schritt besteht darin, die Speicherbereichsinformationen in die Datei `memory.x` einzugeben -] else if lang == "ja" [ +], +ja: [ 第二ステップは、`memory.x`ファイルにメモリ領域の情報を入力することです。 -] else if lang == "zh" [ +], +zh: [ 第二步是将存储区域信息(memory region information)输入`memory.x`。 -] else { todo } +])) ```text $ cat memory.x @@ -224,251 +249,265 @@ MEMORY } ``` -#quote(block: true)[ -#if lang == "en" [ +#quote(block: true, tr(( +en: [ *NOTE*: If you for some reason changed the `memory.x` file after you had made the first build of a specific build target, then do `cargo clean` before `cargo build`, because `cargo build` may not track updates of `memory.x`. -] else if lang == "de" [ +], +de: [ *NOTE*: Wenn Sie aus irgendeinem Grund die Datei `memory.x` geändert haben, nachdem Sie den ersten Build für ein bestimmtes Build-Ziel erstellt haben, dann führen Sie `cargo clean` vor `cargo build` aus, da `cargo build` keine Änderungen von `memory.x` verfolgt. -] else if lang == "zh" [ +], +zh: [ *注意*:如果你因为某些理由,在对某个编译目标首次编译后,改变了`memory.x`文件,需要在`cargo build`之前执行`cargo clean`。因为`cargo build`可能不会跟踪`memory.x`的更新。 -] else { todo } -] +]))) -#if lang == "en" [ +#tr(( +en: [ We'll start with the hello example again, but first we have to make a small change. -] else if lang == "de" [ +], +de: [ Wir beginnen noch einmal mit dem Hallo-Beispiel, aber zuerst müssen wir eine kleine Änderung vornehmen. -] else if lang == "zh" [ +], +zh: [ 我们将再次使用hello示例作为开始,但是首先我们必须做一个小改变。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ In `examples/hello.rs`, make sure the `debug::exit()` call is commented out or removed. It is used only for running in QEMU. -] else if lang == "de" [ +], +de: [ Stellen Sie in `examples/hello.rs`sicher, daß `debug::exit()` auskommentiert oder entfernt wurde. Es wird nur zum Ausführen in QEMU verwendet. -] else if lang == "ja" [ +], +ja: [ `debug::exit()`の呼び出しが、コメントアウトされているか削除されていることを確認して下さい。 これは、QEMUで実行する時のみ、使用します。 -] else if lang == "zh" [ +], +zh: [ 在`examples/hello.rs`中,确保`debug::exit()`调用被注释掉了或者移除掉了。它只能用于在QEMU中运行的情况。 -] else { todo } +])) #raw(block: true, lang: "rust", "#[entry] fn main() -> ! { hprintln!(\"Hello, world!\").unwrap(); - // " + if lang == "en" { - "exit QEMU + // " + ( + en: "exit QEMU // NOTE do not run this on hardware; it can corrupt OpenOCD state - // debug::exit(debug::EXIT_SUCCESS);" - } else if lang == "de" { - "exit QEMU + // debug::exit(debug::EXIT_SUCCESS);", + de: "exit QEMU // HINWEIS: Fuehren Sie dies nicht auf der Hardware aus; es kann den // OpenOCD-Zustand beschaedigen. - // debug::exit(debug::EXIT_SUCCESS);" - } else if lang == "ja" { - "QEMUを終了する + // debug::exit(debug::EXIT_SUCCESS);", + ja: "QEMUを終了する // 注記、ハードウェア上で実行しないで下さい。OpenOCDの状態を破壊する可能性があります。 - // debug::exit(debug::EXIT_SUCCESS);" - } else if lang == "zh" { - "退出 QEMU - // 注意 不要在硬件上运行这个;它会打破OpenOCD的状态" - } else { todos } + " + // debug::exit(debug::EXIT_SUCCESS);", + zh: "退出 QEMU + // 注意 不要在硬件上运行这个;它会打破OpenOCD的状态", + ).at(lang, default: todos) + " loop {} } ") -#if lang == "en" [ +#tr(( +en: [ You can now cross compile programs using `cargo build` and inspect the binaries using `cargo-binutils` as you did before. The `cortex-m-rt` crate handles all the magic required to get your chip running, as helpfully, pretty much all Cortex-M CPUs boot in the same fashion. -] else if lang == "de" [ +], +de: [ Sie können nun Programme mittels `cargo build` cross-kompilieren und die Binärdateien mit `cargo-binutils` untersuchen, genau wie zuvor. Das `cortex-m-rt`-Crate übernimmt die gesamte für den Betrieb des Chips erforderliche „Magie", da erfreulicherweise so gut wie alle Cortex-M-CPUs auf die gleiche Weise booten. -] else if lang == "ja" [ +], +ja: [ これまでやってきた通り、`cargo build`でプログラムをクロスコンパイルし、 `cargo-binutils`でバイナリを調べることができます。 `cortex-m-rt`クレートは、チップを動作させるために必要な、全てのおまじないを処理します。 便利なことに、ほとんど全てのCortex-M CPUが同じ方法で起動します。 -] else if lang == "zh" [ +], +zh: [ 你可以像你之前做的一样,使用`cargo build`检查编译程序,使用`cargo-binutils`观察二进制项。`cortex-m-rt`库可以处理所有让芯片运行起来所需的魔法,几乎所有的Cortex-M CPUs都按同样的方式启动。 -] else { todo } +])) ```console cargo build --example hello ``` -= #(if lang == "en" [Debugging] - else if lang == "de" [Fehlerbehebung (Debugging)] - else if lang == "ja" [デバッグ] - else if lang == "zh" [调试] - else { todo }) += #tr(( + en: [Debugging], + de: [Fehlerbehebung (Debugging)], + ja: [デバッグ], + zh: [调试], +)) #let url_dn = "https://github.com/rust-embedded/debugonomicon" -#if lang == "en" [ +#tr(( +en: [ Debugging will look a bit different. In fact, the first steps can look different depending on the target device. In this section we'll show the steps required to debug a program running on the STM32F3DISCOVERY. This is meant to serve as a reference; for device specific information about debugging check out #link(url_dn)[the Debugonomicon]. -] else if lang == "de" [ +], +de: [ Das Debugging gestaltet sich etwas anders. Tatsächlich können sich bereits die ersten Schritte je nach Zielgerät unterscheiden. In diesem Abschnitt zeigen wir die Schritte, die zum Debuggen eines auf dem STM32F3DISCOVERY laufenden Programms erforderlich sind. Dies soll als Referenz dienen; für gerätespezifische Informationen zum Debugging konsultieren Sie bitte #link(url_dn)[das Debugonomicon]. -] else if lang == "ja" [ +], +ja: [ デバッグ方法は少し違います。実際、最初のステップは、ターゲットデバイスによって異なります。 このセクションでは、STM32F3DISCOVERY上で実行しているプログラムをデバッグするために必要となる手順を説明します。 これは、参考の役目を果たします。デバイス固有のデバッグ情報は、 #link(url_dn)[the Debugonomicon]を参照して下さい。 -] else if lang == "zh" [ +], +zh: [ 调试会看起来有点不一样。事实上,取决于不同的目标设备,第一步可能看起来不一样。在这个章节里,我们将展示,调试一个在STM32F3DISCOVERY上运行的程序,所需要的步骤。这作为一个参考。关于调试有关的设备特定的信息,可以看#link(url_dn)[the Debugonomicon]。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ As before we'll do remote debugging and the client will be a GDB process. This time, however, the server will be OpenOCD. -] else if lang == "de" [ +], +de: [ Wie zuvor führen wir Remote-Debugging durch, wobei der Client ein GDB-Prozess ist. Diesmal ist der Server jedoch OpenOCD. -] else if lang == "ja" [ +], +ja: [ 以前と同様に、リモートデバッグを行います。クライアントがGDBプロセスであることも同様です。 しかし、今回、サーバはOpenOCDになります。 -] else if lang == "zh" [ +], +zh: [ 像之前一样,我们将进行远程调试,客户端将是一个GDB进程。不同的是,OpenOCD将是服务器。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ As done during the #link()[verify] section connect the discovery board to your laptop / PC and check that the ST-LINK header is populated. -] else if lang == "de" [ +], +de: [ Verbinden Sie, wie im Abschnitt "#link()[Die Installation überprüfen]" beschrieben, das Discovery Board mit Ihrem Laptop/PC und prüfen Sie, ob der ST-LINK-Header ausgefüllt ist. -] else if lang == "ja" [ +], +ja: [ #link()[インストールの確認]セクションでやったように、ノートPCまたはPCをdiscoveryボードに接続し、 ST-LINKヘッダが設定されていることを確認して下さい。 -] else if lang == "zh" [ +], +zh: [ 像是在#link()[安装验证]中做的那样,把你的笔记本/个人电脑和discovery开发板连接起来,检查ST-LINK的短路帽是否被安装了。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ On a terminal run `openocd` to connect to the ST-LINK on the discovery board. Run this command from the root of the template; `openocd` will pick up the `openocd.cfg` file which indicates which interface file and target file to use. -] else if lang == "de" [ +], +de: [ Führen Sie in einem Terminal `openocd` aus, um eine Verbindung zum ST-LINK auf dem Discovery-Board herzustellen. Führen Sie diesen Befehl im Stammverzeichnis der Vorlage aus; `openocd` greift dabei auf die Datei `openocd.cfg` zu, in der festgelegt ist, welche Schnittstellen- und Zieldateien verwendet werden sollen. -] else if lang == "ja" [ +], +ja: [ discoveryボードのST-LINKに接続するために、端末で`openocd`を実行して下さい。 このコマンドは、テンプレートプロジェクトのルートディレクトリから実行して下さい。 `openocd`は、どのインタフェースファイルとターゲットファイルを使うか、が記述されている`openocd.cfg`ファイルを見つけます。 -] else if lang == "zh" [ +], +zh: [ 在一个终端上运行 `openocd` 连接到你的开发板上的 ST-LINK 。从模板的根目录运行这个命令;`openocd` 将会选择 `openocd.cfg` 文件,它指出了所使用的接口文件(interface file)和目标文件(target file)。 -] else { todo } +])) ```console cat openocd.cfg ``` -#if lang in ("en", "zh") [ - ```text - # Sample OpenOCD configuration for the STM32F3DISCOVERY development board - - # Depending on the hardware revision you got you'll have to pick ONE of these - # interfaces. At any time only one interface should be commented out. +#raw(block: true, lang: "text", +"# " + ts(( + en: "Sample OpenOCD configuration for the STM32F3DISCOVERY development board - # Revision C (newer revision) - source [find interface/stlink.cfg] +# Depending on the hardware revision you got you'll have to pick ONE of these +# interfaces. At any time only one interface should be commented out. - # Revision A and B (older revisions) - # source [find interface/stlink-v2.cfg] +# Revision C (newer revision)", + de: "Beispiel-OpenOCD-Konfiguration für das STM32F3DISCOVERY-Entwicklungsboard - source [find target/stm32f3x.cfg] - ``` -] else if lang == "de" [ - ```text - # Beispiel-OpenOCD-Konfiguration für das STM32F3DISCOVERY-Entwicklungsboard +# Je nach der vorliegenden Hardware-Revision muessen Sie eine dieser +# Schnittstellen auswaehlen. Es sollte jeweils nur eine Schnittstelle +# auskommentiert sein. - # Je nach der vorliegenden Hardware-Revision muessen Sie eine dieser - # Schnittstellen auswaehlen. Es sollte jeweils nur eine Schnittstelle - # auskommentiert sein. +# Revision C (neuere Revision)", + ja: "STM32F3DISCOVERY開発ボード用のOpenOCD設定サンプル - # Revision C (neuere Revision) - source [find interface/stlink.cfg] +# 持っているハードウェアのリビジョンに応じて、これらのインタフェースのうち、1つを選んで下さい。 +# 常に、1つのインタフェースがコメントアウトされているべきです。 - # Revision A and B (aeltere Revisionen) - # source [find interface/stlink-v2.cfg] +# リビジョンC (新しいリビジョン)", + )) + " +source [find interface/stlink.cfg] - source [find target/stm32f3x.cfg] - ``` -] else if lang == "ja" [ - ```text - # STM32F3DISCOVERY開発ボード用のOpenOCD設定サンプル +# " + ts(( + en: "Revision A and B (older revisions)", + de: "Revision A and B (aeltere Revisionen)", + ja: "リビジョンAとB(古いリビジョン)", + )) + " +# source [find interface/stlink-v2.cfg] - # 持っているハードウェアのリビジョンに応じて、これらのインタフェースのうち、1つを選んで下さい。 - # 常に、1つのインタフェースがコメントアウトされているべきです。 - - # リビジョンC (新しいリビジョン) - source [find interface/stlink-v2-1.cfg] - - # リビジョンAとB(古いリビジョン) - # source [find interface/stlink-v2.cfg] - - source [find target/stm32f3x.cfg] - ``` -] else { todo } +source [find target/stm32f3x.cfg] +") -#quote(block: true)[ -#if lang == "en" [ +#quote(block: true, tr(( +en: [ *NOTE*: If you found out that you have an older revision of the discovery board during the #link()[verify] section then you should modify the `openocd.cfg` file at this point to use `interface/stlink-v2.cfg`. -] else if lang == "de" [ +], +de: [ *HINWEIS* Falls Sie im Abschnitt "#link()[Die Installation überprüfen]" feststellen, dass Sie eine ältere Revision des Discovery-Boards besitzen, sollten Sie an dieser Stelle die Datei `openocd.cfg` so anpassen, dass `interface/stlink-v2.cfg` verwendet wird. -] else if lang == "ja" [ +], +ja: [ *注記* #link()[インストールの確認]セクションで、古いバージョンのdiscoveryボードを持っていることが判明している場合、 `interface/stlink-v2.cfg`を使うように`openocd.cfg`ファイルを修正する必要があります。 -] else if lang == "zh" [ +], +zh: [ *注意* 如果你在#link()[安装验证]章节中,发现你的discovery开发板是一个更旧的版本,那么你应该修改你的 `openocd.cfg` 文件,注释掉 `interface/stlink.cfg`,让它去使用 `interface/stlink-v2.cfg` 。 -] else { todo } -] +]))) ```text $ openocd @@ -490,42 +529,52 @@ Info : Target voltage: 2.913879 Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints ``` -#if lang == "en" [ +#tr(( +en: [ On another terminal run GDB, also from the root of the template. -] else if lang == "de" [ +], +de: [ Starten Sie in einem weiteren Terminal GDB, ebenfalls vom Stammverzeichnis des Templates aus. -] else if lang == "ja" [ +], +ja: [ 別の端末で、GDBを実行します。こちらも、テンプレートプロジェクトのルートディレクトから実行して下さい。 -] else { todo } +])) ```text gdb-multiarch -q target/thumbv7em-none-eabihf/debug/examples/hello ``` -#if lang == "en" [ +#tr(( +en: [ *NOTE*: like before you might need another version of gdb instead of `gdb-multiarch` depending on which one you installed in the installation chapter. This could also be `arm-none-eabi-gdb` or just `gdb`. -] else if lang == "de" [ +], +de: [ *HINWEIS*: Wie bereits erwähnt, benötigen Sie möglicherweise anstelle von `gdb-multiarch` eine andere Version von gdb, je nachdem, welche Version Sie im Installationskapitel installiert haben. Dies könnte beispielsweise `arm-none-eabi-gdb` oder einfach `gdb` sein. -] else if lang == "zh" [ +], +zh: [ *注意*: 像之前一样,你可能需要另一个版本的gdb而不是`gdb-multiarch`,取决于你在之前的章节安装了什么工具。这也可能使用的是`arm-none-eabi-gdb`或者只是`gdb` -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Next connect GDB to OpenOCD, which is waiting for a TCP connection on port 3333. -] else if lang == "de" [ +], +de: [ Verbinden Sie nun GDB mit OpenOCD, das auf eine TCP-Verbindung an Port 3333 wartet. -] else if lang == "ja" [ +], +ja: [ 次に、TCP 3333ポートで接続待ちしているOpenOCDに、GDBを接続します。 -] else if lang == "zh" [ +], +zh: [ 接下来把GDB连接到OpenOCD,它正在等待一个在端口3333上的TCP链接。 -] else { todo } +])) ```console (gdb) target remote :3333 @@ -533,17 +582,21 @@ Remote debugging using :3333 0x00000000 in ?? () ``` -#if lang == "en" [ +#tr(( +en: [ Now proceed to _flash_ (load) the program onto the microcontroller using the `load` command. -] else if lang == "de" [ +], +de: [ Fahren Sie nun damit fort, das Programm mithilfe des Befehls `load` auf den Mikrocontroller zu _flashen_ (zu laden). -] else if lang == "ja" [ +], +ja: [ それでは、`load`コマンドを使って、マイクロコントローラにプログラムを_書き込んで_下さい。 -] else if lang == "zh" [ +], +zh: [ 接下来使用`load`命令,继续 _flash_(加载) 程序到微控制器上。 -] else { todo } +])) ```console (gdb) load @@ -554,54 +607,64 @@ Start address 0x08000400, load size 7468 Transfer rate: 13 KB/sec, 2489 bytes/write. ``` -#if lang == "en" [ +#tr(( +en: [ The program is now loaded. This program uses semihosting so before we do any semihosting call we have to tell OpenOCD to enable semihosting. You can send commands to OpenOCD using the `monitor` command. -] else if lang == "de" [ +], +de: [ Das Programm ist nun geladen. Da dieses Programm Semihosting verwendet, müssen wir OpenOCD anweisen, Semihosting zu aktivieren, bevor wir einen entsprechenden Aufruf tätigen. Befehle können mithilfe des Befehls `monitor` an OpenOCD gesendet werden. -] else if lang == "ja" [ +], +ja: [ プログラムがロードされました。このプログラムはセミホスティングを使用します。そこで、 セミホスティングを呼び出して何かを行う前に、OpenOCDにセミホスティングを有効にするように、 指示する必要があります。 -] else if lang == "zh" [ +], +zh: [ 程序现在被加载了。这个程序使用半主机模式,因此在我们调用半主机模式之前,我们必须告诉OpenOCD使能半主机。你可以使用 `monitor` 命令,发送命令给OpenOCD 。 -] else { todo } +])) ```console (gdb) monitor arm semihosting enable semihosting is enabled ``` -#quote(block: true)[ -#if lang == "en" [ +#quote(block: true, tr(( +en: [ You can see all the OpenOCD commands by invoking the `monitor help` command. -] else if lang == "de" [ +], +de: [ Sie können sich alle OpenOCD-Befehle anzeigen lassen, indem Sie den Befehl `monitor help` aufrufen. -] else if lang == "ja" [ +], +ja: [ `monitor help`コマンドを実行することで、全てのOpenOCDコマンドを見ることができます。 -] else if lang == "zh" [ +], +zh: [ 通过调用 `monitor help` 命令,你能看到所有的OpenOCD命令。 -] else { todo } -] +]))) -#if lang == "en" [ +#tr(( +en: [ Like before we can skip all the way to `main` using a breakpoint and the `continue` command. -] else if lang == "de" [ +], +de: [ Wie zuvor können wir mithilfe eines Breakpoints und des Befehls `continue` direkt zu `main` springen. -] else if lang == "ja" [ +], +ja: [ 以前のように、ブレイクポイントと`continue`コマンドを使用することで、`main`までスキップすることができます。 -] else if lang == "zh" [ +], +zh: [ 像我们之前一样,使用一个断点和 `continue` 命令我们可以跳过所有的步骤到 `main` 。 -] else { todo } +])) ```console (gdb) break main @@ -615,32 +678,36 @@ Breakpoint 1, hello::__cortex_m_rt_main_trampoline () at examples/hello.rs:11 11 #[entry] ``` -#quote(block: true)[ -#if lang == "en" [ +#quote(block: true, tr(( +en: [ *NOTE* If GDB blocks the terminal instead of hitting the breakpoint after you issue the `continue` command above, you might want to double check that the memory region information in the `memory.x` file is correctly set up for your device (both the starts _and_ lengths). -] else if lang == "de" [ +], +de: [ *HINWEIS* Falls GDB das Terminal blockiert, anstatt am Haltepunkt (Breakpoint) anzuhalten, nachdem Sie den oben genannten Befehl `continue` eingegeben haben, sollten Sie überprüfen, ob die Angaben zum Speicherbereich in der Datei `memory.x` für Ihr Gerät korrekt konfiguriert sind (sowohl die Startadressen _als auch_ die Längen). -] else if lang == "zh" [ +], +zh: [ *注意* 如果在你使用了上面的`continue`命令后,GDB阻塞住了终端而不是停在了断点处,你可能需要检查下`memory.x`文件中的存储分区的信息,对于你的设备来说是否被正确的设置了起始位置*和*大小 。 -] else { todo } -] +]))) -#if lang == "en" [ +#tr(( +en: [ Step into the main function with `step`. -] else if lang == "de" [ +], +de: [ Springen Sie mit `step` in die main-Funktion. -] else if lang == "zh" [ +], +zh: [ 使用`step`步进main函数里。 -] else { todo } +])) ```console (gdb) step @@ -649,20 +716,24 @@ hello::__cortex_m_rt_main () at examples/hello.rs:13 13 hprintln!("Hello, world!").unwrap(); ``` -#if lang == "en" [ +#tr(( +en: [ After advancing the program with `next` you should see "Hello, world!" printed on the OpenOCD console, among other stuff. -] else if lang == "de" [ +], +de: [ Nachdem Sie das Programm mit `next` weitergeführt haben, sollten Sie unter anderem „Hello, world!" auf der OpenOCD-Konsole ausgegeben sehen. -] else if lang == "ja" [ +], +ja: [ #todoupd("ja") この時点で、OpenOCDコンソールに、他のものと入り混じって「Hello, world!」と表示されるはずです。 -] else if lang == "zh" [ +], +zh: [ 在使用了`next`让函数继续执行之后,你应该看到 "Hello, world!" 被打印到了OpenOCD控制台上。 -] else { todo } +])) ```console $ openocd @@ -677,23 +748,29 @@ Info : halted: PC: 0x080004b8 Info : halted: PC: 0x080004bc ``` -#if lang == "en" [ +#tr(( +en: [ The message is only displayed once as the program is about to enter the infinite loop defined in line 19: `loop {}` -] else if lang == "de" [ +], +de: [ Die Meldung wird nur einmal angezeigt, kurz bevor das Programm in die in Zeile 19 definierte Endlosschleife eintritt: `loop {}` -] else if lang == "zh" [ +], +zh: [ 消息只打印一次,然后进入定义在19行的无限循环中: `loop {}` -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ You can now exit GDB using the `quit` command. -] else if lang == "de" [ +], +de: [ Sie können GDB nun mit dem Befehl `quit` beenden. -] else if lang == "zh" [ +], +zh: [ 使用 `quit` 命令,你现在可以退出 GDB 了。 -] else { todo } +])) ```console (gdb) quit @@ -704,21 +781,24 @@ A debugging session is active. Quit anyway? (y or n) ``` -#if lang == "en" [ +#tr(( +en: [ Debugging now requires a few more steps so we have packed all those steps into a single GDB script named `openocd.gdb`. The file was created during the `cargo generate` step, and should work without any modifications. Let's have a peek: -] else if lang == "de" [ +], +de: [ Das Debugging erfordert nun einige zusätzliche Schritte; daher haben wir all diese Schritte in einem einzigen GDB-Skript namens `openocd.gdb` zusammengefasst. Die Datei wurde während des Schritts `cargo generate` erstellt und sollte ohne Änderungen funktionieren. Werfen wir einen Blick darauf: -] else if lang == "zh" [ +], +zh: [ 现在调试比之前多了点步骤,因此我们要把所有步骤打包进一个名为 `openocd.gdb` 的GDB脚本中。这个文件在 `cargo generate` 步骤中被生成,因此不需要任何修改了。让我们看一下: -] else { todo } +])) ```console cat openocd.gdb @@ -743,40 +823,48 @@ load stepi ``` #let cmd_gdb_hello = ` -x openocd.gdb target/thumbv7em-none-eabihf/debug/examples/hello` -#if lang == "en" [ +#tr(( +en: [ Now running #cmd_gdb_hello will immediately connect GDB to OpenOCD, enable semihosting, load the program and start the process. -] else if lang == "de" [ +], +de: [ Wenn Sie nun den Befehl #cmd_gdb_hello ausführen, verbindet sich GDB sofort mit OpenOCD, aktiviert Semihosting, lädt das Programm und startet den Prozess. -] else if lang == "ja" [ +], +ja: [ #cmd_gdb_hello;を実行することで、GDBはすぐにOpenOCDに接続し、 セミホスティングを有効化し、プログラムをロードした上で、プロセスを開始します。 -] else if lang == "zh" [ +], +zh: [ 现在运行 #cmd_gdb_hello 将会立即把GDB和OpenOCD连接起来,使能半主机,加载程序和启动进程。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Alternatively, you can turn ` -x openocd.gdb` into a custom runner to make `cargo run` build a program _and_ start a GDB session. This runner is included in `.cargo/config.toml` but it's commented out. -] else if lang == "de" [ +], +de: [ Alternativ können Sie ` -x openocd.gdb` als benutzerdefinierten Runner einrichten, sodass `cargo run` das Programm baut _und_ eine GDB-Sitzung startet. Dieser Runner ist bereits in der Datei `.cargo/config.toml` enthalten, jedoch auskommentiert. -] else if lang == "ja" [ +], +ja: [ 別の方法として、` -x openocd.gdb`をカスタムランナーにして、`cargo run`でプログラムをビルドし、 _さらに_GDBセッションを開始することもできます。このランナーは、`.cargo/config`に含まれていますが、 コメントアウトされています。 -] else if lang == "zh" [ +], +zh: [ 另外,你能将 ` -x openocd.gdb` 放进一个自定义的 runner 中,使 `cargo run` 能编译程序并启动一个GDB会话。这个 runner 在 `.cargo/config.toml` 中,但是它被注释掉了。 -] else { todo } +])) ```console head -n10 .cargo/config.toml @@ -784,27 +872,26 @@ head -n10 .cargo/config.toml #raw(block: true, lang: "toml", "[target.thumbv7m-none-eabi] -# " + if lang in ("en", "zh") { - "uncomment this to make `cargo run` execute programs on QEMU" - } else if lang == "de" { - "Dies auskommentieren, damit `cargo run` Programme auf QEMU ausfuehrt." - } else if lang == "ja" { - "ここのコメントアウトを外すと、`cargo run`はQEMUでプログラムを実行します" - } else { todos } + " +# " + ts(( + en: "uncomment this to make `cargo run` execute programs on QEMU", + de: "Dies auskommentieren, damit `cargo run` Programme auf QEMU ausfuehrt.", + ja: "ここのコメントアウトを外すと、`cargo run`はQEMUでプログラムを実行します", + zh: "uncomment this to make `cargo run` execute programs on QEMU", + )) + " # runner = \"qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel\" [target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))'] -# " + if lang in ("en", "zh") { - "uncomment ONE of these three option to make `cargo run` start a GDB session -# which option to pick depends on your system" - } else if lang == "de" { - "Heben Sie die Auskommentierung einer dieser drei Optionen auf, damit - # `cargo run` eine GDB-Sitzung startet. -# Welche Option Sie waehlen sollten, haengt von Ihrem System ab." - } else if lang == "ja" { - "3つの選択肢のうち、1つのコメントアウトを外すと、`cargo run`はGDBセッションを開始します。 -# どの選択肢を使うか、は対象システムによって異なります。" - } else { todos } + " +# " + ts(( + en: "uncomment ONE of these three option to make `cargo run` start a GDB session +# which option to pick depends on your system", + de: "Heben Sie die Auskommentierung einer dieser drei Optionen auf, damit +# `cargo run` eine GDB-Sitzung startet. +# Welche Option Sie waehlen sollten, haengt von Ihrem System ab.", + ja: "3つの選択肢のうち、1つのコメントアウトを外すと、`cargo run`はGDBセッションを開始します。 +# どの選択肢を使うか、は対象システムによって異なります。", + zh: "uncomment ONE of these three option to make `cargo run` start a GDB session +# which option to pick depends on your system", + )) + " runner = \"arm-none-eabi-gdb -x openocd.gdb\" # runner = \"gdb-multiarch -x openocd.gdb\" # runner = \"gdb -x openocd.gdb\" diff --git a/typ/start/index.typ b/typ/start/index.typ index 450c33af..15416de0 100644 --- a/typ/start/index.typ +++ b/typ/start/index.typ @@ -1,14 +1,16 @@ #import "../config.typ": * -#h1(if lang == "en" [Getting Started] - else if lang == "de" [Erste Schritte] - else if lang == "ja" [入門] - else if lang == "zh" [开始] - else { todo }) +#h1(tr(( + en: [Getting Started], + de: [Erste Schritte], + ja: [入門], + zh: [开始], +))) #let ln_f3 = link("http://www.st.com/en/evaluation-tools/stm32f3discovery.html")[STM32F3DISCOVERY] -#if lang == "en" [ +#tr(( +en: [ In this section we'll walk you through the process of writing, building, flashing and debugging embedded programs. You will be able to try most of the examples without any special hardware as we will show you the @@ -16,7 +18,8 @@ section where hardware is required is, naturally enough, the #link()[Hardware] section, where we use OpenOCD to program an #ln_f3. -] else if lang == "de" [ +], +de: [ In diesem Abschnitt führen wir Sie Schritt für Schritt durch den Prozess des Schreibens, Kompilierens, Flashen und Debuggens von Embedded-Programmen. Sie können die meisten der Beispiele ohne spezielle @@ -25,10 +28,12 @@ Abschnitt, in dem Hardware erforderlich ist, ist natürlich der Abschnitt #link()[Hardware], wo wir OpenOCD verwenden, um ein #ln_f3 zu programmieren. -] else if lang == "ja" [ +], +ja: [ このセクションでは、組込みプログラムを書いて、ビルドして、フラッシュに書き込み、デバッグする、という一連のプロセスを説明します。 ほとんどの例を特別なハードウェアなしで試すことができます。有名なオープンソースハードウェアエミュレータであるQEMUを使うからです。 - ハードウェアが必要となる唯一のセクションは、当然ながら、OpenOCDを使って#ln_f3;にプログラムする#link("./hardware.md")[Hardware]セクションです。 -] else if lang == "zh" [ + ハードウェアが必要となる唯一のセクションは、当然ながら、OpenOCDを使って#ln_f3;にプログラムする#link()[Hardware]セクションです。 +], +zh: [ 在这部分里,你将会经历编写,编译,烧录和调试嵌入式程序。大多数的例子都不需要特定的硬件就可以试试,因为我们将要向你展示一个开源硬件仿真器,QEMU的基本使用。唯一需要硬件的部分,那就是,#link()[硬件]那一章,我们会使用OpenOCD去编程一个#ln_f3。 -] else { todo } \ No newline at end of file +])) \ No newline at end of file diff --git a/typ/start/interrupts.typ b/typ/start/interrupts.typ index e484966f..f9ff5563 100644 --- a/typ/start/interrupts.typ +++ b/typ/start/interrupts.typ @@ -1,18 +1,21 @@ #import "../config.typ": * -#h1(offset: whole, - if lang in ("en", "de") [Interrupts] - else if lang == "ja" [割り込み] - else if lang == "zh" [中断] - else { todo }) +#h1(offset: whole, tr(( + en: [Interrupts], + de: [Interrupts], + ja: [割り込み], + zh: [中断], +))) -#if lang == "en" [ +#tr(( +en: [ Interrupts differ from exceptions in a variety of ways but their operation and use is largely similar and they are also handled by the same interrupt controller. Whereas exceptions are defined by the Cortex-M architecture, interrupts are always vendor (and often even chip) specific implementations, both in naming and functionality. -] else if lang == "de" [ +], +de: [ Interrupts unterscheiden sich in vielerlei Hinsicht von Exceptions; ihre Funktionsweise und Verwendung sind jedoch weitgehend ähnlich, und sie werden zudem von demselben Interrupt-Controller verwaltet. Während @@ -20,14 +23,17 @@ sich bei Interrupts -- sowohl hinsichtlich der Benennung als auch der Funktionalität -- stets um herstellerspezifische (und oft sogar chip-spezifische) Implementierungen. -] else if lang == "ja" [ +], +ja: [ 割り込みは様々な点で例外と違いますが、その動作と使用方法は、ほとんど同じで、同じ割り込みコントローラによって処理されます。 例外がCortex-Mアーキテクチャで定義されているのに対し、割り込みは、命名と機能との両方において、常にベンダ(もっと言うとチップ)固有の実装です。 -] else if lang == "zh" [ +], +zh: [ 虽然中断和异常在很多方面都不一样,但是它们的操作和使用几乎是一样的,且它们也能被同一个中断控制器处理。然而异常是由Cortex-M微架构定义的,中断在命名和功能上总是由特定厂商(经常甚至是芯片)实现的。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Interrupts do allow for a lot of flexibility which needs to be accounted for when attempting to use them in an advanced way. We will not cover those uses in this book, however it is a good idea to keep the following @@ -38,7 +44,8 @@ handler might be interrupted by another higher-priority interrupt - In general the reason causing the interrupt to trigger needs to be cleared to prevent re-entering the interrupt handler endlessly -] else if lang == "de" [ +], +de: [ Interrupts bieten ein hohes Maß an Flexibilität, das bei einer fortgeschrittenen Nutzung berücksichtigt werden muss. Wir werden diese Anwendungsfälle in diesem Buch zwar nicht behandeln, dennoch ist es @@ -51,74 +58,86 @@ - Im Allgemeinen muss die Ursache für die Auslösung des Interrupts beseitigt werden, um zu verhindern, dass der Interrupt-Handler endlos erneut aufgerufen wird. -] else if lang == "ja" [ +], +ja: [ 割り込みは、高度な使い方をしようとする時に必要とされる様々な柔軟性を考慮に入れています。 本書では、そのような高度な使い方は対象外です。しかし、次の点に留意することをお勧めします。 - 割り込みは、ハンドラの実行順序を決めるプログラム可能な優先度を持ちます。 - 割り込みは、ネストとプリエンプションが可能です。つまり、割り込みハンドラの実行は、より優先度の高い割り込みに割り込まれる場合があります。 - 通常、割り込み要因は、割り込みハンドラが無限に再呼び出しされないようにするため、クリアされる必要があります。 -] else if lang == "zh" [ +], +zh: [ 中断提供了更多的灵活性,当尝试用一种高级的方法使用它们时,我们需要对这种灵活性进行解释。但我们将不会在这本书里涵盖这些内容,最好把下面的东西记在心里: - 中断有可以编程的优先级,其决定了它们的处理函数的执行顺序。 - 中断能嵌套且抢占,i.e.~一个中断处理函数的执行可以被其它更高优先级的中断打断。 - 通常需要清除掉导致中断被触发的原因,避免无限地再次进入中断处理函数。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ The general initialization steps at runtime are always the same: - Setup the peripheral(s) to generate interrupts requests at the desired occasions - Set the desired priority of the interrupt handler in the interrupt controller - Enable the interrupt handler in the interrupt controller -] else if lang == "de" [ +], +de: [ Die allgemeinen Initialisierungsschritte zur Laufzeit sind immer gleich: - Konfigurieren Sie die Peripheriekomponente(n) so, dass Interrupt-Anforderungen zu den gewünschten Zeitpunkten ausgelöst werden. - Legen Sie die gewünschte Priorität des Interrupt-Handlers im Interrupt-Controller fest. - Aktivieren Sie den Interrupt-Handler im Interrupt-Controller. -] else if lang == "ja" [ +], +ja: [ ランタイムでの一般的な初期化手順は、常に同じです。 - 必要な時に割り込み要求を起こすように、ペリフェラルを設定します - 割り込みコントローラで割り込みハンドラの優先度をセットします - 割り込みコントローラで割り込みハンドラを有効化します -] else if lang == "zh" [ +], +zh: [ 运行时的初始化步骤总是相同的: - 设置外设在想要的事件发生时产生中断请求 - 在中断控制器中设置需要的中断处理函数的优先级 - 在中断控制器中使能中断处理函数 -] else { todo } +])) #let ln_int = link("https://docs.rs/cortex-m-rt-macros/0.1.5/cortex_m_rt_macros/attr.interrupt.html")[`interrupt`] -#if lang == "en" [ +#tr(( +en: [ Similarly to exceptions, the cortex-m-rt crate exposes an #ln_int attribute for declaring interrupt handlers. However, this attribute is only available when the device feature is enabled. That said, this attribute is not intended to be used directly---doing so will result in a compilation error. -] else if lang == "de" [ +], +de: [ Ähnlich wie bei Exceptions stellt das `cortex-m-rt`-Crate ein #ln_int;-Attribut zur Deklaration von Interrupt-Handlern bereit. Dieses Attribut steht jedoch nur zur Verfügung, wenn das Device-Feature aktiviert ist. Es ist allerdings nicht für die direkte Verwendung vorgesehen; eine solche würde zu einem Kompilierfehler führen. -] else if lang == "ja" [ +], +ja: [ 例外と同様に、例外ハンドラを宣言するために、`cortex-m-rt`クレートは、#ln_int;属性を提供しています。 利用可能な割り込み(そして割り込みハンドラテーブルでの配置)は、通常、`svd2rust`を使ってSVDから自動生成されます。 -] else if lang == "zh" [ +], +zh: [ #todoupd("zh") 与异常相似,`cortex-m-rt` crate提供了一个#ln_int;属性去声明中断处理函数。可用的中断(及它们在中断向量表中的位置)通常由`svd2rust`从一个SVD描述文件自动地生成。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Instead, you should use the re-exported version of the interrupt attribute provided by the device crate (usually generated using svd2rust). This ensures that the compiler can verify that the interrupt actually exists on the target device. The list of available interrupts---and their position in the interrupt vector table---is typically auto-generated from an SVD file by svd2rust. -] else if lang == "de" [ +], +de: [ Stattdessen sollten Sie die vom Device-Crate (das üblicherweise mit `svd2rust` generiert wird) bereitgestellte, re-exportierte Version des `interrupt`-Attributs verwenden. Dies stellt sicher, dass der Compiler @@ -126,102 +145,104 @@ existiert. Die Liste der verfügbaren Interrupts -- sowie deren Position in der Interrupt-Vektortabelle -- wird typischerweise von `svd2rust` automatisch aus einer SVD-Datei generiert. -] else { todo } +])) #raw(block: true, lang: "rust", "rust -use lm3s6965::interrupt; // " + if lang == "en" { - "Re-exported attribute from the device crate" - } else if lang == "de" { - "Aus dem Device-Crate zurueck-exportiertes Attribut" - } else { todos } + " +use lm3s6965::interrupt; // " + ts(( + en: "Re-exported attribute from the device crate", + de: "Aus dem Device-Crate zurueck-exportiertes Attribut" + )) + " -// " + if lang == "en" { - "Interrupt handler for the Timer2 interrupt" - } else if lang == "de" { - "Interrupt-Handler fuer den Timer2-Interrupt" - } else if lang == "ja" { - "タイマ2割り込みの割り込みハンドラ" - } else if lang == "zh" { - "Timer2中断的中断处理函数" - } else { todos } + " +// " + ts(( + en: "Interrupt handler for the Timer2 interrupt", + de: "Interrupt-Handler fuer den Timer2-Interrupt", + ja: "タイマ2割り込みの割り込みハンドラ", + zh: "Timer2中断的中断处理函数", + )) + " #[interrupt] fn TIMER2A() { // .. - // " + if lang == "en" { - "Clear reason for the generated interrupt request" - } else if lang == "de" { - "Eindeutiger Grund fuer die generierte Interrupt-Anforderung" - } else if lang == "ja" { - "発生した割り込み要求の原因をクリアします" - } else if lang == "zh" { - "清除生成中断请求的原因" - } else { todos } + " + // " + ts(( + en: "Clear reason for the generated interrupt request", + de: "Eindeutiger Grund fuer die generierte Interrupt-Anforderung", + ja: "発生した割り込み要求の原因をクリアします", + zh: "清除生成中断请求的原因", + )) + " } ") -#if lang == "en" [ +#tr(( +en: [ Interrupt handlers look like plain functions (except for the lack of arguments) similar to exception handlers. However they can not be called directly by other parts of the firmware due to the special calling conventions. It is however possible to generate interrupt requests in software to trigger a diversion to the interrupt handler. -] else if lang == "de" [ +], +de: [ Interrupt-Handler ähneln -- abgesehen vom Fehlen von Argumenten -- gewöhnlichen Funktionen, ganz wie Exception-Handler. Aufgrund spezieller Aufrufkonventionen können sie jedoch nicht direkt von anderen Teilen der Firmware aufgerufen werden. Es ist allerdings möglich, Interrupt-Anforderungen per Software zu erzeugen, um einen Sprung zum Interrupt-Handler auszulösen. -] else if lang == "ja" [ +], +ja: [ 割り込みハンドラは、通常の関数のように見え、例外ハンドラに似ています(引数がないことを除いて)。 しかし、割り込みハンドラは、特別な呼び出し規約のため、ファームウェアの他の部分から直接呼び出すことができません。 ソフトウェアで割り込み要求を起こし、割り込みハンドラへの転送を発生させることは可能です。 -] else if lang == "zh" [ +], +zh: [ 中断处理函数和异常处理函数一样看起来像是普通的函数(除了没有入参)。然而由于特殊的调用规定,它不能被固件的其它部分直接调用。然而,可以在软件中生成中断请求,转移到中断处理函数中。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Similar to exception handlers it is also possible to declare `static mut` variables inside the interrupt handlers for _safe_ state keeping. -] else if lang == "de" [ +], +de: [ Ähnlich wie bei Exception-Handlern ist es auch hier möglich, `static mut`-Variablen innerhalb der Interrupt-Handler zu deklarieren, um den Zustand auf _sichere_ Weise zu speichern. -] else if lang == "ja" [ +], +ja: [ 例外ハンドラと同様に、割り込みハンドラ内で`static mut`変数を宣言し、状態を_安全_に保持することができます。 -] else if lang == "zh" [ +], +zh: [ 与异常处理函数一样,也能在中断处理函数中声明`static mut`变量且保持 _safe_ 状态。 -] else { todo } +])) #raw(block: true, lang: "rust", "#[interrupt] fn TIMER2A() { static mut COUNT: u32 = 0; - // " + if lang == "en" { - "`COUNT` has type `&mut u32` and it's safe to use" - } else if lang == "de" { - "`COUNT` hat den Typ `&mut u32` und ist sicher zu verwenden." - } else if lang == "ja" { - "`COUNT`は`&mut u32`の型を持っており、その使用は安全です" - } else if lang == "zh" { - "`COUNT` 的类型是 `&mut u32` 且它用起来安全" - } else { todos } + " + // " + ts(( + en: "`COUNT` has type `&mut u32` and it's safe to use", + de: "`COUNT` hat den Typ `&mut u32` und ist sicher zu verwenden.", + ja: "`COUNT`は`&mut u32`の型を持っており、その使用は安全です", + zh: "`COUNT` 的类型是 `&mut u32` 且它用起来安全", + )) + " *COUNT += 1; } ") -#if lang == "en" [ +#tr(( +en: [ For a more detailed description about the mechanisms demonstrated here please refer to the #link()[exceptions section]. -] else if lang == "de" [ +], +de: [ Für eine detailliertere Beschreibung der hier veranschaulichten Mechanismen konsultieren Sie bitte den #link()[Abschnitt zu Ausnahmen]. -] else if lang == "ja" [ +], +ja: [ ここで示した仕組みの詳細については、#link()[例外セクション]を参照して下さい。 -] else if lang == "zh" [ +], +zh: [ 关于这里所说的机制的更多细节描述,请参考#link()[异常章节]。 -] else { todo } +])) diff --git a/typ/start/panicking.typ b/typ/start/panicking.typ index fd814aeb..5270f3af 100644 --- a/typ/start/panicking.typ +++ b/typ/start/panicking.typ @@ -1,78 +1,91 @@ #import "../config.typ": * -#h1(offset: whole, - if lang == "en" [Panicking] - else if lang == "de" [In Panik geraten] - else if lang == "ja" [パニック] - else if lang == "zh" [运行时恐慌(Panicking)] - else { todo }) +#h1(offset: whole, tr(( + en: [Panicking], + de: [In Panik geraten], + ja: [パニック], + zh: [运行时恐慌(Panicking)], +))) -#if lang == "en" [ +#tr(( +en: [ Panicking is a core part of the Rust language. Built-in operations like indexing are runtime checked for memory safety. When out of bounds indexing is attempted this results in a panic. -] else if lang == "de" [ +], +de: [ Das Auslösen einer Panik ist ein wesentlicher Bestandteil der Sprache Rust. Eingebaute Operationen wie der Zugriff per Index werden zur Laufzeit auf Speichersicherheit überprüft. Erfolgt ein Zugriff außerhalb der zulässigen Grenzen, führt dies zu einer Panik. -] else if lang == "ja" [ +], +ja: [ パニックはRustのコア部分です。インデックス操作のような言語組込みの操作は、メモリ安全性をランタイム時に検査されます。 範囲外のインデックスにアクセスしようとすると、パニックが発生します。 -] else if lang == "zh" [ +], +zh: [ 运行时恐慌是Rust语言的一个核心部分。像是索引这样的内建的操作为了存储安全性是运行时检查的。当尝试越界索引时,这会导致运行时恐慌(panic)。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ In the standard library panicking has a defined behavior: it unwinds the stack of the panicking thread, unless the user opted for aborting the program on panics. -] else if lang == "de" [ +], +de: [ In der Standardbibliothek ist das Verhalten bei einer Panik definiert: Der Stack des betroffenen Threads wird abgewickelt (Stack Unwinding), es sei denn, der Benutzer hat sich für einen Programmabbruch im Falle einer Panik entschieden. -] else if lang == "ja" [ +], +ja: [ 標準ライブラリでは、パニックは定義された動作です。ユーザがパニック発生時にプログラムをアボートする選択をしない限り、 パニックを起こしたスレッドのスタックを巻き戻します。 -] else if lang == "zh" [ +], +zh: [ 在标准库中,运行时恐慌的行为被定义成:展开(unwinds)恐慌的线程的栈,除非用户自己选择在恐慌时终止程序。 -] else { todo } +])) #let ln_info = link("https://doc.rust-lang.org/core/panic/struct.PanicInfo.html")[`PanicInfo`] -#if lang == "en" [ +#tr(( +en: [ In programs without standard library, however, the panicking behavior is left undefined. A behavior can be chosen by declaring a `#[panic_handler]` function. This function must appear exactly _once_ in the dependency graph of a program, and must have the following signature: `fn(&PanicInfo) -> !`, where #ln_info is a struct containing information about the location of the panic. -] else if lang == "de" [ +], +de: [ In Programmen ohne Standardbibliothek ist das Verhalten bei einer Panik hingegen nicht definiert. Ein Verhalten lässt sich durch die Deklaration einer `#[panic_handler]`-Funktion festlegen. Diese Funktion muss im Abhängigkeitsgraphen des Programms genau _einmal_ vorkommen und folgende Signatur aufweisen: `fn(&PanicInfo) -> !`, wobei #ln_info eine Struktur ist, die Informationen über den Ort des Panic enthält. -] else if lang == "ja" [ +], +ja: [ しかし、非標準のプログラムでは、パニック時の挙動は、未定義のままです。`#[panic_handler]`関数を宣言することにより、 挙動を選択することができます。この関数は、プログラムの依存関係グラフに、*1回だけ*現れる必要があります。 そして、 `fn(&PanicInfo) -> !`のシグネチャを持つ必要があります。 ここで、#ln_info;は、パニックした位置情報を含む構造体です。 -] else if lang == "zh" [ +], +zh: [ 然而在没有标准库的程序中,运行时恐慌的行为是未被定义了的。通过声明一个 `#[panic_handler]` 函数可以选择一个运行时恐慌的行为。 这个函数在一个程序的依赖图中必须只出现一次,且必须有这样的签名: `fn(&PanicInfo) -> !`,`PanicInfo`是一个包含关于发生运行时恐慌的位置信息的结构体。 -] else { todo } +])) #let ln_abort = link("https://crates.io/crates/panic-abort")[`panic-abort`] #let ln_halt = link("https://crates.io/crates/panic-halt")[`panic-halt`] #let ln_itm = link("https://crates.io/crates/panic-itm")[`panic-itm`] #let ln_sh = link("https://crates.io/crates/panic-semihosting")[`panic-semihosting`] -#if lang == "en" [ +#tr(( +en: [ Given that embedded systems range from user facing to safety critical (cannot crash) there's no one size fits all panicking behavior but there are plenty of commonly used behaviors. These common behaviors have been @@ -82,7 +95,8 @@ - #ln_halt. A panic causes the program, or the current thread, to halt by entering an infinite loop. - #ln_itm. The panicking message is logged using the ITM, an ARM Cortex-M specific peripheral. - #ln_sh. The panicking message is logged to the host using the semihosting technique. -] else if lang == "de" [ +], +de: [ Da das Spektrum eingebetteter Systeme von anwenderorientierten bis hin zu sicherheitskritischen Anwendungen (bei denen ein Absturz ausgeschlossen sein muss) reicht, gibt es kein universelles Verhalten im @@ -97,7 +111,8 @@ Cortex-M spezifischen Peripheriekomponente. - #ln_sh. Die Panik-Meldung wird mithilfe der Semihosting-Technik auf dem Host protokolliert. -] else if lang == "ja" [ +], +ja: [ 組込みシステムは、ユーザとやり取りするものから、安全性が重要な(クラッシュできない)ものまであります。 そのため、全てのパニック時動作に対応できる唯一のものはありませんが、よく利用される挙動がたくさんあります。 これらの一般的な挙動が、`#[panic_handler]`関数を定義するクレートにまとめられています。 @@ -107,7 +122,8 @@ - #ln_itm。パニック発生時のメッセージは、ARM Cortex-M固有のペリフェラルであるITMを使ってログ出力されます。 - #ln_sh。パニック発生時のメッセージは、セミホスティングを使ってログ出力されます。 -] else if lang == "zh" [ +], +zh: [ 鉴于嵌入式系统的范围从面向用户的系统到安全关键系统,没有一个运行时恐慌行为能满足所有场景,但是有许多常用的行为。这些常用的行为已经被打包进了一些crates中,这些crates中定义了 `#[panic_handler]`函数。比如: - #ln_abort. @@ -119,98 +135,105 @@ - #ln_sh. 使用半主机技术,运行时恐慌的信息被记录到主机上。 -] else { todo } +])) #let ln_handler = link("https://crates.io/keywords/panic-handler")[`panic-handler`] -#if lang == "en" [ +#tr(( +en: [ You may be able to find even more crates searching for the #ln_handler keyword on crates.io. -] else if lang == "de" [ +], +de: [ Möglicherweise findest du noch mehr Crates, wenn du auf crates.io nach dem Schlüsselwort #ln_handler suchst. -] else if lang == "ja" [ +], +ja: [ crates.ioで#ln_handler;をキーワードに検索することで、さらにクレートを見つけることができます。 -] else if lang == "zh" [ +], +zh: [ 在crates.io上搜索 #ln_handler,你甚至可以找到更多的crates。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ A program can pick one of these behaviors simply by linking to the corresponding crate. The fact that the panicking behavior is expressed in the source of an application as a single line of code is not only useful as documentation but can also be used to change the panicking behavior according to the compilation profile. For example: -] else if lang == "de" [ +], +de: [ Ein Programm kann eines dieser Verhalten einfach dadurch auswählen, dass es das entsprechende Crate einbindet. Dass das Verhalten im Fehlerfall (Panic-Verhalten) im Quellcode einer Anwendung als einzelne Codezeile ausgedrückt wird, ist nicht nur als Dokumentation nützlich, sondern ermöglicht es auch, dieses Verhalten je nach Kompilierungsprofil anzupassen. Zum Beispiel: -] else if lang == "ja" [ +], +ja: [ プログラムは、対応するクレートとリンクすることで、これらの挙動の中から1つを選びます。 パニック時の挙動がアプリケーションソースコードの中で単一行で表現されていることは、ドキュメントとして有用なだけでなく、 パニック時の挙動をコンパイル時のプロファイルで変更にする時にも利用できます。 例えば -] else if lang == "zh" [ +], +zh: [ 仅仅通过链接到相关的crate中,一个程序就可以简单地从这些行为中选择一个运行时恐慌行为。将运行时恐慌的行为作为一行代码放进一个应用的源码中,不仅仅是因为可以作为文档使用,而且能根据编译配置改变运行时恐慌的行为。比如: -] else { todo } +])) #raw(block: true, lang: "rust", "#![no_main] #![no_std] -// " + if lang == "en" { - "dev profile: easier to debug panics; can put a breakpoint on `rust_begin_unwind`" - } else if lang == "de" { - "dev-Profil: einfacheres Debuggen von Panics; man kann einen Breakpoint bei -// `rust_begin_unwind` setzen." - } else if lang == "ja" { - "開発プロファイル:パニックのデバッグを容易にします。`rust_begin_unwind`にブレイクポイントを置くことを可能にします。" - } else if lang == "zh" { - "dev配置: 更容易调试运行时恐慌; 可以在 `rust_begin_unwind` 上放一个断点" - } else { todos } + " +// " + ts(( + en: "dev profile: easier to debug panics; can put a breakpoint on `rust_begin_unwind`", + de: "dev-Profil: einfacheres Debuggen von Panics; man kann einen Breakpoint bei +// `rust_begin_unwind` setzen.", + ja: "開発プロファイル:パニックのデバッグを容易にします。`rust_begin_unwind`にブレイクポイントを置くことを可能にします。", + zh: "dev配置: 更容易调试运行时恐慌; 可以在 `rust_begin_unwind` 上放一个断点" + )) + " #[cfg(debug_assertions)] use panic_halt as _; -// " + if lang == "en" { - "release profile: minimize the binary size of the application" - } else if lang == "de" { - "Release-Profil: Minimierung der Binaergroesse der Anwendung" - } else if lang == "ja" { - "リリースプロファイル:アプリケーションのバイナリサイズを最小化します。" - } else if lang == "zh" { - "release配置: 最小化应用的二进制文件的大小" - } else { todos } + " +// " + ts(( + en: "release profile: minimize the binary size of the application", + de: "Release-Profil: Minimierung der Binaergroesse der Anwendung", + ja: "リリースプロファイル:アプリケーションのバイナリサイズを最小化します。", + zh: "release配置: 最小化应用的二进制文件的大小", + )) + " #[cfg(not(debug_assertions))] use panic_abort as _; // .. ") -#if lang == "en" [ +#tr(( +en: [ In this example the crate links to the `panic-halt` crate when built with the dev profile (`cargo build`), but links to the `panic-abort` crate when built with the release profile (`cargo build --release`). -] else if lang == "de" [ +], +de: [ In diesem Beispiel verlinkt der Crate beim Bauen mit dem Dev-Profil (`cargo build`) auf den `panic-halt`-Crate, beim Bauen mit dem Release-Profil (`cargo build --release`) hingegen auf den `panic-abort`-Crate. -] else if lang == "ja" [ +], +ja: [ #todoupd("ja") この例では、開発プロファイルでビルド(`cargo build`)した時は、`panic-halt`クレートとリンクします。 しかし、リリースプロファイルでビルド(`cargo build --release`)した時は、`panic-abort`クレートとリンクします。 -] else if lang == "zh" [ +], +zh: [ 在这个例子里,当使用dev配置编译的时候(`cargo build`),crate链接到 `panic-halt` crate上,但是当使用release配置编译时(`cargo build --release`),crate链接到`panic-abort` crate上。 -] else { todo } +])) #quote(block: true)[ -#if lang == "en" [ +#tr(( +en: [ The `use panic_abort as _;` form of the `use` statement is used to ensure the `panic_abort` panic handler is included in our final executable while making it clear to the compiler that we won't @@ -220,7 +243,8 @@ use panic_abort as _; before the 2018 edition of Rust, and should now only be used for "sysroot" crates (those distributed with Rust itself) such as `proc_macro`, `alloc`, `std`, and `test`. -] else if lang == "de" [ +], +de: [ Die `use panic_abort as _;`-Variante der `use`-Anweisung wird verwendet, um sicherzustellen, dass der `panic_abort`-Panic-Handler in die fertige ausführbare Datei aufgenommen wird, während dem Compiler gleichzeitig @@ -232,30 +256,35 @@ use panic_abort as _; nur noch für sogenannte „sysroot"-Crates (die gemeinsam mit Rust selbst ausgeliefert werden) -- wie etwa `proc_macro`, `alloc`, `std` und `test` -- verwendet werden sollte. -] else if lang == "zh" [ +], +zh: [ `use panic_abort as _` 形式的 `use` 语句,被用来确保 `panic_abort` 运行时恐慌函数被包含进我们最终的可执行程序里,同时让编译器清楚地知道我们不会从这个crate显式地使用任何东西。没有 `_` 重命名,编译器将会警告我们有一个未使用的导入。有时候你可能会看到 `extern crate panic_abort`,这是Rust 2018之前的版本使用的更旧的写法,现在应该只被用于 "sysroot" crates (与Rust一起发布的crates),比如 `proc_macro`,`alloc`,`std` 和 `test` 。 -] else { todo } +])) ] -= #(if lang == "en" [An example] - else if lang == "de" [Ein Beispiel] - else if lang == "zh" [一个例子] - else { todo }) += #tr(( + en: [An example], + de: [Ein Beispiel], + zh: [一个例子], +)) -#if lang == "en" [ +#tr(( +en: [ Here's an example that tries to index an array beyond its length. The operation results in a panic. -] else if lang == "de" [ +], +de: [ Hier ist ein Beispiel, das versucht, auf ein Array an einer Position zuzugreifen, die über dessen Länge hinausgeht. Der Vorgang führt zu einer Panic. -] else if lang == "zh" [ +], +zh: [ 这里有一个尝试越界访问数组的例子。操作的结果导致了一个运行时恐慌(panic)。 -] else { todo } +])) #raw(block: true, lang: "rust", "#![no_main] @@ -269,27 +298,28 @@ use cortex_m_rt::entry; fn main() -> ! { let xs = [0, 1, 2]; let i = xs.len(); - let _y = xs[i]; // " + if lang == "en" { - "out of bounds access" - } else if lang == "de" { - "Zugriff ausserhalb der zulaessigen Grenzen" - } else if lang == "zh" { - "out of bounds access" - } else { todos } + " + let _y = xs[i]; // " + ts(( + en: "out of bounds access", + de: "Zugriff ausserhalb der zulaessigen Grenzen", + zh: "out of bounds access" + )) + " loop {} } ") -#if lang == "en" [ +#tr(( +en: [ This example chose the `panic-semihosting` behavior which prints the panic message to the host console using semihosting. -] else if lang == "de" [ +], +de: [ Für dieses Beispiel wurde das Verhalten `panic-semihosting` gewählt, das die Panic-Meldung mittels Semihosting auf der Host-Konsole ausgibt. -] else if lang == "zh" [ +], +zh: [ 这个例子选择了`panic-semihosting`行为,运行时恐慌的信息会被打印至使用了半主机模式的主机控制台上。 -] else { todo } +])) ```text $ cargo run @@ -297,12 +327,15 @@ $ cargo run panicked at 'index out of bounds: the len is 3 but the index is 4', src/main.rs:12:13 ``` -#if lang == "en" [ +#tr(( +en: [ You can try changing the behavior to `panic-halt` and confirm that no message is printed in that case. -] else if lang == "de" [ +], +de: [ Sie können versuchen, das Verhalten auf `panic-halt` zu ändern, und bestätigen, dass in diesem Fall keine Meldung ausgegeben wird. -] else if lang == "zh" [ +], +zh: [ 你可以尝试将行为改成`panic-halt`,确保在这个案例里没有信息被打印。 -] else { todo } +])) diff --git a/typ/start/qemu.typ b/typ/start/qemu.typ index 24efe17a..e2fc5c94 100644 --- a/typ/start/qemu.typ +++ b/typ/start/qemu.typ @@ -47,13 +47,15 @@ #let lm3s6965 = link("http://www.ti.com/product/LM3S6965")[LM3S6965] #let url_qemu = "https://wiki.qemu.org/Documentation/Platforms/ARM#Supported_in_qemu-system-arm" -#if lang == "en" [ +#tr(( +en: [ We'll start writing a program for the #lm3s6965, a Cortex-M3 microcontroller. We have chosen this as our initial target because it #link(url_qemu)[can be emulated] using QEMU so you don't need to fiddle with hardware in this section and we can focus on the tooling and the development process. -] else if lang == "de" [ +], +de: [ Wir beginnen mit der Entwicklung eines Programms für den #lm3s6965, einen ARM Cortex-M3-Mikrocontroller. Wir haben diesen als unser erstes Ziel @@ -62,74 +64,89 @@ sodass Sie sich in diesem Abschnitt nicht mit der Hardware beschäftigen müssen und wir uns auf die Werkzeuge und den Entwicklungsprozess konzentrieren können. -] else if lang == "zh" [ +], +zh: [ 我们将开始为#lm3s6965;编写程序,一个Cortex-M3微控制器。因为它能使用#link("https://wiki.qemu.org/Documentation/Platforms/ARM#Supported_in_qemu-system-arm")[QEMU仿真],所以我们选择它作为我们的第一个目标,本节中,不需要使用硬件,我们注意力可以集中在工具和开发过程上。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ *IMPORTANT* We'll use the name "app" for the project name in this tutorial. Whenever you see the word "app" you should replace it with the name you selected for your project. Or, you could also name your project "app" and avoid the substitutions. -] else if lang == "de" [ +], +de: [ *WICHTIG* In dieser Anleitung verwenden wir den Namen „app" als Projektnamen. Wann immer Sie das Wort „app" sehen, sollten Sie es durch den Namen ersetzen, den Sie für Ihr Projekt gewählt haben. Alternativ können Sie Ihr Projekt auch „app" nennen und so die Ersetzungen vermeiden. -] else if lang == "zh" [ +], +zh: [ *重要* 在这个引导里,我们将使用"app"这个名字来代指项目名。无论何时你看到单词"app",你应该用你选择的项目名来替代"app"。或者你也可以选择把你的项目命名为"app",避免要替换掉。 -] else { todo } +])) -= #(if lang == "en" [Creating a non standard Rust program] - else if lang == "de" [Erstellen eines nicht standardmäßigen Rust-Programms] - else if lang == "zh" [生成一个非标准的 Rust program] - else { todo }) += #tr(( + en: [Creating a non standard Rust program], + de: [Erstellen eines nicht standardmäßigen Rust-Programms], + zh: [生成一个非标准的 Rust program], +)) #let ln_quick = link("https://github.com/rust-embedded/cortex-m-quickstart")[`cortex-m-quickstart`] -#if lang == "en" [ +#tr(( +en: [ We'll use the #ln_quick project template to generate a new project from it. The created project will contain a barebone application: a good starting point for a new embedded rust application. In addition, the project will contain an `examples` directory, with several separate applications, highlighting some of the key embedded rust functionality. -] else if lang == "de" [ +], +de: [ Wir werden die Projektvorlage #ln_quick verwenden, um daraus ein neues Projekt zu erstellen. Das erstellte Projekt enthält eine Minimalanwendung: einen guten Ausgangspunkt für eine neue Embedded-Rust-Anwendung. Darüber hinaus enthält das Projekt ein Verzeichnis `examples` mit mehreren separaten Anwendungen, die einige der wichtigsten Funktionen von Embedded Rust veranschaulichen. -] else if lang == "zh" [ +], +zh: [ 我们将使用#ln_quick;项目模板来生成一个新项目。生成的项目将包含一个最基本的应用:对于一个新的嵌入式rust应用来说,是一个很好的开始。另外,项目将包含一个`example`文件夹,文件夹中有许多独立的应用,突出了一些关键的嵌入式rust的功能。 -] else { todo } +])) -== #(if lang == "en" [Using `cargo-generate`] - else if lang == "de" [Verwendung von `cargo-generate`] - else if lang == "zh" [使用 `cargo-generate`] - else { todo }) +== #tr(( + en: [Using `cargo-generate`], + de: [Verwendung von `cargo-generate`], + zh: [使用 `cargo-generate`], +)) -#if lang == "en" [ +#tr(( +en: [ First install cargo-generate -] else if lang == "de" [ +], +de: [ Installieren Sie zunächst cargo-generate -] else if lang == "zh" [ +], +zh: [ 首先安装 cargo-generate -] else { todo } +])) ```console cargo install cargo-generate ``` -#if lang == "en" [ +#tr(( +en: [ Then generate a new project -] else if lang == "de" [ +], +de: [ Erstellen Sie anschließend ein neues Projekt -] else if lang == "zh" [ +], +zh: [ 然后生成一个新项目 -] else { todo } +])) ```console cargo generate --git https://github.com/knurling-rs/app-template @@ -145,31 +162,38 @@ cargo generate --git https://github.com/knurling-rs/app-template cd app ``` -== #(if lang == "en" [Using `git`] - else if lang == "de" [Verwendung von `git`] - else if lang == "zh" [使用 `git`] - else { todo }) +== #tr(( + en: [Using `git`], + de: [Verwendung von `git`], + zh: [使用 `git`], +)) -#if lang == "en" [ +#tr(( +en: [ Clone the repository -] else if lang == "de" [ +], +de: [ Das Repository klonen -] else if lang == "zh" [ +], +zh: [ 克隆仓库 -] else { todo } +])) ```console git clone https://github.com/rust-embedded/cortex-m-quickstart app cd app ``` -#if lang == "en" [ +#tr(( +en: [ And then fill in the placeholders in the `Cargo.toml` file -] else if lang == "de" [ +], +de: [ Und fülle dann die Platzhalter in der Datei `Cargo.toml` aus -] else if lang == "zh" [ +], +zh: [ 然后补充`Cargo.toml`文件中的占位符 -] else { todo } +])) ```toml [package] @@ -186,20 +210,24 @@ test = false bench = false ``` -== #(if lang == "en" [Using neither] - else if lang == "de" [Keines von beiden verwenden] - else if lang == "zh" [要么使用] - else { todo }) +== #tr(( + en: [Using neither], + de: [Keines von beiden verwenden], + zh: [要么使用], +)) -#if lang == "en" [ +#tr(( +en: [ Grab the latest snapshot of the `cortex-m-quickstart` template and extract it. -] else if lang == "de" [ +], +de: [ Laden Sie den neuesten Snapshot der Vorlage `cortex-m-quickstart` herunter und entpacken Sie ihn. -] else if lang == "zh" [ +], +zh: [ 抓取最新的 `cortex-m-quickstart` 模板,解压它。 -] else { todo } +])) ```console curl -LO https://github.com/rust-embedded/cortex-m-quickstart/archive/master.zip @@ -208,39 +236,49 @@ mv cortex-m-quickstart-master app cd app ``` -#if lang == "en" [ +#tr(( +en: [ Or you can browse to #ln_quick, click the green "Clone or download" button and then click "Download ZIP". -] else if lang == "de" [ +], +de: [ Oder Sie navigieren zu #ln_quick, klicken auf die grüne Schaltfläche „Clone or download" und anschließend auf „Download ZIP". -] else if lang == "zh" [ +], +zh: [ 或者你可以浏览#ln_quick,点击绿色的 "Clone or download" 按钮,然后点击 "Download ZIP" 。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Then fill in the placeholders in the `Cargo.toml` file as done in the second part of the "Using `git`" version. -] else if lang == "de" [ +], +de: [ Füllen Sie anschließend die Platzhalter in der Datei `Cargo.toml` aus, wie im zweiten Teil der Version „Verwendung von `git` beschrieben. -] else if lang == "zh" [ +], +zh: [ 然后像在 "使用 `git`" 那里的第二部分写的那样填充 `Cargo.toml` 。 -] else { todo } +])) -= #(if lang == "en" [Program Overview] - else if lang == "de" [Programmübersicht] - else if lang == "zh" [项目概览] - else { todo }) += #tr(( + en: [Program Overview], + de: [Programmübersicht], + zh: [项目概览], +)) -#if lang == "en" [ +#tr(( +en: [ For convenience here are the most important parts of the source code in `src/main.rs`: -] else if lang == "de" [ +], +de: [ Der Einfachheit halber sind hier die wichtigsten Teile des Quellcodes in `src/main.rs` aufgeführt: -] else if lang == "zh" [ +], +zh: [ 这是`src/main.rs`中源码最重要的部分。 -] else { todo } +])) #raw(block: true, lang: "rust", "#![no_std] @@ -253,184 +291,199 @@ use cortex_m_rt::entry; #[entry] fn main() -> ! { loop { - // " + if lang == "en" { - "your code goes here" - } else if lang == "de" { - "Hier kommt Ihr Code hin." - } else if lang == "zh" { - "your code goes here" - } else { todos } + " + // " + ts(( + en: "your code goes here", + de: "Hier kommt Ihr Code hin.", + zh: "your code goes here", + )) + " } } ") -#if lang == "en" [ +#tr(( +en: [ This program is a bit different from a standard Rust program so let's take a closer look. -] else if lang == "de" [ +], +de: [ Dieses Programm unterscheidet sich ein wenig von einem typischen Rust-Programm, schauen wir es uns also einmal genauer an. -] else if lang == "zh" [ +], +zh: [ 这个程序与标准Rust程序有一点不同,让我们走近点看看。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ `#![no_std]` indicates that this program will _not_ link to the standard crate, `std`. Instead it will link to its subset: the `core` crate. -] else if lang == "de" [ +], +de: [ `#![no_std]` gibt an, dass dieses Programm _nicht_ mit der Standard-Crate `std` verknüpft wird. Stattdessen wird es mit deren Teilmenge verknüpft: der Crate `core`. -] else if lang == "zh" [ +], +zh: [ `#![no_std]`指出这个程序将 _不会_ 链接标准crate`std`。反而它将会链接到它的子集: `core` crate。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ `#![no_main]` indicates that this program won't use the standard `main` interface that most Rust programs use. The main (no pun intended) reason to go with `no_main` is that using the `main` interface in `no_std` context requires nightly. -] else if lang == "de" [ +], +de: [ `#![no_main]` gibt an, dass dieses Programm nicht die Standard-`main`- Schnittstelle verwendet, die die meisten Rust-Programme nutzen. Der Hauptgrund (kein Wortspiel beabsichtigt) für die Verwendung von `no_main` ist, dass die Nutzung der `main`-Schnittstelle im `no_std`-Kontext „Nightly" erfordert. -] else if lang == "zh" [ +], +zh: [ `#![no_main]`指出这个程序将不会使用标准的且被大多数Rust程序使用的`main`接口。使用`no_main`的主要理由是,在`no_std`上下文中使用`main`接口需要 nightly 版的 Rust。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ `use panic_halt as _;`. This crate provides a `panic_handler` that defines the panicking behavior of the program. We will cover this in more detail in the #link()[Panicking] chapter of the book. -] else if lang == "de" [ +], +de: [ `use panic_halt as _;`. Diese Crate stellt einen `panic_handler` bereit, der das Verhalten des Programms im Panikfall definiert. Wir werden darauf im Kapitel #link()[In Panik geraten] des Buches näher eingehen. -] else if lang == "zh" [ +], +zh: [ `use panic_halt as _;`。这个crate提供了一个`panic_handler`,它定义了程序陷入`panic`时的行为。我们将会在这本书的#link()[运行时恐慌(Panicking)]章节中覆盖更多的细节。 -] else { todo } +])) #let ln_entry = link("https://docs.rs/cortex-m-rt-macros/latest/cortex_m_rt_macros/attr.entry.html")[`#[entry]`] #let ln_rt = link("https://crates.io/crates/cortex-m-rt")[`cortex-m-rt`] -#if lang == "en" [ +#tr(( +en: [ #ln_entry is an attribute provided by the #ln_rt crate that's used to mark the entry point of the program. As we are not using the standard `main` interface we need another way to indicate the entry point of the program and that'd be `#[entry]`. -] else if lang == "de" [ +], +de: [ #ln_entry ist ein Attribut des #ln_rt;-Crate, das dazu dient, den Einstiegspunkt des Programms zu kennzeichnen. Da wir nicht die Standard-Schnittstelle `main` verwenden, benötigen wir eine andere Möglichkeit, den Einstiegspunkt des Programms anzugeben, und das wäre `#[entry]`. -] else if lang == "zh" [ +], +zh: [ #ln_entry 是一个由#ln_rt;提供的属性,它用来标记程序的入口。当我们不使用标准的`main`接口时,我们需要其它方法来指示程序的入口,那就是`#[entry]`。 -] else { todo } +])) #let url_div = "https://doc.rust-lang.org/rust-by-example/fn/diverging.html" -#if lang == "en" [ +#tr(( +en: [ `fn main() -> !`. Our program will be the _only_ process running on the target hardware so we don't want it to end! We use a #link(url_div)[divergent function] (the `-> !` bit in the function signature) to ensure at compile time that'll be the case. -] else if lang == "de" [ +], +de: [ `fn main() -> !`. Unser Programm wird der _einzige_ Prozess sein, der auf der Zielhardware läuft, deshalb soll es nicht beendet werden! Wir verwenden eine #link(url_div)[divergent function] (das `-> !` in der Funktionssignatur), um bereits zur Kompilierungszeit sicherzustellen, dass dies auch der Fall ist. -] else if lang == "zh" [ +], +zh: [ `fn main() -> !`。我们的程序将会是运行在目标板子上的 _唯一_ 的进程,因此我们不想要它结束!我们使用一个#link(url_div)[发散函数] (函数签名中的 `-> !` )来确保在编译时就是这么回事儿。 -] else { todo } +])) -= #(if lang == "en" [Cross compiling] - else if lang == "de" [Cross-Kompilierung] - else if lang == "zh" [交叉编译] - else { todo }) += #tr(( + en: [Cross compiling], + de: [Cross-Kompilierung], + zh: [交叉编译], +)) -#if lang == "en" [ +#tr(( +en: [ First of all we will need the memory layout for the target microcontroller, the LM3S6965 in our case. Otherwise the build will fail to link the image. Create a file named `memory.x` at the root of the project and paste the following content: -] else if lang == "de" [ +], +de: [ Zunächst benötigen wir das Speicherlayout für den Ziel-Mikrocontroller, in unserem Fall den LM3S6965. Andernfalls schlägt die Verknüpfung des Images beim Build fehl. Erstellen Sie eine Datei mit dem Namen `memory.x` im Stammverzeichnis des Projekts und fügen Sie den folgenden Inhalt ein: -] else { todo } +])) #raw(block: true, lang: "text", "MEMORY { - /* " + if lang == "en" { - "NOTE 1 K = 1 KiBi = 1024 bytes */ + /* " + ts(( + en: "NOTE 1 K = 1 KiBi = 1024 bytes */ /* TODO Adjust these memory regions to match your device memory layout */ /* These values correspond to the LM3S6965, one of the few devices - /* QEMU can emulate */" - } else if lang == "de" { - "Hinweis: 1 K = 1 KiBi = 1024 bytes */ + /* QEMU can emulate */", + de: "Hinweis: 1 K = 1 KiBi = 1024 bytes */ /* TODO Passen Sie diese Speicherbereiche an das Speicherlayout Ihres Geräts an. */ /* Diese Werte entsprechen dem LM3S6965, einem der wenigen Bausteine, die - QEMU emulieren kann */" - } else { todos } + " + QEMU emulieren kann */", + )) + " FLASH : ORIGIN = 0x00000000, LENGTH = 256K RAM : ORIGIN = 0x20000000, LENGTH = 64K } -/* " + if lang == "en" { - "This is where the call stack will be allocated. */ +/* " + ts(( + en: "This is where the call stack will be allocated. */ /* The stack is of the full descending type. */ /* You may want to use this variable to locate the call stack and static - variables in different memory regions. Below is shown the default value */" - } else if lang == "de" { - "Hier wird der Aufrufstapel zugewiesen. */ + variables in different memory regions. Below is shown the default value */", + de: "Hier wird der Aufrufstapel zugewiesen. */ /* Der Stapel ist vom Typ „vollständig absteigend“. */ /* Möglicherweise möchten Sie diese Variable verwenden, um den Aufrufstapel und statische Variablen in verschiedenen Speicherbereichen zu lokalisieren. Nachstehend ist der Standardwert aufgeführt */" - } else { todos } + " + )) + " /* _stack_start = ORIGIN(RAM) + LENGTH(RAM); */ -/* " + if lang == "en" { - "You can use this symbol to customize the location of the .text section */ +/* " + ts(( + en: "You can use this symbol to customize the location of the .text section */ /* If omitted the .text section will be placed right after the .vector_table section */ /* This is required only on microcontrollers that store some configuration right - after the vector table */" - } else if lang == "de" { - "Mit diesem Symbol können Sie den Speicherort des .text-Abschnitts anpassen. */ + after the vector table */", + de: "Mit diesem Symbol können Sie den Speicherort des .text-Abschnitts anpassen. */ /* Wird dies weggelassen, wird der .text-Abschnitt direkt nach dem .vector_table- Abschnitt platziert */ /* Dies ist nur bei Mikrocontrollern erforderlich, die bestimmte Konfigurationsdaten direkthinter der Vektortabelle speichern */" - } else { todos } + " + )) + " /* _stext = ORIGIN(FLASH) + 0x400; */ -/* " + if lang == "en" { - "Example of putting non-initialized variables into custom RAM locations. */ +/* " + ts(( + en: "Example of putting non-initialized variables into custom RAM locations. */ /* This assumes you have defined a region RAM2 above, and in the Rust sources added the attribute `#[link_section = \".ram2bss\"]` to the data you want to place there. */ -/* Note that the section will not be zero-initialized by the runtime! */" - } else if lang == "de" { - "Beispiel für die Zuweisung nicht initialisierter Variablen zu +/* Note that the section will not be zero-initialized by the runtime! */", + de: "Beispiel für die Zuweisung nicht initialisierter Variablen zu benutzerdefinierten RAM-Speicherplätzen. */ /* Dies setzt voraus, dass Sie oben einen Bereich namens „RAM2“ definiert und in den Rust-Quelldateien das Attribut `#[link_section = „.ram2bss“]` zu den Daten hinzugefügt haben, die Sie dort platzieren möchten. */ /* Beachten Sie, dass der Bereich von der Laufzeitumgebung nicht auf Null initialisiert wird! */" - } else { todos } + " + )) + " /* SECTIONS { .ram2bss (NOLOAD) : ALIGN(4) { *(.ram2bss); @@ -440,125 +493,140 @@ initialisiert wird! */" */ ") -#if lang == "en" [ +#tr(( +en: [ The next step is to _cross_ compile the program for the Cortex-M3 architecture. That's as simple as running `cargo build --target $TRIPLE` if you know what the compilation target (`$TRIPLE`) should be. Luckily, the `.cargo/config.toml` in the template has the answer: -] else if lang == "de" [ +], +de: [ Der nächste Schritt besteht darin, das Programm für die Cortex-M3-Architektur _cross_ zu kompilieren. Das geht ganz einfach mit dem Befehl `cargo build --target $TRIPLE`, sofern Sie wissen, wie das Kompilierungsziel (`$TRIPLE`) lauten soll. Glücklicherweise finden Sie die Antwort in der Datei `.cargo/config.toml` in der Vorlage: -] else if lang == "zh" [ +], +zh: [ 下一步是为Cortex-M3架构_交叉_编译程序。如果你知道编译目标(`$TRIPLE`)应该是什么,运行`cargo build --target $TRIPLE`就可以了。幸运地,模板中的`.cargo/config.toml`有这个答案: -] else { todo } +])) ```console tail -n6 .cargo/config.toml ``` -#if lang in ("en", "zh") [ - ```toml - [build] - # Pick ONE of these compilation targets - # target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ - target = "thumbv7m-none-eabi" # Cortex-M3 - # target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU) - # target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) - ``` -] else if lang == "de" [ - ```toml - [build] - # Waehlen Sie EINES dieser Kompilierungsziele aus - # target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ - target = "thumbv7m-none-eabi" # Cortex-M3 - # target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU) - # target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) - ``` -] else { todo } - -#if lang == "en" [ +#let _and_ = tr(( + en: " and ", +), default: " and ") + +#raw(block: true, lang: "toml", +"[build] +# " + ts(( + en: "Pick ONE of these compilation targets", + de: "Waehlen Sie EINES dieser Kompilierungsziele aus", + )) + " +# target = \"thumbv6m-none-eabi\" # Cortex-M0"+_and_+"Cortex-M0+ +target = \"thumbv7m-none-eabi\" # Cortex-M3 +# target = \"thumbv7em-none-eabi\" # Cortex-M4"+_and_+"Cortex-M7 (no FPU) +# target = \"thumbv7em-none-eabihf\" # Cortex-M4F"+_and_+"Cortex-M7F (with FPU) +") + +#tr(( +en: [ To cross compile for the Cortex-M3 architecture we have to use `thumbv7m-none-eabi`. That target is not automatically installed when installing the Rust toolchain, it would now be a good time to add that target to the toolchain, if you haven't done it yet: -] else if lang == "de" [ +], +de: [ Für die Cross-Kompilierung für die Cortex-M3-Architektur müssen wir `thumbv7m-none-eabi` verwenden. Dieses Ziel wird bei der Installation der Rust-Werkzeuge nicht automatisch installiert. Jetzt wäre ein guter Zeitpunkt, dieses Ziel zu den Werkzeugen hinzuzufügen, falls Sie dies noch nicht getan haben: -] else if lang == "zh" [ +], +zh: [ 为了交叉编译Cortex-M3架构我们不得不使用`thumbv7m-none-eabi`。当安装Rust工具时,target不会自动被安装,如果还没有添加,现在可以去添加那个target到工具链上。 -] else { todo } +])) ```console rustup target add thumbv7m-none-eabi ``` -#if lang == "en" [ +#tr(( +en: [ Since the `thumbv7m-none-eabi` compilation target has been set as the default in your `.cargo/config.toml` file, the two commands below do the same: -] else if lang == "de" [ +], +de: [ Da das Kompilierungsziel `thumbv7m-none-eabi` in Ihrer Datei `.cargo/config.toml` als Standard festgelegt wurde, haben die beiden folgenden Befehle dieselbe Wirkung: -] else if lang == "zh" [ +], +zh: [ 因为`thumbv7m-none-eabi`编译目标在你的`.cargo/config.toml`中被设置成默认值,下面的两个命令是一样的效果: -] else { todo } +])) ```console cargo build --target thumbv7m-none-eabi cargo build ``` -= #(if lang == "en" [Inspecting] - else if lang == "de" [Überprüfen] - else if lang == "zh" [检查] - else { todo }) += #tr(( + en: [Inspecting], + de: [Überprüfen], + zh: [检查], +)) -#if lang == "en" [ +#tr(( +en: [ Now we have a non-native ELF binary in `target/thumbv7m-none-eabi/debug/app`. We can inspect it using `cargo-binutils`. -] else if lang == "de" [ +], +de: [ Nun haben wir eine nicht-native ELF-Binärdatei in `target/thumbv7m-none-eabi/debug/app`. Wir können sie mit `cargo-binutils` untersuchen. -] else if lang == "zh" [ +], +zh: [ 现在在`target/thumbv7m-none-eabi/debug/app`中有一个非主机环境的ELF二进制文件。我们能使用`cargo-binutils`检查它。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ With `cargo-readobj` we can print the ELF headers to confirm that this is an ARM binary. -] else if lang == "de" [ +], +de: [ Mit `cargo-readobj` können wir die ELF-Header ausgeben, um zu überprüfen, ob es sich um eine ARM- Binärdatei handelt. -] else if lang == "zh" [ +], +zh: [ 使用`cargo-readobj`我们能打印ELF头,确认这是一个ARM二进制。 -] else { todo } +])) ```console cargo readobj --bin app -- --file-headers ``` -#if lang == "en" [ +#tr(( +en: [ Note that: - `--bin app` is sugar for inspect the binary at `target/$TRIPLE/debug/app` - `--bin app` will also (re)compile the binary, if necessary -] else if lang == "de" [ +], +de: [ Bitte beachten Sie: - `--bin app` ist eine vereinfachte Schreibweise für die Überprüfung der Binärdatei unter `target/$TRIPLE/debug/app` - `--bin app` kompiliert die Binärdatei bei Bedarf auch (neu) -] else if lang == "zh" [ +], +zh: [ 注意: - `--bin app` 是一个用来查看二进制项`target/$TRIPLE/debug/app`的语法糖 - `--bin app` 需要时也会重新编译二进制项。 -] else { todo } +])) ```text ELF Header: @@ -583,26 +651,32 @@ ELF Header: Section header string table index: 18 ``` -#if lang == "en" [ +#tr(( +en: [ `cargo-size` can print the size of the linker sections of the binary. -] else if lang == "de" [ +], +de: [ Mit `cargo-size` lässt sich die Größe der Linker-Abschnitte der Binärdatei anzeigen. -] else if lang == "zh" [ +], +zh: [ `cargo-size` 能打印二进制项的linker section的大小。 -] else { todo } +])) ```console cargo size --bin app --release -- -A ``` -#if lang == "en" [ +#tr(( +en: [ we use `--release` to inspect the optimized version -] else if lang == "de" [ +], +de: [ Wir verwenden `--release`, um die optimierte Version zu überprüfen. -] else if lang == "zh" [ +], +zh: [ 我们使用`--release`查看优化后的版本 -] else { todo } +])) ```text app : @@ -627,7 +701,8 @@ Total 14570 ``` #quote(block: true)[ -#if lang == "en" [ +#tr(( +en: [ A refresher on ELF linker sections - `.text` contains the program instructions - `.rodata` contains constant values like strings @@ -639,7 +714,8 @@ Total 14570 the vector (interrupt) table - `.ARM.attributes` and the `.debug_*` sections contain metadata and will _not_ be loaded onto the target when flashing the binary. -] else if lang == "de" [ +], +de: [ Eine Auffrischung zum Thema ELF-Linker-Abschnitte - `.text` enthält die Programmbefehle - `.rodata` enthält konstante Werte wie Zeichenfolgen @@ -652,7 +728,8 @@ Total 14570 - Die Abschnitte `.ARM.attributes` und `.debug_*` enthalten Metadaten und werden _nicht_ beim Flashen der Binärdatei auf das Zielgerät geladen werden. -] else if lang == "zh" [ +], +zh: [ ELF linker sections的复习 - `.text` 包含程序指令 - `.rodata` 包含像是字符串这样的常量 @@ -661,33 +738,39 @@ Total 14570 - `.vector_table` 是一个我们用来存储向量(中断)表的_非_标准的section - `.ARM.attributes` 和 `.debug_*` sections包含元数据,当烧录二进制文件时,它们不会被加载到目标上。 -] else { todo } +])) ] -#if lang == "en" [ +#tr(( +en: [ *IMPORTANT*: ELF files contain metadata like debug information so their _size on disk_ does _not_ accurately reflect the space the program will occupy when flashed on a device. _Always_ use `cargo-size` to check how big a binary really is. -] else if lang == "de" [ +], +de: [ *WICHTIG*: ELF-Dateien enthalten Metadaten wie Debug-Informationen, sodass ihre _Größe auf der Festplatte_ _nicht_ genau den Speicherplatz widerspiegelt, den das Programm beanspruchen wird, wenn es auf ein Gerät geflasht wird. Verwenden Sie _immer_ `cargo-size`, um zu überprüfen, wie groß eine Binärdatei tatsächlich ist. -] else if lang == "zh" [ +], +zh: [ *重要*: ELF文件包含像是调试信息这样的元数据,因此它们在_硬盘上的尺寸_没有正确地反应处程序被烧录到设备上时将占据的空间的大小。要_一直_使用`cargo-size`检查一个二进制项的大小。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ `cargo-objdump` can be used to disassemble the binary. -] else if lang == "de" [ +], +de: [ Mit `cargo-objdump` lässt sich die Binärdatei disassemblieren. -] else if lang == "zh" [ +], +zh: [ `cargo-objdump` 能用来反编译二进制项。 -] else { todo } +])) ```console cargo objdump --bin app --release -- --disassemble --no-show-raw-insn --print-imm-hex @@ -695,32 +778,38 @@ cargo objdump --bin app --release -- --disassemble --no-show-raw-insn --print-im #quote(block: true)[ #let ln_issue269 = link("https://github.com/rust-embedded/book/issues/269") -#if lang == "en" [ +#tr(( +en: [ *NOTE* if the above command complains about `Unknown command line argument` see the following bug report: #ln_issue269 -] else if lang == "de" [ +], +de: [ *HINWEIS*: Falls der obige Befehl die Fehlermeldung „Unbekanntes Befehlszeilenargument" ausgibt, siehe den folgenden Fehlerbericht: #ln_issue269 -] else if lang == "zh" [ +], +zh: [ *注意* 如果上面的命令抱怨 `Unknown command line argument` 看下面的bug报告:#ln_issue269 -] else { todo } +])) ] #quote(block: true)[ -#if lang == "en" [ +#tr(( +en: [ *NOTE* this output can differ on your system. New versions of rustc, LLVM and libraries can generate different assembly. We truncated some of the instructions to keep the snippet small. -] else if lang == "de" [ +], +de: [ *HINWEIS* Diese Ausgabe kann auf Ihrem System abweichen. Neuere Versionen von rustc, LLVM und Bibliotheken können unterschiedlichen Assemblercode erzeugen. Wir haben einige der Befehle gekürzt, um den Ausschnitt kurz zu halten. -] else if lang == "zh" [ +], +zh: [ *注意* 在你的系统上这个输出可能不一样。rustc, LLVM 和库的新版本能产出不同的汇编。我们截取了一些指令 -] else { todo } +])) ] ```text @@ -762,93 +851,112 @@ HardFault: 663: ``` -= #(if lang == "en" [Running] - else if lang == "de" [Ausführen] - else if lang == "zh" [运行] - else { todo }) += #tr(( + en: [Running], + de: [Ausführen], + zh: [运行], +)) -#if lang == "en" [ +#tr(( +en: [ Next, let's see how to run an embedded program on QEMU! This time we'll use the `hello` example which actually does something. By default, this example uses `[defmt]` and RTT to print text. -] else if lang == "de" [ +], +de: [ Als Nächstes schauen wir uns an, wie man ein eingebettetes Programm auf QEMU ausführt! Diesmal verwenden wir das `hello`-Beispiel, das tatsächlich etwas tut. Standardmäßig nutzt dieses Beispiel `[defmt]` und RTT, um Text auszugeben. -] else if lang == "zh" [ +], +zh: [ #todoupd("zh") 接下来,让我们看一个嵌入式程序是如何在QEMU上运行的!此刻我们将使用 `hello` 示例,来做些真正的事。 -] else { todo } +])) #quote(block: true)[ -#if lang == "en" [ +#tr(( +en: [ *NOTE* `defmt` is a third-party dependency (i.e.~non-core) widely used in the Embedded Rust ecosystem. -] else if lang == "de" [ +], +de: [ *HINWEIS* `defmt` ist eine Abhängigkeit eines Drittanbieters (d. h. keine Kernkomponente), die im Embedded-Rust-Ökosystem weit verbreitet ist. -] else { todo } +])) ] -#if lang == "en" [ +#tr(( +en: [ In order to read and decode the messages produced by `defmt` in the host, we need to switch the RTT transport output to semihosting. When using real hardware this requires a debug session but when using QEMU this Just Works. -] else if lang == "de" [ +], +de: [ Um die von `defmt` im Host erzeugten Nachrichten lesen und entschlüsseln zu können, müssen wir die RTT-Transportausgabe auf „Semihosting" umstellen. Bei Verwendung von echter Hardware erfordert dies eine Debug-Sitzung, bei Verwendung von QEMU funktioniert dies jedoch einfach so. -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Let's switch the dependencies: -] else if lang == "de" [ +], +de: [ Stellen wir die Abhängigkeiten um: -] else { todo } +])) ```console cargo remove defmt-rtt cargo add defmt-semihosting ``` -#if lang == "en" [ +#tr(( +en: [ Open `src/lib.rs` and replace `use defmt_rtt as _;` by `use defmt_semihosting as _;` -] else if lang == "de" [ +], +de: [ Öffnen Sie `src/lib.rs` und ersetzen Sie `use defmt_rtt as _;` durch `use defmt_semihosting as _;`. -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Now we can build the example: -] else if lang == "de" [ +], +de: [ Nun können wir das Beispiel kompilieren: -] else { todo } +])) ```console cargo build --bin hello ``` -#if lang == "en" [ +#tr(( +en: [ The output binary will be located at `target/thumbv7m-none-eabi/debug/hello`. -] else if lang == "de" [ +], +de: [ Die ausgegebene Binärdatei befindet sich unter `target/thumbv7m-none-eabi/debug/hello`. -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ To run this binary on QEMU, the following command would be usually enough: -] else if lang == "de" [ +], +de: [ Um diese Binärdatei unter QEMU auszuführen, reicht in der Regel der folgende Befehl aus: -] else if lang == "zh" [ +], +zh: [ 为了在QEMU上运行这个二进制项,执行下列的命令: -] else { todo } +])) ```console qemu-system-arm \ @@ -860,14 +968,16 @@ qemu-system-arm \ ``` #let ln_run = link("https://github.com/knurling-rs/defmt/tree/main/qemu-run/")[`qemu-run`] -#if lang == "en" [ +#tr(( +en: [ In our case, since we use `defmt`, the host will not be able to decode the output. Instead, we will need a tool by Ferrous Systems named #ln_run: -] else if lang == "de" [ +], +de: [ Da wir in unserem Fall `defmt` verwenden, kann der Host die Ausgabe nicht dekodieren. Stattdessen benötigen wir ein Werkzeug von Ferrous Systems namens #ln_run: -] else { todo } +])) ```console git clone git@github.com:knurling-rs/defmt.git @@ -879,13 +989,15 @@ cargo run -- --machine lm3s6965evb ../qemu-rs/target/thumbv7m-none-eabi/debug/he Hello, world! ``` -#if lang == "en" [ +#tr(( +en: [ The command should successfully exit (exit code = 0) after printing the text. On \*nix you can check that with the following command: -] else if lang == "de" [ +], +de: [ Der Befehl sollte nach der Ausgabe des Textes erfolgreich beendet werden (Exit-Code = 0). Unter \*nix können Sie dies mit dem folgenden Befehl überprüfen: -] else { todo } +])) ```console echo $? @@ -895,7 +1007,8 @@ echo $? 0 ``` -#if lang == "en" [ +#tr(( +en: [ Let's break down that QEMU command: - `qemu-system-arm`. This is the QEMU emulator. There are a few variants of these QEMU binaries; this one does full _system_ emulation of @@ -912,7 +1025,8 @@ echo $? stdout, stderr and stdin and create files on the host. - `-kernel $file`. This tells QEMU which binary to load and run on the emulated machine. -] else if lang == "de" [ +], +de: [ Schauen wir uns diesen QEMU-Befehl einmal genauer an: - `qemu-system-arm`. Dies ist der QEMU-Emulator. Es gibt einige Varianten dieser QEMU-Binärdateien; diese hier führt eine vollständige @@ -932,7 +1046,8 @@ echo $? und die Host-Eingabe (stdin) zu nutzen sowie Dateien auf dem Host zu erstellen. - `-kernel $file`. Damit wird QEMU mitgeteilt, welche Binärdatei auf der emulierten Maschine geladen und ausgeführt werden soll. -] else if lang == "zh" [ +], +zh: [ #todoupd("zh") 让我们看看QEMU命令: - `qemu-system-arm`。这是QEMU仿真器。这些QEMU二进制项有一些变体,这个仿真器能做ARM机器的全系统仿真。 @@ -946,19 +1061,21 @@ echo $? 输入这么长的QEMU命令太费功夫了!我们可以设置一个自定义运行器(runner)简化步骤。`.cargo/config.toml` 有一个被注释掉的,可以调用QEMU的运行器。让我们去掉注释。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Typing out that long QEMU command is too much work! We can set a custom runner to simplify the process. `.cargo/config.toml` has a commented out runner that invokes QEMU; let's uncomment it: -] else if lang == "de" [ +], +de: [ Das Eintippen dieses langen QEMU-Befehls ist viel zu mühsam! Wir können einen benutzerdefinierten Runner einrichten, um den Vorgang zu vereinfachen. In der Datei `.cargo/config.toml` gibt es einen auskommentierten Runner, der QEMU aufruft; entfernen wir die Auskommentierung: -] else { todo } +])) ```console head -n3 .cargo/config.toml @@ -966,27 +1083,29 @@ head -n3 .cargo/config.toml #raw(block: true, lang: "toml", "[target.thumbv7m-none-eabi] -# " + if lang == "en" { - "uncomment this to make `cargo run` execute programs on QEMU" - } else if lang == "de" { - "Entferne den Kommentar hier, damit `cargo run` Programme auf QEMU ausfuehrt" - } else { todos } + " +# " + ts(( + en: "uncomment this to make `cargo run` execute programs on QEMU", + de: "Entferne den Kommentar hier, damit `cargo run` Programme auf QEMU ausfuehrt", + )) + " runner = \"qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel\" ") -#if lang == "en" [ +#tr(( +en: [ This runner only applies to the `thumbv7m-none-eabi` target, which is our default compilation target. Now `cargo run` will compile the program and run it on QEMU: -] else if lang == "de" [ +], +de: [ Dieser Runner gilt nur für das Ziel `thumbv7m-none-eabi`, das unser Standard-Kompilierungsziel ist. Nun kompiliert `cargo run` das Programm und führt es auf QEMU aus: -] else if lang == "zh" [ +], +zh: [ 这个运行器只会应用于 `thumbv7m-none-eabi` 目标,它是我们的默认编译目标。现在 `cargo run` 将会编译程序且在QEMU上运行它。 -] else { todo } +])) ```console cargo ru( @@ -1001,61 +1120,77 @@ cargo ru( Hello, world! ``` -= #(if lang == "en" [Debugging] - else if lang == "de" [Fehlerbehebung (Debugging)] - else if lang == "zh" [调试] - else { todo }) += #tr(( + en: [Debugging], + de: [Fehlerbehebung (Debugging)], + zh: [调试], +)) -#if lang == "en" [ +#tr(( +en: [ Debugging is critical to embedded development. Let's see how it's done. -] else if lang == "de" [ +], +de: [ Das Debuggen ist für die Embedded-Entwicklung von entscheidender Bedeutung. Schauen wir uns einmal an, wie es funktioniert. -] else if lang == "zh" [ +], +zh: [ 对于嵌入式开发来说,调试非常重要。让我们来看下如何调试它。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Debugging an embedded device involves _remote_ debugging as the program that we want to debug won't be running on the machine that's running the debugger program (GDB or LLDB). -] else if lang == "de" [ +], +de: [ Das Debuggen eines Embedded-Geräts erfolgt _remote_, da das Programm, das wir debuggen möchten, nicht auf dem Rechner läuft, auf dem das Debugger-Programm (GDB oder LLDB) ausgeführt wird. -] else if lang == "zh" [ +], +zh: [ 因为我们想要调试的程序所运行的机器上并没有运行一个调试器程序(GDB或者LLDB),所以调试一个嵌入式设备就涉及到了 _远程_ 调试 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Remote debugging involves a client and a server. In a QEMU setup, the client will be a GDB (or LLDB) process and the server will be the QEMU process that's also running the embedded program. -] else if lang == "de" [ +], +de: [ Beim Remote-Debugging sind ein Client und ein Server beteiligt. In einer QEMU-Umgebung ist der Client ein GDB- (oder LLDB-)Prozess und der Server der QEMU-Prozess, auf dem auch das eingebettete Programm läuft. -] else if lang == "zh" [ +], +zh: [ 远程调试涉及一个客户端和一个服务器。在QEMU的情况中,客户端将是一个GDB(或者LLDM)进程且服务器将会是运行着嵌入式程序的QEMU进程。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ In this section we'll use the `hello` example we already compiled. -] else if lang == "de" [ +], +de: [ In diesem Abschnitt verwenden wir das bereits kompilierte Beispiel `hello`. -] else if lang == "zh" [ +], +zh: [ 在这部分,我们要使用我们已经编译的 `hello` 示例。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ The first debugging step is to launch QEMU in debugging mode: -] else if lang == "de" [ +], +de: [ Der erste Schritt beim Debuggen besteht darin, QEMU im Debugging-Modus zu starten: -] else if lang == "zh" [ +], +zh: [ 调试的第一步是在调试模式中启动QEMU: -] else { todo } +])) ```console qemu-system-arm \ @@ -1068,7 +1203,8 @@ qemu-system-arm \ -kernel target/thumbv7m-none-eabi/debug/examples/hello ``` -#if lang == "en" [ +#tr(( +en: [ This command won't print anything to the console and will block the terminal. We have passed two extra flags this time: - `-gdb tcp::3333`. This tells QEMU to wait for a GDB connection on TCP @@ -1076,7 +1212,8 @@ qemu-system-arm \ - `-S`. This tells QEMU to freeze the machine at startup. Without this the program would have reached the end of main before we had a chance to launch the debugger! -] else if lang == "de" [ +], +de: [ Dieser Befehl gibt nichts auf der Konsole aus und blockiert das Terminal. Wir haben diesmal zwei zusätzliche Flags übergeben: - `-gdb tcp::3333`. Damit wird QEMU angewiesen, auf eine GDB-Verbindung @@ -1085,51 +1222,61 @@ qemu-system-arm \ einzufrieren. Ohne diese Option hätte das Programm das Ende der Funktion `main` erreicht, bevor wir die Gelegenheit gehabt hätten, den Debugger zu starten! -] else if lang == "zh" [ +], +zh: [ 这个命令将不打印任何东西到调试台上,且将会阻塞住终端。此刻我们还传递了两个额外的标志。 - `-gdb tcp::3333`。这告诉QEMU在3333的TCP端口上等待一个GDB连接。 - `-S`。这告诉QEMU在启动时,冻结机器。没有这个,在我们有机会启动调试器之前,程序有可能已经到达了主程序的底部了! -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Next we launch GDB in another terminal and tell it to load the debug symbols of the example: -] else if lang == "de" [ +], +de: [ Als Nächstes starten wir GDB in einem anderen Terminal und weisen es an, die Debug-Symbole des Beispiels zu laden: -] else if lang == "zh" [ +], +zh: [ 接下来我们在另一个终端启动GDB,且告诉它去加载示例的调试符号。 -] else { todo } +])) ```console gdb-multiarch -q target/thumbv7m-none-eabi/debug/examples/hello ``` -#if lang == "en" [ +#tr(( +en: [ *NOTE*: you might need another version of gdb instead of `gdb-multiarch` depending on which one you installed in the installation chapter. This could also be `arm-none-eabi-gdb` or just `gdb`. -] else if lang == "de" [ +], +de: [ *HINWEIS*: Möglicherweise benötigen Sie anstelle von `gdb-multiarch` eine andere Version von gdb, je nachdem, welche Sie im Kapitel zur Installation installiert haben. Dies könnte auch `arm-none-eabi-gdb` oder einfach nur `gdb` sein. -] else if lang == "zh" [ +], +zh: [ *注意*: 你可能需要另一个gdb版本而不是 `gdb-multiarch`,取决于你在安装章节中安装了哪个。这个可能是 `arm-none-eabi-gdb` 或者只是 `gdb`。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Then within the GDB shell we connect to QEMU, which is waiting for a connection on TCP port 3333. -] else if lang == "de" [ +], +de: [ Anschließend stellen wir innerhalb der GDB-Shell eine Verbindung zu QEMU her, das auf dem TCP-Port 3333 auf eine Verbindung wartet. -] else if lang == "zh" [ +], +zh: [ 然后在GDB shell中,我们连接QEMU,QEMU正在等待一个在3333 TCP端口上的连接。 -] else { todo } +])) ```console target remote :3333 @@ -1141,72 +1288,87 @@ Reset () at $REGISTRY/cortex-m-rt-0.6.1/src/lib.rs:473 473 pub unsafe extern "C" fn Reset() -> ! { ``` -#if lang == "en" [ +#tr(( +en: [ You'll see that the process is halted and that the program counter is pointing to a function named `Reset`. That is the reset handler: what Cortex-M cores execute upon booting. -] else if lang == "de" [ +], +de: [ Sie werden feststellen, dass der Prozess angehalten wurde und der Programmzähler auf eine Funktion namens `Reset` zeigt. Das ist der Reset-Handler: das, was Cortex-M-Kerne beim Booten ausführen. -] else if lang == "zh" [ +], +zh: [ 你将看到,进程被挂起了,程序计数器正指向一个名为 `Reset` 的函数。那是 reset 句柄:Cortex-M 内核在启动时执行的中断函数。 -] else { todo } +])) #quote(block: true)[ -#if lang == "en" [ +#tr(( +en: [ Note that on some setup, instead of displaying the line `Reset () at $REGISTRY/cortex-m-rt-0.6.1/src/lib.rs:473` as shown above, gdb may print some warnings like: -] else if lang == "de" [ +], +de: [ Beachten Sie, dass gdb in manchen Konfigurationen anstelle der oben gezeigten Zeile `Reset () at $REGISTRY/cortex-m-rt-0.6.1/src/lib.rs:473` möglicherweise Warnungen wie die folgenden ausgibt: -] else if lang == "zh" [ +], +zh: [ 注意在一些配置中,可能不会像上面一样,显示`Reset() at $REGISTRY/cortex-m-rt-0.6.1/src/lib.rs:473`,gdb可能打印一些警告,比如: -] else { todo } +])) `core::num::bignum::Big32x40::mul_small () at src/libcore/num/bignum.rs:254` `src/libcore/num/bignum.rs: No such file or directory.` -#if lang == "en" [ +#tr(( +en: [ That's a known glitch. You can safely ignore those warnings, you're most likely at Reset(). -] else if lang == "de" [ +], +de: [ Das ist ein bekannter Fehler. Sie können diese Warnungen getrost ignorieren, da Sie sich höchstwahrscheinlich bei `Reset()` befinden. -] else if lang == "zh" [ +], +zh: [ 那是一个已知的小bug,你可以安全地忽略这些警告,你非常大可能已经进入Reset()了。 -] else { todo } +])) ] -#if lang == "en" [ +#tr(( +en: [ This reset handler will eventually call our main function. Let's skip all the way there using a breakpoint and the `continue` command. To set the breakpoint, let's first take a look where we would like to break in our code, with the `list` command. -] else if lang == "de" [ +], +de: [ Dieser Reset-Handler ruft schließlich unsere Hauptfunktion auf. Lassen Sie uns den gesamten Weg dorthin mithilfe eines Haltepunkts und des Befehls `continue` überspringen. Um den Haltepunkt zu setzen, schauen wir uns zunächst mit dem Befehl `list` an, an welcher Stelle in unserem Code wir anhalten möchten. -] else if lang == "zh" [ +], +zh: [ 这个reset句柄最终将调用我们的主函数,让我们使用一个断点和`continue`命令跳过所有的步骤。为了设置断点,让我们首先看下我们想要在我们代码哪里打断点,使用`list`指令 -] else { todo } +])) ```console list main ``` -#if lang == "en" [ +#tr(( +en: [ This will show the source code, from the file examples/hello.rs. -] else if lang == "de" [ +], +de: [ Dadurch wird der Quellcode aus der Datei „examples/hello.rs" angezeigt. -] else if lang == "zh" [ +], +zh: [ 这将显示从examples/hello.rs文件来的源代码。 -] else { todo } +])) ```text 6 use panic_halt as _; @@ -1221,30 +1383,36 @@ list main 15 // exit QEMU ``` -#if lang == "en" [ +#tr(( +en: [ We would like to add a breakpoint just before the "Hello, world!", which is on line 13. We do that with the `break` command: -] else if lang == "de" [ +], +de: [ Wir möchten einen Haltepunkt direkt vor „Hello, world!" setzen, das sich in Zeile 13 befindet. Dazu verwenden wir den Befehl `break`: -] else if lang == "zh" [ +], +zh: [ 我们想要在"Hello, world!"之前添加一个断点,在13行那里。我们可以使用`break`命令 -] else { todo } +])) ```console break 13 ``` -#if lang == "en" [ +#tr(( +en: [ We can now instruct gdb to run up to our main function, with the `continue` command: -] else if lang == "de" [ +], +de: [ Wir können gdb nun mit dem Befehl `continue` anweisen, bis zu unserer Hauptfunktion weiterzulaufen: -] else if lang == "zh" [ +], +zh: [ 我们现在能使用`continue`命令指示gdb运行到我们的主函数。 -] else { todo } +])) ```console continue @@ -1257,15 +1425,18 @@ Breakpoint 1, hello::__cortex_m_rt_main () at examples\hello.rs:13 13 hprintln!("Hello, world!").unwrap(); ``` -#if lang == "en" [ +#tr(( +en: [ We are now close to the code that prints "Hello, world!". Let's move forward using the `next` command. -] else if lang == "de" [ +], +de: [ Wir sind nun fast bei dem Code angelangt, der „Hello, world!" ausgibt. Machen wir weiter mit dem Befehl `next`. -] else if lang == "zh" [ +], +zh: [ 我们现在靠近打印"Hello, world!"的代码。让我们使用`next`命令继续前进。 -] else { todo } +])) ```console next @@ -1275,28 +1446,34 @@ next 16 debug::exit(debug::EXIT_SUCCESS); ``` -#if lang == "en" [ +#tr(( +en: [ At this point you should see "Hello, world!" printed on the terminal that's running `qemu-system-arm`. -] else if lang == "de" [ +], +de: [ An dieser Stelle sollte auf dem Terminal, auf dem `qemu-system-arm` läuft, „Hello, world!" angezeigt werden. -] else if lang == "zh" [ +], +zh: [ 在这里,你应该看到 "Hello, world!" 被打印到正在运行 `qemu-system-arm` 的终端上。 -] else { todo } +])) ```text $ qemu-system-arm (..) Hello, world! ``` -#if lang == "en" [ +#tr(( +en: [ Calling `next` again will terminate the QEMU process. -] else if lang == "de" [ +], +de: [ Ein erneuter Aufruf von `next` beendet den QEMU-Prozess. -] else if lang == "zh" [ +], +zh: [ 再次调用`next`将会终止QEMU进程。 -] else { todo } +])) ```console next @@ -1306,13 +1483,16 @@ next [Inferior 1 (Remote target) exited normally] ``` -#if lang == "en" [ +#tr(( +en: [ You can now exit the GDB session. -] else if lang == "de" [ +], +de: [ Sie können die GDB-Sitzung nun beenden. -] else if lang == "zh" [ +], +zh: [ 你现在能退出GDB的会话了。 -] else { todo } +])) ```console quit diff --git a/typ/start/registers.typ b/typ/start/registers.typ index ea127043..ffd59c88 100644 --- a/typ/start/registers.typ +++ b/typ/start/registers.typ @@ -1,20 +1,22 @@ #import "../config.typ": * -#h1(offset: whole, - if lang == "en" [Memory Mapped Registers] - else if lang == "de" [Im Speicher abgebildete Register] - else if lang == "zh" [存储映射的寄存器(Memory-Mapped Registers)] - else { todo }) +#h1(offset: whole, tr(( + en: [Memory Mapped Registers], + de: [Im Speicher abgebildete Register], + zh: [存储映射的寄存器(Memory-Mapped Registers)], +))) -#if lang == "en" [ +#tr(( +en: [ Embedded systems can only get so far by executing normal Rust code and moving data around in RAM. If we want to get any information into or out of our system (be that blinking an LED, detecting a button press or communicating with an off-chip peripheral on some sort of bus) we're going to have to dip into the world of Peripherals and their 'memory mapped registers'. -] else if lang == "de" [ +], +de: [ Bei eingebetteten Systemen stößt man mit der reinen Ausführung von Standard-Rust-Code und dem Verschieben von Daten im Arbeitsspeicher (RAM) irgendwann an Grenzen. Wenn wir Informationen in das System @@ -23,16 +25,18 @@ externen Peripheriekomponente über einen Bus --, müssen wir uns mit der Welt der Peripherieeinheiten und deren „im Speicher abgebildeten Registern" (memory-mapped registers) befassen. -] else if lang == "zh" [ +], +zh: [ 嵌入式系统想要继续执行下去,只有通过执行常规的Rust代码并在RAM间移动数据才行。如果我们想要获取或者发出信息(点亮一个LED,发现一个按钮按下或者在总线上与芯片外设通信),我们不得不深入了解外设和它们的"存储映射的寄存器"。 -] else { todo } +])) #let ln_cortex = link("https://crates.io/crates/cortex-m")[cortex-m] #let ln_tm4c123x = link("https://crates.io/crates/tm4c123x")[tm4c123x] #let ln_f30x = link("https://crates.io/crates/stm32f30x")[stm32f30x] #let ln_hal = link("https://crates.io/crates/embedded-hal")[embedded-hal] #let ln_f3 = link("https://crates.io/crates/stm32f3-discovery")[stm32f3-discovery] -#if lang == "en" [ +#tr(( +en: [ You may well find that the code you need to access the peripherals in your micro-controller has already been written, at one of the following levels: @@ -65,7 +69,8 @@ pre-configuring various peripherals and GPIO pins to suit the specific developer kit or board you are using, such as #ln_f3 for the STM32F3DISCOVERY board. -] else if lang == "de" [ +], +de: [ Möglicherweise stellen Sie fest, dass der für den Zugriff auf die Peripherie Ihres Mikrocontrollers erforderliche Code bereits auf einer der folgenden Ebenen implementiert wurde: @@ -101,7 +106,8 @@ passend für das jeweils verwendete Entwickler-Kit oder Board vorkonfigurieren -- wie zum Beispiel #ln_f3 für das STM32F3DISCOVERY-Board. -] else if lang == "zh" [ +], +zh: [ 你可能会发现,访问你的微控制器外设所需要的代码,已经存在于下面的某个抽象层中了。 - Micro-architecture Crate(微架构库) - 这个库拥有任何对于微控制器的处理器内核来说经常会用到的程序,也包括在这些微控制器中的通用外设。比如 @@ -117,21 +123,24 @@ #ln_hal 的信息。 - Board Crate(开发板库) - 这些Crate通过预配置不同的外设和GPIO管脚再进行了一层抽象以适配你正在使用的特定的开发者工具或者开发板,比如对于STM32F3DISCOVERY开发板来说,是#ln_f3 -] else { todo } +])) -= #(if lang == "en" [Board Crate] - else if lang == "de" [Board Crate] - else if lang == "zh" [开发板Crate (Board Crate)] - else { todo }) += #tr(( + en: [Board Crate], + de: [Board Crate], + zh: [开发板Crate (Board Crate)], +)) -#if lang == "en" [ +#tr(( +en: [ A board crate is the perfect starting point, if you're new to embedded Rust. They nicely abstract the HW details that might be overwhelming when starting studying this subject, and makes standard tasks easy, like turning a LED on or off. The functionality it exposes varies a lot between boards. Since this book aims at staying hardware agnostic, the board crates won't be covered by this book. -] else if lang == "de" [ +], +de: [ Eine Board-Crate ist der ideale Ausgangspunkt für den Einstieg in Embedded Rust. Sie abstrahiert auf angenehme Weise die Hardware-Details, die Anfänger in diesem Bereich oft überfordern können, und erleichtert @@ -139,63 +148,74 @@ bereitgestellte Funktionsumfang variiert jedoch stark von Board zu Board. Da dieses Buch hardwareunabhängig bleiben soll, werden Board-Crates hier nicht behandelt. -] else if lang == "zh" [ +], +zh: [ 如果你是嵌入式Rust新手,board crate是一个完美的开始。它们很好地抽象出了,在开始学习这个项目时,需要耗费心力了解的硬件细节,使得标准工作,像是打开或者关闭LED,变得简单。不同的板子间,它们提供的功能变化很大。因为这本书是不假设我们使用的是何种板子,所以这本书不会提到board crate。 -] else { todo } +])) #let url_disco = "https://rust-embedded.github.io/discovery/" -#if lang == "en" [ +#tr(( +en: [ If you want to experiment with the STM32F3DISCOVERY board, it is highly recommended to take a look at the #ln_f3 board crate, which provides functionality to blink the board LEDs, access its compass, bluetooth and more. The #link(url_disco)[Discovery] book offers a great introduction to the use of a board crate. -] else if lang == "de" [ +], +de: [ Wenn Sie mit dem STM32F3DISCOVERY-Board experimentieren möchten, ist es sehr empfehlenswert, sich das #ln_f3;-Board-Crate anzusehen; dieses bietet Funktionen, um die LEDs des Boards blinken zu lassen sowie auf den Kompass, Bluetooth und mehr zuzugreifen. Das #link(url_disco)[Discovery]-Buch bietet eine hervorragende Einführung in die Verwendung eines Board-Crates. -] else if lang == "zh" [ +], +zh: [ 如果你想要用STM32F3DISCOVERY开发板做实验,强烈建议看一下#ln_f3;开发板crate,它提供了闪烁LEDs,访问它的指南针,蓝牙和其它的功能。#link(url_disco)[Discovery]书对于一个board crate的用法提供一个很好的介绍。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ But if you're working on a system that doesn't yet have dedicated board crate, or you need functionality not provided by existing crates, read on as we start from the bottom, with the micro-architecture crates. -] else if lang == "de" [ +], +de: [ Wenn Sie jedoch an einem System arbeiten, für das es noch kein dediziertes Board-Crate gibt, oder wenn Sie Funktionen benötigen, die von vorhandenen Crates nicht abgedeckt werden, lesen Sie weiter: Wir beginnen ganz unten, bei den Mikroarchitektur-Crates. -] else if lang == "zh" [ +], +zh: [ 但是如果你正在使用一个还没有提供专用的board crate的系统,或者你需要的一些功能,现存的crates不提供,那我们需要从底层的微架构crates开始。 -] else { todo } +])) -= #(if lang == "en" [Micro-architecture crate] - else if lang == "de" [Mikroarchitektur-Crate] - else if lang == "zh" [Micro-architecture crate] - else { todo }) += #tr(( + en: [Micro-architecture crate], + de: [Mikroarchitektur-Crate], + zh: [Micro-architecture crate], +)) -#if lang == "en" [ +#tr(( +en: [ Let's look at the SysTick peripheral that's common to all Cortex-M based micro-controllers. We can find a pretty low-level API in the #ln_cortex crate, and we can use it like this: -] else if lang == "de" [ +], +de: [ Betrachten wir die SysTick-Peripherie, die allen Mikrocontrollern auf Cortex-M-Basis gemeinsam ist. Im #ln_cortex;-Crate finden wir eine recht hardwarenahe API, die sich folgendermaßen verwenden lässt: -] else if lang == "zh" [ +], +zh: [ 让我们看一下SysTick外设,SysTick外设存在于所有的Cortex-M微控制器中。我们能在#ln_cortex crate中找到一个相当底层的API,我们能像这样使用它: -] else { todo } +])) ```rust #![no_std] @@ -220,7 +240,8 @@ fn main() -> ! { } ``` -#if lang == "en" [ +#tr(( +en: [ The functions on the `SYST` struct map pretty closely to the functionality defined by the ARM Technical Reference Manual for this peripheral. There's nothing in this API about 'delaying for X @@ -229,7 +250,8 @@ fn main() -> ! { called `Peripherals::take()` - this is a special routine that guarantees that there is only one `SYST` structure in our entire program. For more on that, see the #link()[Peripherals] section. -] else if lang == "de" [ +], +de: [ Die Funktionen der `SYST`-Struktur entsprechen weitgehend der Funktionalität, die im ARM Technical Reference Manual für diese Peripherieeinheit definiert ist. Diese API bietet keine Möglichkeit, @@ -241,19 +263,22 @@ fn main() -> ! { sicherstellt, dass im gesamten Programm nur eine einzige `SYST`-Instanz existiert. Weitere Informationen dazu finden Sie im Abschnitt "#link()[Peripherien]". -] else if lang == "zh" [ +], +zh: [ `SYST`结构体上的功能,相当接近ARM技术手册为这个外设定义的功能。在这个API中没有关于 '延迟X毫秒' 的功能 - 我们不得不通过使用一个 `while` 循环来粗略地实现它。注意,我们调用了`Peripherals::take()`才能访问我们的`SYST`结构体 - 这是一个特别的程序,保障了在我们的整个程序中只存在一个`SYST`结构体实例,更多的信息可以看#link()[外设]部分。 -] else { todo } +])) -= #(if lang == "en" [Using a Peripheral Access Crate (PAC)] - else if lang == "de" [Verwendung eines Peripheral Access Crate (PAC)] - else if lang == "zh" [使用一个外设访问Crate (PAC)] - else { todo }) += #tr(( + en: [Using a Peripheral Access Crate (PAC)], + de: [Verwendung eines Peripheral Access Crate (PAC)], + zh: [使用一个外设访问Crate (PAC)], +)) -#if lang == "en" [ +#tr(( +en: [ We won't get very far with our embedded software development if we restrict ourselves to only the basic peripherals included with every Cortex-M. At some point, we're going to need to write some code that's @@ -261,7 +286,8 @@ fn main() -> ! { example, let's assume we have an Texas Instruments TM4C123 - a middling 80MHz Cortex-M4 with 256 KiB of Flash. We're going to pull in the #ln_tm4c123x crate to make use of this chip. -] else if lang == "de" [ +], +de: [ Bei der Entwicklung von Embedded-Software kommen wir nicht weit, wenn wir uns auf die grundlegenden Peripheriekomponenten beschränken, die in jedem Cortex-M enthalten sind. Irgendwann müssen wir Code schreiben, der @@ -270,12 +296,13 @@ fn main() -> ! { TM4C123 verwenden -- einen soliden 80-MHz-Cortex-M4 mit 256 KiB Flash-Speicher. Um diesen Chip nutzen zu können, binden wir das #ln_tm4c123x;-Crate ein. -] else if lang == "zh" [ +], +zh: [ 如果我们把自己只局限于每个Cortex-M拥有的基本外设,那我们的嵌入式软件开发将不会走得太远。我们准备需要写一些特定于我们正在使用的微控制器的代码。在这个例子里,让我们假设我们有一个TI的TM4C123 - 一个有256KiB Flash的中等规模的80MHz的Cortex-M4。我们用#ln_tm4c123x crate去使用这个芯片。 -] else { todo } +])) ```rust #![no_std] @@ -304,7 +331,8 @@ pub fn init() -> (Delay, Leds) { ``` #let ln_svd2rust = link("https://crates.io/crates/svd2rust")[svd2rust] -#if lang == "en" [ +#tr(( +en: [ We've accessed the `PWM0` peripheral in exactly the same way as we accessed the `SYST` peripheral earlier, except we called `tm4c123x::Peripherals::take()`. As this crate was auto-generated using @@ -319,7 +347,8 @@ pub fn init() -> (Delay, Leds) { values have a special meaning), then the function is marked as `unsafe`. We can see this in the example above when setting the `load` and `compa` sub-fields using the `bits()` function. -] else if lang == "de" [ +], +de: [ Wir haben auf die `PWM0`-Peripherie auf genau dieselbe Weise zugegriffen wie zuvor auf die `SYST`-Peripherie, mit dem Unterschied, dass wir `tm4c123x::Peripherals::take()` aufgerufen haben. Da dieses Crate @@ -336,26 +365,31 @@ pub fn init() -> (Delay, Leds) { besondere Bedeutung haben), wird die Funktion als `unsafe` markiert. Dies lässt sich im obigen Beispiel beim Setzen der Unterfelder `load` und `compa` mittels der Funktion `bits()` beobachten. -] else if lang == "zh" [ +], +zh: [ 我们访问 `PWM0` 外设的方法和我们之前访问 `SYST` 的方法一样,除了我们调用的是 `tm4c123x::Peripherals::take()` 之外。因为这个crate是使用#ln_svd2rust;自动生成的,访问我们寄存器位段的函数的参数是一个闭包,而不是一个数值参数。虽然这看起来像是有了更多的代码,但是Rust编译器能使用这个闭包为我们执行一系列检查,且产生的机器码十分接近手写的汇编码!如果自动生成的代码不能确保某个访问函数其所有可能的参数都能发挥作用(比如,如果寄存器被SVD定义为32位,但是没有说明某些32位值是否有特殊作用),那么该函数需要被标记为 `unsafe` 。我们能在上面看到这样的例子,我们使用 `bits()` 函数设置 `load` 和 `compa` 子域。 -] else { todo } +])) -== #(if lang in ("en", "zh") [Reading] - else if lang == "de" [Lesen] - else { todo }) +== #tr(( + en: [Reading], + de: [Lesen], + zh: [Reading], +)) #let url_struct_r = "https://docs.rs/tm4c123x/0.7.0/tm4c123x/pwm0/ctl/struct.R.html" -#if lang == "en" [ +#tr(( +en: [ The `read()` function returns an object which gives read-only access to the various sub-fields within this register, as defined by the manufacturer's SVD file for this chip. You can find all the functions available on special `R` return type for this particular register, in this particular peripheral, on this particular chip, in the #link(url_struct_r)[tm4c123x documentation]. -] else if lang == "de" [ +], +de: [ Die Funktion `read()` gibt ein Objekt zurück, das schreibgeschützten Zugriff auf die verschiedenen Teilfelder dieses Registers gewährt -- entsprechend der vom Hersteller für diesen Chip bereitgestellten @@ -363,30 +397,33 @@ pub fn init() -> (Delay, Leds) { Registers (innerhalb der jeweiligen Peripherieeinheit auf diesem Chip) verfügbaren Funktionen finden Sie in der #link(url_struct_r)[tm4c123x-Dokumentation]. -] else if lang == "zh" [ +], +zh: [ `read()` 函数返回一个对象,这个对象提供了对这个寄存器中不同子域的只读访问,由厂商提供的这个芯片的SVD文件定义。在 #link(url_struct_r)[tm4c123x documentation] 中你能找到在这个特别的返回类型 `R` 上所有可用的函数,其与特定芯片中的特定外设的特定寄存器有关。 -] else { todo } +])) #raw(block: true, lang: "rust", "if pwm.ctl.read().globalsync0().is_set() { - // " + if lang in ("en", "zh") { - "Do a thing" - } else if lang == "de" { - "Tu etwas" - } else { todos } + " + // " + ts(( + en: "Do a thing", + de: "Tu etwas", + )) + " } ") -== #(if lang in ("en", "zh") [Writing] - else if lang == "de" [Schreiben] - else { todo }) +== #tr(( + en: [Writing], + de: [Schreiben], + zh: [Writing], +)) #let url_struct_w = "https://docs.rs/tm4c123x/0.7.0/tm4c123x/pwm0/ctl/struct.W.html" -#if lang == "en" [ +#tr(( +en: [ The `write()` function takes a closure with a single argument. Typically we call this `w`. This argument then gives read-write access to the various sub-fields within this register, as defined by the @@ -396,7 +433,8 @@ pub fn init() -> (Delay, Leds) { #link(url_struct_w)[tm4c123x documentation]. Note that all of the sub-fields that we do not set will be set to a default value for us - any existing content in the register will be lost. -] else if lang == "de" [ +], +de: [ Die Funktion `write()` erwartet einen Closure mit einem einzelnen Argument. Üblicherweise bezeichnen wir dieses als `w`. Dieses Argument gewährt Lese- und Schreibzugriff auf die verschiedenen Teilfelder @@ -408,31 +446,36 @@ pub fn init() -> (Delay, Leds) { Beachten Sie, dass alle Teilfelder, die wir nicht explizit setzen, automatisch auf einen Standardwert gesetzt werden; bereits vorhandene Inhalte des Registers gehen dabei verloren. -] else if lang == "zh" [ +], +zh: [ `write()`函数使用一个只有一个参数的闭包。通常我们把这个参数叫做 `w`。然后这个参数提供对这个寄存器中不同的子域的读写访问,由厂商关于这个芯片的SVD文件提供。再一次,在 #link(url_struct_w)[tm4c123x documentation] 中你能找到 `W` 所有可用的函数,其与特定芯片中的特定外设的特定寄存器有关。注意,所有我们没有设置的子域将会被设置成一个默认值 - 将会丢失任何在这个寄存器中的现存的内容。 -] else { todo } +])) ```rust pwm.ctl.write(|w| w.globalsync0().clear_bit()); ``` -== #(if lang in ("en", "zh") [Modifying] - else if lang == "de" [Ändern] - else { todo }) +== #tr(( + en: [Modifying], + de: [Ändern], + zh: [Modifying], +)) -#if lang == "en" [ +#tr(( +en: [ If we wish to change only one particular sub-field in this register and leave the other sub-fields unchanged, we can use the `modify` function. This function takes a closure with two arguments - one for reading and one for writing. Typically we call these `r` and `w` respectively. The `r` argument can be used to inspect the current contents of the register, and the `w` argument can be used to modify the register contents. -] else if lang == "de" [ +], +de: [ Wenn wir in diesem Register nur ein bestimmtes Teilfeld ändern und die übrigen Teilfelder unverändert lassen möchten, können wir die Funktion `modify` verwenden. Diese Funktion nimmt eine Closure mit zwei @@ -441,30 +484,34 @@ pwm.ctl.write(|w| w.globalsync0().clear_bit()); Argument `r` dient dazu, den aktuellen Inhalt des Registers zu betrachten, während das Argument `w` genutzt werden kann, um den Registerinhalt zu ändern. -] else if lang == "zh" [ +], +zh: [ 如果我们希望只改变这个寄存器中某个特定的子域而让其它子域不变,我们能使用`modify`函数。这个函数使用一个具有两个参数的闭包 - 一个用来读取,一个用来写入。通常我们分别称它们为 `r` 和 `w` 。 `r` 参数能被用来查看这个寄存器现在的内容,`w` 参数能被用来修改寄存器的内容。 -] else { todo } +])) ```rust pwm.ctl.modify(|r, w| w.globalsync0().clear_bit()); ``` -#if lang == "en" [ +#tr(( +en: [ The `modify` function really shows the power of closures here. In C, we'd have to read into some temporary value, modify the correct bits and then write the value back. This means there's considerable scope for error: -] else if lang == "de" [ +], +de: [ Die Funktion `modify` veranschaulicht hier eindrucksvoll die Leistungsfähigkeit von Closures. In C müssten wir den Wert zunächst in eine temporäre Variable einlesen, die entsprechenden Bits ändern und den Wert anschließend wieder zurückschreiben. Dies birgt ein erhebliches Fehlerpotenzial: -] else if lang == "zh" [ +], +zh: [ `modify` 函数在这里真正展示了闭包的能量。在C中,我们经常需要读取一些临时值,修改成正确的比特,然后再把值写回。这意味着出现错误的范围非常大。 -] else { todo } +])) #raw(block: true, lang: "c", "uint32_t temp = pwm0.ctl.read(); @@ -472,19 +519,20 @@ temp |= PWM0_CTL_GLOBALSYNC0; pwm0.ctl.write(temp); uint32_t temp2 = pwm0.enable.read(); temp2 |= PWM0_ENABLE_PWM4EN; -pwm0.enable.write(temp); // " + if lang in ("en", "de") { - "Uh oh! Wrong variable!" - } else if lang == "zh" { - "哦 不! 错误的变量!" - } else { todos } + " +pwm0.enable.write(temp); // " + ts(( + en: "Uh oh! Wrong variable!", + zh: "哦 不! 错误的变量!", + )) + " ") -= #(if lang == "en" [Using a HAL crate] - else if lang == "de" [Verwendung eines HAL-Crates] - else if lang == "zh" [使用一个HAL crate] - else { todo }) += #tr(( + en: [Using a HAL crate], + de: [Verwendung eines HAL-Crates], + zh: [使用一个HAL crate], +)) -#if lang == "en" [ +#tr(( +en: [ The HAL crate for a chip typically works by implementing a custom Trait for the raw structures exposed by the PAC. Often this trait will define a function called `constrain()` for single peripherals or `split()` for @@ -501,7 +549,8 @@ pwm0.enable.write(temp); // " + if lang in ("en", "de") { into the correct state (say, by selecting the appropriate Alternate Function Mode) before passing the pin into Peripheral. All with no run-time cost! -] else if lang == "de" [ +], +de: [ Ein HAL-Crate für einen Chip funktioniert typischerweise, indem es ein spezifisches Trait für die vom PAC bereitgestellten Rohstrukturen implementiert. Oft definiert dieses Trait eine Funktion namens @@ -521,7 +570,8 @@ pwm0.enable.write(temp); // " + if lang in ("en", "de") { Pin dann in den korrekten Zustand versetzen (etwa durch Auswahl des passenden Modus für alternative Funktionen), bevor er ihn an die Peripherieeinheit übergibt. Und das alles ohne Laufzeitkosten! -] else if lang == "zh" [ +], +zh: [ 一个芯片的HAL crate是通过为PAC暴露的基础结构体们实现一个自定义Trait来发挥作用的。经常这个trait将会为某个外设定义一个被称作 `constrain()` @@ -532,15 +582,18 @@ pwm0.enable.write(temp); // " + if lang in ("en", "de") { port对象来说错误地将波特率转换为时钟滴答数也是不会发生的。一些crates甚至为每个GPIO管脚的状态定义了特定的 traits,在把管脚传递进外设前,要求用户去把一个管脚设置成正确的状态(通过选择Alternate Function模式) 。所有这些都没有运行时开销的! -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Let's see an example: -] else if lang == "de" [ +], +de: [ Schauen wir uns ein Beispiel an: -] else if lang == "zh" [ +], +zh: [ 让我们看一个例子: -] else { todo } +])) #raw(block: true, lang: "rust", "#![no_std] @@ -559,120 +612,96 @@ fn main() -> ! { let p = hal::Peripherals::take().unwrap(); let cp = hal::CorePeripherals::take().unwrap(); - // " + if lang == "en" { - "Wrap up the SYSCTL struct into an object with a higher-layer API" - } else if lang == "de" { - "Kapseln Sie die SYSCTL-Struktur in einem Objekt mit einer API auf - // hoeherer Ebene." - } else if lang == "zh" { - "将SYSCTL结构体封装成一个有更高抽象API的对象" - } else { todos } + " + // " + ts(( + en: "Wrap up the SYSCTL struct into an object with a higher-layer API", + de: "Kapseln Sie die SYSCTL-Struktur in einem Objekt mit einer API auf + // hoeherer Ebene.", + zh: "将SYSCTL结构体封装成一个有更高抽象API的对象", + )) + " let mut sc = p.SYSCTL.constrain(); - // " + if lang == "en" { - "Pick our oscillation settings" - } else if lang == "de" { - "Waehlen Sie unsere Oszillationseinstellungen aus." - } else if lang == "zh" { - "选择我们的晶振配置" - } else { todos } + " + // " + ts(( + en: "Pick our oscillation settings", + de: "Waehlen Sie unsere Oszillationseinstellungen aus.", + zh: "选择我们的晶振配置", + )) + " sc.clock_setup.oscillator = sysctl::Oscillator::Main( sysctl::CrystalFrequency::_16mhz, sysctl::SystemClock::UsePll(sysctl::PllOutputFrequency::_80_00mhz), ); - // " + if lang == "en" { - "Configure the PLL with those settings" - } else if lang == "de" { - "Konfigurieren Sie die PLL mit diesen Einstellungen." - } else if lang == "zh" { - "设置PLL" - } else { todos } + " + // " + ts(( + en: "Configure the PLL with those settings", + de: "Konfigurieren Sie die PLL mit diesen Einstellungen.", + zh: "设置PLL", + )) + " let clocks = sc.clock_setup.freeze(); - // " + if lang == "en" { - "Wrap up the GPIO_PORTA struct into an object with a higher-layer API. + // " + ts(( + en: "Wrap up the GPIO_PORTA struct into an object with a higher-layer API. // Note it needs to borrow `sc.power_control` so it can power up the GPIO - // peripheral automatically." - } else if lang == "de" { - "Kapseln Sie die `GPIO_PORTA`-Struktur in einem Objekt mit einer API der + // peripheral automatically.", + de: "Kapseln Sie die `GPIO_PORTA`-Struktur in einem Objekt mit einer API der // hoeheren Ebene. // Beachten Sie, dass es `sc.power_control` nutzen muss, um die - // Stromversorgung der GPIO-Peripherie automatisch zu aktivieren." - } else if lang == "zh" { - "把GPIO_PORTA结构体封装成一个有更高抽象API的对象 - // 注意它需要借用 `sc.power_control` 因此它能自动开启GPIO外设。" - } else { todos } + " + // Stromversorgung der GPIO-Peripherie automatisch zu aktivieren.", + zh: "把GPIO_PORTA结构体封装成一个有更高抽象API的对象 + // 注意它需要借用 `sc.power_control` 因此它能自动开启GPIO外设。", + )) + " let mut porta = p.GPIO_PORTA.split(&sc.power_control); - // " + if lang == "en" { - "Activate the UART." - } else if lang == "de" { - "Aktivieren Sie den UART." - } else if lang == "zh" { - "激活UART" - } else { todos } + " + // " + ts(( + en: "Activate the UART.", + de: "Aktivieren Sie den UART.", + zh: "激活UART", + )) + " let uart = Serial::uart0( p.UART0, - // " + if lang == "en" { - "The transmit pin" - } else if lang == "de" { - "Der Sende-Pin" - } else if lang == "zh" { - "激活UART" - } else { todos } + " + // " + ts(( + en: "The transmit pin", + de: "Der Sende-Pin", + zh: "激活UART", + )) + " porta .pa1 .into_af_push_pull::(&mut porta.control), - // " + if lang == "en" { - "The receive pin" - } else if lang == "de" { - "Der Empfangs-Pin" - } else if lang == "zh" { - "接收管脚" - } else { todos } + " + // " + ts(( + en: "The receive pin", + de: "Der Empfangs-Pin", + zh: "接收管脚" + )) + " porta .pa0 .into_af_push_pull::(&mut porta.control), - // " + if lang == "en" { - "No RTS or CTS required" - } else if lang == "de" { - "Kein RTS oder CTS erforderlich" - } else if lang == "zh" { - "不需要RTS或者CTS" - } else { todos } + " + // " + ts(( + en: "No RTS or CTS required", + de: "Kein RTS oder CTS erforderlich", + zh: "不需要RTS或者CTS" + )) + " (), (), - // " + if lang == "en" { - "The baud rate" - } else if lang == "de" { - "Die Baudrate" - } else if lang == "zh" { - "波特率" - } else { todos } + " + // " + ts(( + en: "The baud rate", + de: "Die Baudrate", + zh: "波特率", + )) + " 115200_u32.bps(), - // " + if lang == "en" { - "Output handling" - } else if lang == "de" { - "Ausgabeverarbeitung" - } else if lang == "zh" { - "输出处理" - } else { todos } + " + // " + ts(( + en: "Output handling", + de: "Ausgabeverarbeitung", + zh: "输出处理", + )) + " NewlineMode::SwapLFtoCRLF, - // " + if lang == "en" { - "We need the clock rates to calculate the baud rate divisors" - } else if lang == "de" { - "Wir benoetigen die Taktfrequenzen, um die Baudraten-Teiler zu - // berechnen." - } else if lang == "zh" { - "我们需要时钟频率去计算波特率除法器(divisors)" - } else { todos } + " + // " + ts(( + en: "We need the clock rates to calculate the baud rate divisors", + de: "Wir benoetigen die Taktfrequenzen, um die Baudraten-Teiler zu + // berechnen.", + zh: "我们需要时钟频率去计算波特率除法器(divisors)", + )) + " &clocks, - // " + if lang == "en" { - "We need this to power up the UART peripheral" - } else if lang == "de" { - "Wir benoetigen dies, um die UART-Peripherie zu aktivieren." - } else if lang == "zh" { - "我们需要这个去启动UART外设" - } else { todos } + " + // " + ts(( + en: "We need this to power up the UART peripheral", + de: "Wir benoetigen dies, um die UART-Peripherie zu aktivieren.", + zh: "我们需要这个去启动UART外设", + )) + " &sc.power_control, ); diff --git a/typ/start/semihosting.typ b/typ/start/semihosting.typ index ae493e8f..0f63e231 100644 --- a/typ/start/semihosting.typ +++ b/typ/start/semihosting.typ @@ -1,19 +1,21 @@ #import "../config.typ": * -#h1(offset: whole, - if lang == "en" [Semihosting] - else if lang == "de" [Semihosting] - else if lang == "zh" [半主机模式] - else { todo }) - -#if lang == "en" [ +#h1(offset: whole, tr(( + en: [Semihosting], + de: [Semihosting], + zh: [半主机模式], +))) + +#tr(( +en: [ Semihosting is a mechanism that lets embedded devices do I/O on the host and is mainly used to log messages to the host console. Semihosting requires a debug session and pretty much nothing else (no extra wires!) so it's super convenient to use. The downside is that it's super slow: each write operation can take several milliseconds depending on the hardware debugger (e.g.~ST-Link) you use. -] else if lang == "de" [ +], +de: [ Semihosting ist ein Mechanismus, der es eingebetteten Systemen ermöglicht, Ein-/Ausgabeoperationen über den eigenen Rechner (Host) durchzuführen; er wird hauptsächlich dazu genutzt, Meldungen auf der @@ -23,23 +25,27 @@ jedoch die sehr geringe Geschwindigkeit: Je nach verwendetem Hardware-Debugger (z. B. ST-Link) kann jeder Schreibvorgang mehrere Millisekunden in Anspruch nehmen. -] else if lang == "zh" [ +], +zh: [ 半主机模式是一种可以让嵌入式设备在主机上进行I/O操作的的机制,主要被用来记录信息到主机控制台上。半主机模式需要一个debug会话,除此之外几乎没有其它要求了,因此它非常易于使用。缺点是它非常慢:每个写操作需要几毫秒的时间,其取决于你的硬件调试器(e.g.~ST-LINK)。 -] else { todo } +])) #let ln_sh = link("https://crates.io/crates/cortex-m-semihosting")[`cortex-m-semihosting`] -#if lang == "en" [ +#tr(( +en: [ The #ln_sh crate provides an API to do semihosting operations on Cortex-M devices. The program below is the semihosting version of "Hello, world!": -] else if lang == "de" [ +], +de: [ Das #ln_sh;-Crate stellt eine API für Semihosting-Operationen auf Cortex-M-Geräten bereit. Das folgende Programm ist die Semihosting-Version von „Hello, world!": -] else if lang == "zh" [ +], +zh: [ #ln_sh crate 提供了一个API去在Cortex-M设备上执行半主机操作。下面的程序是"Hello, world!"的半主机版本。 -] else { todo } +])) ```rust #![no_main] @@ -58,16 +64,19 @@ fn main() -> ! { } ``` -#if lang == "en" [ +#tr(( +en: [ If you run this program on hardware you'll see the "Hello, world!" message within the OpenOCD logs. -] else if lang == "de" [ +], +de: [ Wenn Sie dieses Programm auf der Hardware ausführen, sehen Sie die Meldung „Hello, world!" in den OpenOCD-Protokollen. -] else if lang == "zh" [ +], +zh: [ 如果你在硬件上运行这个程序,你将会在OpenOCD的logs中看到"Hello, world!"信息。 -] else { todo } +])) ```text $ openocd @@ -76,61 +85,69 @@ Hello, world! (..) ``` -#if lang == "en" [ +#tr(( +en: [ You do need to enable semihosting in OpenOCD from GDB first: -] else if lang == "de" [ +], +de: [ Sie müssen zunächst Semihosting in OpenOCD über GDB aktivieren: -] else if lang == "zh" [ +], +zh: [ 你首先需要从GDB使能OpenOCD中的半主机模式。 -] else { todo } +])) ```console (gdb) monitor arm semihosting enable semihosting is enabled ``` -#if lang == "en" [ +#tr(( +en: [ QEMU understands semihosting operations so the above program will also work with `qemu-system-arm` without having to start a debug session. Note that you'll need to pass the `-semihosting-config` flag to QEMU to enable semihosting support; these flags are already included in the `.cargo/config.toml` file of the template. -] else if lang == "de" [ +], +de: [ QEMU unterstützt Semihosting-Operationen, sodass das obige Programm auch mit `qemu-system-arm` funktioniert, ohne dass eine Debug-Sitzung gestartet werden muss. Beachten Sie, dass Sie QEMU die Option `-semihosting-config` übergeben müssen, um die Semihosting-Unterstützung zu aktivieren; diese Optionen sind bereits in der Datei `.cargo/config.toml` der Vorlage enthalten. -] else if lang == "zh" [ +], +zh: [ QEMU理解半主机操作,因此上面的程序不需要启动一个debug会话,也能在`qemu-system-arm`中工作。注意你需要传递`-semihosting-config`标志给QEMU去使能支持半主机模式;这些标识已经被包括在模板的`.cargo/config.toml`文件中了。 -] else { todo } +])) #raw(block: true, lang: "text", -"$ # " + if lang in ("en", "zh") { - "this program will block the terminal" - } else if lang == "de" { - "Dieses Programm wird das Terminal blockieren." - } else { todos } + " +"$ # " + ts(( + en: "this program will block the terminal", + de: "Dieses Programm wird das Terminal blockieren.", + )) + " $ cargo run Running `qemu-system-arm (..) Hello, world! ") -#if lang == "en" [ +#tr(( +en: [ There's also an `exit` semihosting operation that can be used to terminate the QEMU process. Important: do *not* use `debug::exit` on hardware; this function can corrupt your OpenOCD session and you will not be able to debug more programs until you restart it. -] else if lang == "de" [ +], +de: [ Es gibt auch eine `exit`-Semihosting-Operation, mit der sich der QEMU-Prozess beenden lässt. Wichtig: Verwenden Sie `debug::exit` *nicht* auf echter Hardware; diese Funktion kann Ihre OpenOCD-Sitzung beschädigen, sodass Sie keine weiteren Programme mehr debuggen können, bis Sie die Sitzung neu starten. -] else if lang == "zh" [ +], +zh: [ `exit`半主机操作也能被用于终止QEMU进程。重要:*不要*在硬件上使用`debug::exit`;这个函数会关闭你的OpenOCD对话,这样你就不能执行其它的程序调试操作了,除了重启它。 -] else { todo } +])) ```rust #![no_main] @@ -163,34 +180,40 @@ $ echo $? 1 ``` -#if lang == "en" [ +#tr(( +en: [ One last tip: you can set the panicking behavior to `exit(EXIT_FAILURE)`. This will let you write `no_std` run-pass tests that you can run on QEMU. -] else if lang == "de" [ +], +de: [ Ein letzter Tipp: Du kannst das Verhalten bei einem Panic auf `exit(EXIT_FAILURE)` einstellen. Dadurch lassen sich `no_std`-Tests schreiben, die erfolgreich durchlaufen und unter QEMU ausgeführt werden können. -] else if lang == "zh" [ +], +zh: [ 最后一个提示:你可以将运行时恐慌(panicking)的行为设置成 `exit(EXIT_FAILURE)`。这会允许你编写可以在QEMU上运行通过的 `no_std` 测试。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ For convenience, the `panic-semihosting` crate has an "exit" feature that when enabled invokes `exit(EXIT_FAILURE)` after logging the panic message to the host stderr. -] else if lang == "de" [ +], +de: [ Praktischerweise bietet der `panic-semihosting`-Crate ein „exit"-Feature an; ist dieses aktiviert, wird `exit(EXIT_FAILURE)` aufgerufen, nachdem die Panic-Meldung auf dem `stderr` des Hosts ausgegeben wurde. -] else if lang == "zh" [ +], +zh: [ 为了方便,`panic-semihosting` crate有一个 "exit" 特性。当它使能的时候,在主机stderr上打印恐慌(painc)信息后会调用 `exit(EXIT_FAILURE)` 。 -] else { todo } +])) ```rust #![no_main] @@ -222,35 +245,41 @@ $ echo $? 1 ``` -#if lang == "en" [ +#tr(( +en: [ *NOTE*: To enable this feature on `panic-semihosting`, edit your `Cargo.toml` dependencies section where `panic-semihosting` is specified with: -] else if lang == "de" [ +], +de: [ *HINWEIS*: Um diese Funktion für `panic-semihosting` zu aktivieren, bearbeiten Sie den Abschnitt „dependencies" in Ihrer `Cargo.toml`, in dem `panic-semihosting` wie folgt angegeben ist: -] else if lang == "zh" [ +], +zh: [ *注意*: 为了在`panic-semihosting`上使能这个特性,编辑你的`Cargo.toml`依赖,`panic-semihosting`改写成: -] else { todo } +])) ```toml panic-semihosting = { version = "VERSION", features = ["exit"] } ``` #let ln_spec_defs = link("https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html")[`specifying dependencies`] -#if lang == "en" [ +#tr(( +en: [ where `VERSION` is the version desired. For more information on dependencies features check the #ln_spec_defs section of the Cargo book. -] else if lang == "de" [ +], +de: [ wobei `VERSION` für die gewünschte Version steht. Weitere Informationen zu Abhängigkeiten und Funktionen finden Sie im Abschnitt "#ln_spec_defs" des Cargo-Buchs. -] else if lang == "zh" [ +], +zh: [ `VERSION`是想要的版本。关于依赖features的更多信息查看Cargo book的#ln_spec_defs;部分。 -] else { todo } +])) diff --git a/typ/static-guarantees/design-contracts.typ b/typ/static-guarantees/design-contracts.typ index 3e4bbc73..7bc2676a 100644 --- a/typ/static-guarantees/design-contracts.typ +++ b/typ/static-guarantees/design-contracts.typ @@ -1,54 +1,56 @@ #import "../config.typ": * -#h1(offset: whole, - if lang == "en" [Design Contracts] - else if lang == "de" [Design-Verträge] - else if lang == "zh" [设计约定(design contracts)] - else { todo }) - -#if lang == "en" [ +#h1(offset: whole, tr(( + en: [Design Contracts], + de: [Design-Verträge], + zh: [设计约定(design contracts)], +))) + +#tr(( +en: [ In our last chapter, we wrote an interface that _didn't_ enforce design contracts. Let's take another look at our imaginary GPIO configuration register: -] else if lang == "de" [ +], +de: [ Im letzten Kapitel haben wir eine Schnittstelle geschrieben, die _keine_ Design-Verträge (Design Contracts) durchsetzte. Werfen wir noch einmal einen Blick auf unser fiktives GPIO-Konfigurationsregister: -] else if lang == "zh" [ +], +zh: [ 在我们的上个章节中,我们写了一个接口,但没有强制遵守设计约定。让我们再看下我们假想的GPIO配置寄存器: -] else { todo } +])) #include "gpio-table.typ" -#if lang == "en" [ +#tr(( +en: [ If we instead checked the state before making use of the underlying hardware, enforcing our design contracts at runtime, we might write code that looks like this instead: -] else if lang == "de" [ +], +de: [ Wenn wir stattdessen den Zustand überprüfen würden, bevor wir die zugrundeliegende Hardware nutzen, und dabei unsere Entwurfsverträge zur Laufzeit durchsetzen würden, könnten wir stattdessen Code schreiben, der wie folgt aussieht: -] else if lang == "zh" [ +], +zh: [ 如果在使用底层硬件之前检查硬件的状态,在运行时强制用户遵守设计约定,代码可能像这一样: -] else { todo } +])) #raw(block: true, lang: "rust", -"/// " + if lang == "en" { - "GPIO interface" - } else if lang == "de" { - "GPIO-Schnittstelle" - } else if lang == "zh" { - "GPIO接口" - } else { todos } + " +"/// " + ts(( + en: "GPIO interface", + de: "GPIO-Schnittstelle", + zh: "GPIO接口", + )) + " struct GpioConfig { - /// " + if lang == "en" { - "GPIO Configuration structure generated by svd2rust" - } else if lang == "de" { - "Von svd2rust generierte GPIO-Konfigurationsstruktur" - } else if lang == "zh" { - "由svd2rust生成的GPIO配制结构体" - } else { todos } + " + /// " + ts(( + en: "GPIO Configuration structure generated by svd2rust", + de: "Von svd2rust generierte GPIO-Konfigurationsstruktur", + zh: "由svd2rust生成的GPIO配制结构体" + )) + " periph: GPIO_CONFIG, } @@ -61,13 +63,11 @@ impl GpioConfig { pub fn set_direction(&mut self, is_output: bool) -> Result<(), ()> { if self.periph.read().enable().bit_is_clear() { - // " + if lang == "en" { - "Must be enabled to set direction" - } else if lang == "de" { - "Muss aktiviert sein, um die Richtung festzulegen." - } else if lang == "zh" { - "必须被使能配置方向" - } else { todos } + " + // " + ts(( + en: "Must be enabled to set direction", + de: "Muss aktiviert sein, um die Richtung festzulegen.", + zh: "必须被使能配置方向" + )) + " return Err(()); } @@ -80,24 +80,20 @@ impl GpioConfig { pub fn set_input_mode(&mut self, variant: InputMode) -> Result<(), ()> { if self.periph.read().enable().bit_is_clear() { - // " + if lang == "en" { - "Must be enabled to set input mode" - } else if lang == "de" { - "Muss aktiviert sein, um den Eingabemodus festzulegen." - } else if lang == "zh" { - "必须被使能配置输入模式" - } else { todos } + " + // " + ts(( + en: "Must be enabled to set input mode", + de: "Muss aktiviert sein, um den Eingabemodus festzulegen.", + zh: "必须被使能配置输入模式", + )) + " return Err(()); } if self.periph.read().direction().bit_is_set() { - // " + if lang == "en" { - "Direction must be input" - } else if lang == "de" { - "Die Richtung muss input sein." - } else if lang == "zh" { - "方向必须被设置成输入" - } else { todos } + " + // " + ts(( + en: "Direction must be input", + de: "Die Richtung muss input sein.", + zh: "方向必须被设置成输入", + )) + " return Err(()); } @@ -110,24 +106,20 @@ impl GpioConfig { pub fn set_output_status(&mut self, is_high: bool) -> Result<(), ()> { if self.periph.read().enable().bit_is_clear() { - // " + if lang == "en" { - "Must be enabled to set output status" - } else if lang == "de" { - "Muss aktiviert sein, um den Ausgangsstatus festzulegen." - } else if lang == "zh" { - "设置输出状态必须被使能" - } else { todos } + " + // " + ts(( + en: "Must be enabled to set output status", + de: "Muss aktiviert sein, um den Ausgangsstatus festzulegen.", + zh: "设置输出状态必须被使能", + )) + " return Err(()); } if self.periph.read().direction().bit_is_clear() { - // " + if lang == "en" { - "Direction must be output" - } else if lang == "de" { - "Die Richtung muss output sein." - } else if lang == "zh" { - "方向必须是输出" - } else { todos } + " + // " + ts(( + en: "Direction must be output", + de: "Die Richtung muss output sein.", + zh: "方向必须是输出", + )) + " return Err(()); } @@ -140,24 +132,20 @@ impl GpioConfig { pub fn get_input_status(&self) -> Result { if self.periph.read().enable().bit_is_clear() { - // " + if lang == "en" { - "Must be enabled to get status" - } else if lang == "de" { - "Muss aktiviert sein, um den Status abzurufen." - } else if lang == "zh" { - "获取状态必须被使能" - } else { todos } + " + // " + ts(( + en: "Must be enabled to get status", + de: "Muss aktiviert sein, um den Status abzurufen.", + zh: "获取状态必须被使能", + )) + " return Err(()); } if self.periph.read().direction().bit_is_set() { - // " + if lang == "en" { - "Direction must be input" - } else if lang == "de" { - "Die Richtung muss input sein." - } else if lang == "zh" { - "方向必须是输入" - } else { todos } + " + // " + ts(( + en: "Direction must be input", + de: "Die Richtung muss input sein.", + zh: "方向必须是输入", + )) + " return Err(()); } @@ -166,64 +154,65 @@ impl GpioConfig { } ") -#if lang == "en" [ +#tr(( +en: [ Because we need to enforce the restrictions on the hardware, we end up doing a lot of runtime checking which wastes time and resources, and this code will be much less pleasant for the developer to use. -] else if lang == "de" [ +], +de: [ Da wir die Hardware-Beschränkungen durchsetzen müssen, führen wir letztlich zahlreiche Laufzeitüberprüfungen durch -- was Zeit und Ressourcen verschwendet --, und der Code ist für Entwickler deutlich weniger angenehm zu handhaben. -] else if lang == "zh" [ +], +zh: [ 因为需要强制遵守硬件上的限制,所以最后做了很多运行时检查,它浪费了我们很多时间和资源,对于开发者来说,这个代码用起来就没那么愉快了。 -] else { todo } +])) -= #(if lang == "en" [Type States] - else if lang == "de" [Typzustände] - else if lang == "zh" [类型状态(Type states)] - else { todo }) += #tr(( + en: [Type States], + de: [Typzustände], + zh: [类型状态(Type states)], +)) -#if lang == "en" [ +#tr(( +en: [ But what if instead, we used Rust's type system to enforce the state transition rules? Take this example: -] else if lang == "de" [ +], +de: [ Was aber, wenn wir stattdessen das Typsystem von Rust nutzen würden, um die Regeln für Zustandsübergänge durchzusetzen? Betrachten wir dieses Beispiel: -] else if lang == "zh" [ +], +zh: [ 但是,如果我们让Rust的类型系统去强制遵守状态转换的规则会怎样?看下这个例子: -] else { todo } +])) #raw(block: true, lang: "rust", -"/// " + if lang == "en" { - "GPIO interface" - } else if lang == "de" { - "GPIO-Schnittstelle" - } else if lang == "zh" { - "GPIO接口" - } else { todos } + " +"/// " + ts(( + en: "GPIO interface", + de: "GPIO-Schnittstelle", + zh: "GPIO接口", + )) + " struct GpioConfig { - /// " + if lang == "en" { - "GPIO Configuration structure generated by svd2rust" - } else if lang == "de" { - "Von svd2rust generierte GPIO-Konfigurationsstruktur" - } else if lang == "zh" { - "由svd2rust产生的GPIO配置结构体" - } else { todos } + " + /// " + ts(( + en: "GPIO Configuration structure generated by svd2rust", + de: "Von svd2rust generierte GPIO-Konfigurationsstruktur", + zh: "由svd2rust产生的GPIO配置结构体", + )) + " periph: GPIO_CONFIG, enabled: ENABLED, direction: DIRECTION, mode: MODE, } -// " + if lang == "en" { - "Type states for MODE in GpioConfig" - } else if lang == "de" { - "Typzustaende fuer MODE in GpioConfig" - } else if lang == "zh" { - "GpioConfig中MODE的类型状态" - } else { todos } + " +// " + ts(( + en: "Type states for MODE in GpioConfig", + de: "Typzustaende fuer MODE in GpioConfig", + zh: "GpioConfig中MODE的类型状态", + )) + " struct Disabled; struct Enabled; struct Output; @@ -233,13 +222,11 @@ struct PulledHigh; struct HighZ; struct DontCare; -/// " + if lang == "en" { - "These functions may be used on any GPIO Pin" - } else if lang == "de" { - "Diese Funktionen koennen an jedem GPIO-Pin verwendet werden." - } else if lang == "zh" { - "这些函数可能被用于所有的GPIO管脚" - } else { todos } + " +/// " + ts(( + en: "These functions may be used on any GPIO Pin", + de: "Diese Funktionen koennen an jedem GPIO-Pin verwendet werden.", + zh: "这些函数可能被用于所有的GPIO管脚", + )) + " impl GpioConfig { pub fn into_disabled(self) -> GpioConfig { self.periph.modify(|_r, w| w.enable.disabled()); @@ -280,26 +267,22 @@ impl GpioConfig { } } -/// " + if lang == "en" { - "This function may be used on an Output Pin" - } else if lang == "de" { - "Diese Funktion kann fuer einen Ausgangspin verwendet werden." - } else if lang == "zh" { - "这个函数可能被用于一个输出管脚" - } else { todos } + " +/// " + ts(( + en: "This function may be used on an Output Pin", + de: "Diese Funktion kann fuer einen Ausgangspin verwendet werden.", + zh: "这个函数可能被用于一个输出管脚", + )) + " impl GpioConfig { pub fn set_bit(&mut self, set_high: bool) { self.periph.modify(|_r, w| w.output_mode.set_bit(set_high)); } } -/// " + if lang == "en" { - "These methods may be used on any enabled input GPIO" - } else if lang == "de" { - "Diese Methoden koennen auf jedem aktivierten Eingangs-GPIO verwendet werden." - } else if lang == "zh" { - "这些方法可能被用于任意一个使能的输入GPIO" - } else { todos } + " +/// " + ts(( + en: "These methods may be used on any enabled input GPIO", + de: "Diese Methoden koennen auf jedem aktivierten Eingangs-GPIO verwendet werden.", + zh: "这些方法可能被用于任意一个使能的输入GPIO", + )) + " impl GpioConfig { pub fn bit_is_set(&self) -> bool { self.periph.read().input_status.bit_is_set() @@ -337,124 +320,117 @@ impl GpioConfig { } ") -#if lang == "en" [ +#tr(( +en: [ Now let's see what the code using this would look like: -] else if lang == "de" [ +], +de: [ Schauen wir uns nun an, wie der Code aussehen würde, der dies verwendet: -] else if lang == "zh" [ +], +zh: [ 现在让我们看下代码如何用这个API: -] else { todo } +])) #raw(block: true, lang: "rust", "/* - * " + if lang == "en" { - "Example 1: Unconfigured to High-Z input" - } else if lang == "de" { - "Beispiel 1: Nicht konfiguriert als High-Z-Eingang" - } else if lang == "zh" { - "案例 1: 从未配置到高阻输入" - } else { todos } + " + * " + ts(( + en: "Example 1: Unconfigured to High-Z input", + de: "Beispiel 1: Nicht konfiguriert als High-Z-Eingang", + zh: "案例 1: 从未配置到高阻输入", + )) + " */ let pin: GpioConfig = get_gpio(); -// " + if lang == "en" { - "Can't do this, pin isn't enabled!" - } else if lang == "de" { - "Das ist nicht moeglich, die PIN ist nicht aktiviert!" - } else if lang == "zh" { - "不能这么做,pin没有被使能" - } else { todos } + " +// " + ts(( + en: "Can't do this, pin isn't enabled!", + de: "Das ist nicht moeglich, die PIN ist nicht aktiviert!", + zh: "不能这么做,pin没有被使能", + )) + " // pin.into_input_pull_down(); -// " + if lang == "en" { - "Now turn the pin from unconfigured to a high-z input" - } else if lang == "de" { - "Schalten Sie den Pin nun von „unkonfiguriert“ auf einen hochohmigen Eingang - // (High-Z-Eingang) um." - } else if lang == "zh" { - "现在把管脚从未配置变为高阻输入" - } else { todos } + " +// " + ts(( + en: "Now turn the pin from unconfigured to a high-z input", + de: "Schalten Sie den Pin nun von „unkonfiguriert“ auf einen hochohmigen Eingang + // (High-Z-Eingang) um.", + zh: "现在把管脚从未配置变为高阻输入", + )) + " let input_pin = pin.into_enabled_input(); -// " + if lang == "en" { - "Read from the pin" - } else if lang == "de" { - "Vom Pin lesen" - } else if lang == "zh" { - "从管脚读取" - } else { todos } + " +// " + ts(( + en: "Read from the pin", + de: "Vom Pin lesen", + zh: "从管脚读取", + )) + " let pin_state = input_pin.bit_is_set(); -// " + if lang == "en" { - "Can't do this, input pins don't have this interface!" - } else if lang == "de" { - "Das ist nicht moeglich; Eingangs-Pins verfuegen nicht über diese - // Schnittstelle!" - } else if lang == "zh" { - "不能这么做,输入管脚没有这个接口" - } else { todos } + " +// " + ts(( + en: "Can't do this, input pins don't have this interface!", + de: "Das ist nicht moeglich; Eingangs-Pins verfuegen nicht über diese + // Schnittstelle!", + zh: "不能这么做,输入管脚没有这个接口", + )) + " // input_pin.set_bit(true); /* - * " + if lang == "en" { - "Example 2: High-Z input to Pulled Low input" - } else if lang == "de" { - "Beispiel 2: Hochohmiger Eingang (High-Z) an auf Low-Pegel gezogenen Eingang" - } else if lang == "zh" { - "案例 2: 高阻输入到下拉输入" - } else { todos } + " + * " + ts(( + en: "Example 2: High-Z input to Pulled Low input", + de: "Beispiel 2: Hochohmiger Eingang (High-Z) an auf Low-Pegel gezogenen Eingang", + zh: "案例 2: 高阻输入到下拉输入", + )) + " */ let pulled_low = input_pin.into_input_pull_down(); let pin_state = pulled_low.bit_is_set(); /* - * " + if lang == "en" { - "Example 3: Pulled Low input to Output, set high" - } else if lang == "de" { - "Beispiel 3: Eingang auf Low gezogen, Ausgang auf High gesetzt" - } else if lang == "zh" { - "案例 3: 下拉输入到输出, 拉高" - } else { todos } + " + * " + ts(( + en: "Example 3: Pulled Low input to Output, set high", + de: "Beispiel 3: Eingang auf Low gezogen, Ausgang auf High gesetzt", + zh: "案例 3: 下拉输入到输出, 拉高", + )) + " */ let output_pin = pulled_low.into_enabled_output(); output_pin.set_bit(true); -// " + if lang == "en" { - "Can't do this, output pins don't have this interface!" - } else if lang == "de" { - "Das geht nicht, Ausgangspins verfuegen nicht über diese Schnittstelle!" - } else if lang == "zh" { - "不能这么做,输出管脚没有这个接口" - } else { todos } + " +// " + ts(( + en: "Can't do this, output pins don't have this interface!", + de: "Das geht nicht, Ausgangspins verfuegen nicht über diese Schnittstelle!", + zh: "不能这么做,输出管脚没有这个接口", + )) + " // output_pin.into_input_pull_down(); ") -#if lang == "en" [ +#tr(( +en: [ This is definitely a convenient way to store the state of the pin, but why do it this way? Why is this better than storing the state as an `enum` inside of our `GpioConfig` structure? -] else if lang == "de" [ +], +de: [ Dies ist zweifellos eine praktische Möglichkeit, den Zustand des Pins zu speichern, aber warum sollte man es auf diese Weise tun? Warum ist das besser, als den Zustand als `enum` innerhalb unserer `GpioConfig`-Struktur zu speichern? -] else if lang == "zh" [ +], +zh: [ 这绝对是存储管脚状态的便捷方法,但是为什么这么做?为什么这比把状态当成一个`enum`存在我们的`GpioConfig`结构体中更好? -] else { todo } +])) -= #(if lang == "en" [Compile Time Functional Safety] - else if lang == "de" [Funktionale Sicherheit zur Kompilierzeit] - else if lang == "zh" [编译时功能安全(Functional Safety)] - else { todo }) += #tr(( + en: [Compile Time Functional Safety], + de: [Funktionale Sicherheit zur Kompilierzeit], + zh: [编译时功能安全(Functional Safety)], +)) -#if lang == "en" [ +#tr(( +en: [ Because we are enforcing our design constraints entirely at compile time, this incurs no runtime cost. It is impossible to set an output mode when you have a pin in an input mode. Instead, you must walk through the states by converting it to an output pin, and then setting the output mode. Because of this, there is no runtime penalty due to checking the current state before executing a function. -] else if lang == "de" [ +], +de: [ Da wir unsere Designvorgaben vollständig zur Kompilierzeit durchsetzen, entstehen keinerlei Laufzeitkosten. Es ist unmöglich, einen Ausgabemodus einzustellen, solange sich ein Pin im Eingabemodus befindet. Stattdessen @@ -462,19 +438,23 @@ output_pin.set_bit(true); Ausgangspin umwandelt und anschließend den Ausgabemodus festlegt. Dadurch entfällt der Laufzeitaufwand für die Überprüfung des aktuellen Zustands vor der Ausführung einer Funktion. -] else if lang == "zh" [ +], +zh: [ 因为我们在编译时完全强制遵守设计约定,这造成了没有运行时开销。当管脚处于输入模式时时,是不可能设置输出模式的。必须先把它设置成一个输出管脚,然后再设置输出模式。因为在执行一个函数前会检查现在的状态,因此没有运行时消耗。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Also, because these states are enforced by the type system, there is no longer room for errors by consumers of this interface. If they try to perform an illegal state transition, the code will not compile! -] else if lang == "de" [ +], +de: [ Da diese Zustände zudem durch das Typsystem erzwungen werden, sind Fehler bei der Verwendung dieser Schnittstelle ausgeschlossen. Versucht der Nutzer einen unzulässigen Zustandsübergang, lässt sich der Code nicht kompilieren! -] else if lang == "zh" [ +], +zh: [ 也因为这些状态被类型系统强制遵守,因此没有为这个接口的使用者留太多的犯错余地。如果它们尝试执行一个非法的状态转换,代码将不会编译成功! -] else { todo } +])) diff --git a/typ/static-guarantees/gpio-table.typ b/typ/static-guarantees/gpio-table.typ index 0dd0b313..84c60852 100644 --- a/typ/static-guarantees/gpio-table.typ +++ b/typ/static-guarantees/gpio-table.typ @@ -12,95 +12,111 @@ } }, table.header( - if lang in ("en", "de") [Name] - else if lang == "zh" [名字] - else { todo }, - if lang == "en" [Bit Number(s)] - else if lang == "de" [Bit-Nummer] - else if lang == "zh" [位数(s)] - else { todo }, - if lang == "en" [Value] - else if lang == "de" [Wert] - else if lang == "zh" [值] - else { todo }, - if lang == "en" [Meaning] - else if lang == "de" [Bedeutung] - else if lang == "zh" [含义] - else { todo }, - if lang == "en" [Notes] - else if lang == "de" [Hinweise] - else if lang == "zh" [注释] - else { todo }, + tr(( + en: [Name], + de: [Name], + zh: [名字], + )), + tr(( + en: [Bit Number(s)], + de: [Bit-Nummer], + zh: [位数(s)], + )), + tr(( + en: [Value], + de: [Wert], + zh: [值], + )), + tr(( + en: [Meaning], + de: [Bedeutung], + zh: [含义], + )), + tr(( + en: [Notes], + de: [Hinweise], + zh: [注释], + )), ), table.cell(rowspan: 2)[enable], table.cell(rowspan: 2)[0], [0], [disabled], - if lang == "en" [Disables the GPIO] - else if lang == "de" [Deaktiviert den GPIO] - else if lang == "zh" [关闭GPIO] - else { todo }, + tr(( + en: [Disables the GPIO], + de: [Deaktiviert den GPIO], + zh: [关闭GPIO], + )), [1], [enabled], - if lang == "en" [Enables the GPIO] - else if lang == "de" [Aktiviert den GPIO] - else if lang == "zh" [使能GPIO] - else { todo }, + tr(( + en: [Enables the GPIO], + de: [Aktiviert den GPIO], + zh: [使能GPIO], + )), table.cell(rowspan: 2)[direction], table.cell(rowspan: 2)[1], [0], [input], - if lang == "en" [Sets the direction to Input] - else if lang == "de" [Legt die Richtung auf „Eingang" fest.] - else if lang == "zh" [方向设置成输入] - else { todo }, + tr(( + en: [Sets the direction to Input], + de: [Legt die Richtung auf „Eingang" fest.], + zh: [方向设置成输入], + )), [1], [output], - if lang == "en" [Sets the direction to Output] - else if lang == "de" [Legt die Richtung auf „Ausgang" fest.] - else if lang == "zh" [方向设置成输出] - else { todo }, + tr(( + en: [Sets the direction to Output], + de: [Legt die Richtung auf „Ausgang" fest.], + zh: [方向设置成输出], + )), table.cell(rowspan: 4)[input_mode], table.cell(rowspan: 4)[2..3], [00], [hi-z], - if lang == "en" [Sets the input as high resistance] - else if lang == "de" [Setzt den Eingang auf hochohmig.] - else if lang == "zh" [输入设置为高阻态] - else { todo }, + tr(( + en: [Sets the input as high resistance], + de: [Setzt den Eingang auf hochohmig.], + zh: [输入设置为高阻态], + )), [01], [pull-low], - if lang == "en" [Input pin is pulled low] - else if lang == "de" [Der Eingangspin wird auf Low-Pegel gezogen.] - else if lang == "zh" [下拉输入管脚] - else { todo }, + tr(( + en: [Input pin is pulled low], + de: [Der Eingangspin wird auf Low-Pegel gezogen.], + zh: [下拉输入管脚], + )), [10], [pull-high], - if lang == "en" [Input pin is pulled high] - else if lang == "de" [Der Eingangspin wird auf High-Pegel gezogen] - else if lang == "zh" [上拉输入管脚] - else { todo }, + tr(( + en: [Input pin is pulled high], + de: [Der Eingangspin wird auf High-Pegel gezogen], + zh: [上拉输入管脚], + )), [11], [n/a], - if lang == "en" [Invalid state. Do not set] - else if lang == "de" [Ungültiger Zustand. Nicht setzen.] - else if lang == "zh" [无效状态。不要设置] - else { todo }, + tr(( + en: [Invalid state. Do not set], + de: [Ungültiger Zustand. Nicht setzen.], + zh: [无效状态。不要设置], + )), table.cell(rowspan: 2)[output_mode], table.cell(rowspan: 2)[4], [0], [set-low], - if lang == "en" [Output pin is driven low] - else if lang == "de" [Der Ausgangspin wird auf Low-Pegel gesteuert.] - else if lang == "zh" [把管脚设置成低电平] - else { todo }, + tr(( + en: [Output pin is driven low], + de: [Der Ausgangspin wird auf Low-Pegel gesteuert.], + zh: [把管脚设置成低电平], + )), [1], [set-high], - if lang == "en" [Output pin is driven high] - else if lang == "de" [Der Ausgangspin wird auf High-Pegel gesteuert.] - else if lang == "zh" [把管脚设置成高电平] - else { todo }, + tr(( + en: [Output pin is driven high], + de: [Der Ausgangspin wird auf High-Pegel gesteuert.], + zh: [把管脚设置成高电平], + )), [input_status], [5], [x], [in-val], - if lang == "en" [0 if input is < 1.5v, 1 if input >= 1.5v] - else if lang == "de" [0, wenn der Eingang < 1,5 V ist; 1, wenn der Eingang ≥ 1,5 V ist.] - else if lang == "zh" [如果输入 < 1.5v 为0,如果输入 - >= 1.5v 为1] - else { todo }, + tr(( + en: [0 if input is < 1.5v, 1 if input >= 1.5v], + de: [0, wenn der Eingang < 1,5 V ist; 1, wenn der Eingang ≥ 1,5 V ist.], + zh: [如果输入 < 1.5v 为0,如果输入 >= 1.5v 为1], + )), ) ) \ No newline at end of file diff --git a/typ/static-guarantees/index.typ b/typ/static-guarantees/index.typ index 608133c5..1bfee13d 100644 --- a/typ/static-guarantees/index.typ +++ b/typ/static-guarantees/index.typ @@ -1,73 +1,86 @@ #import "../config.typ": * -#h1(if lang == "en" [Static Guarantees] - else if lang == "de" [Statische Garantien] - else if lang == "zh" [静态保障] - else { todo }) +#h1(tr(( + en: [Static Guarantees], + de: [Statische Garantien], + zh: [静态保障], +))) #let ln_send = link("https://doc.rust-lang.org/core/marker/trait.Send.html")[`Send`] #let ln_sync = link("https://doc.rust-lang.org/core/marker/trait.Sync.html")[`Sync`] -#if lang == "en" [ +#tr(( +en: [ Rust's type system prevents data races at compile time (see #ln_send and #ln_sync traits). The type system can also be used to check other properties at compile time; reducing the need for runtime checks in some cases. -] else if lang == "de" [ +], +de: [ Das Typsystem von Rust verhindert Data Races zur Kompilierzeit (siehe die Traits #ln_send und #ln_sync). Zudem lässt sich das Typsystem nutzen, um weitere Eigenschaften bereits zur Kompilierzeit zu überprüfen, wodurch in einigen Fällen Laufzeitprüfungen überflüssig werden. -] else if lang == "zh" [ +], +zh: [ Rust的类型系统可以在编译时防止数据竞争(看#ln_send;和#ln_sync;特性(traits))。也可以在编译时使用类型系统来完成一些检查工作;减少某些例子中对运行时检查的需要。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ When applied to embedded programs these _static checks_ can be used, for example, to enforce that configuration of I/O interfaces is done properly. For instance, one can design an API where it is only possible to initialize a serial interface by first configuring the pins that will be used by the interface. -] else if lang == "de" [ +], +de: [ Bei Embedded-Programmen lassen sich diese _statischen Prüfungen_ beispielsweise nutzen, um sicherzustellen, dass die Konfiguration von E/A-Schnittstellen korrekt erfolgt. So lässt sich etwa eine API entwerfen, bei der die Initialisierung einer seriellen Schnittstelle nur möglich ist, wenn zuvor die für diese Schnittstelle vorgesehenen Pins konfiguriert wurden. -] else if lang == "zh" [ +], +zh: [ 当应用到嵌入式程序时,这些_静态检查_能被用来,比如,强制按需配置I/O接口。例如,可以设计一个初始化串行接口的API,这个API只有在配置好接口需要的管脚后才可以被正确地使用。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ One can also statically check that operations, like setting a pin low, can only be performed on correctly configured peripherals. For example, trying to change the output state of a pin configured in floating input mode would raise a compile error. -] else if lang == "de" [ +], +de: [ Zudem lässt sich statisch überprüfen, ob Operationen -- wie etwa das Setzen eines Pins auf den Pegel „Low" -- nur an korrekt konfigurierten Peripheriekomponenten ausgeführt werden. Der Versuch, den Ausgangszustand eines Pins zu ändern, der als „Floating Input" (Eingang mit undefiniertem Pegel) konfiguriert ist, würde beispielsweise zu einem Kompilierfehler führen. -] else if lang == "zh" [ +], +zh: [ 也可以静态检查,是否是在正确配置了的外设上执行的操作,像是拉低一个管脚这种操作。比如尝试修改一个被配置成浮空输入模式的管脚的输出状态时,会触发一个编译时错误。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ And, as seen in the previous chapter, the concept of ownership can be applied to peripherals to ensure that only certain parts of a program can modify a peripheral. This _access control_ makes software easier to reason about compared to the alternative of treating peripherals as global mutable state. -] else if lang == "de" [ +], +de: [ Wie bereits im vorherigen Kapitel erläutert, lässt sich auch das Konzept des „Ownership" (Besitzverhältnis) auf Peripheriekomponenten anwenden, um sicherzustellen, dass nur bestimmte Programmteile diese modifizieren können. Diese _Zugriffskontrolle_ erleichtert das Verständnis und die Analyse der Software im Vergleich zu dem alternativen Ansatz, Peripheriekomponenten als globalen, veränderbaren Zustand zu behandeln. -] else if lang == "zh" [ +], +zh: [ 并且,像是在前面章节看到的,所有权的概念能被应用到外设上确保一个程序只有某些部分可以修改一个外设。与将这个外设当做全局可变的状态相比,_访问控制_(assess control)使得软件更容易推理。 -] else { todo } +])) diff --git a/typ/static-guarantees/state-machines.typ b/typ/static-guarantees/state-machines.typ index 008f92b0..77e96d66 100644 --- a/typ/static-guarantees/state-machines.typ +++ b/typ/static-guarantees/state-machines.typ @@ -1,13 +1,14 @@ #import "../config.typ": * -#h1(offset: whole, - if lang == "en" [Peripherals as State Machines] - else if lang == "de" [Peripheriegeräte als Zustandsautomaten] - else if lang == "zh" [作为状态机的外设] - else { todo }) +#h1(offset: whole, tr(( + en: [Peripherals as State Machines], + de: [Peripheriegeräte als Zustandsautomaten], + zh: [作为状态机的外设], +))) #let url_pin = "https://en.wikipedia.org/wiki/General-purpose_input/output" -#if lang == "en" [ +#tr(( +en: [ The peripherals of a microcontroller can be thought of as set of state machines. For example, the configuration of a simplified #link(url_pin)[GPIO pin] could be represented as the following tree of states: @@ -20,7 +21,8 @@ - Input: High Resistance - Input: Pulled Low - Input: Pulled High -] else if lang == "de" [ +], +de: [ Die Peripherie eines Mikrocontrollers lässt sich als eine Menge von Zustandsautomaten auffassen. So ließe sich beispielsweise die Konfiguration eines vereinfachten @@ -35,7 +37,8 @@ - Eingang: Hoher Widerstand - Eingang: Auf Low-Pegel gezogen - Eingang: Auf High-Pegel gezogen -] else if lang == "zh" [ +], +zh: [ 一个微控制器的外设可以被想成是一组状态机。比如,一个简化的#link(url_pin)[GPIO管脚]的配置可以被表达成下列的状态树: - 关闭 - 使能 @@ -46,16 +49,18 @@ - 输入: 高阻态 - 输入: 下拉 - 输入: 上拉 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ If the peripheral starts in the `Disabled` mode, to move to the `Input: High Resistance` mode, we must perform the following steps: + Disabled + Enabled + Configured as Input + Input: High Resistance -] else if lang == "de" [ +], +de: [ Wenn die Peripherieeinheit im Modus `Deaktiviert` startet, müssen wir die folgenden Schritte ausführen, um in den Modus `Eingang: Hoher Widerstand` zu ​​wechseln: @@ -63,30 +68,35 @@ + Aktiviert + Als Eingang konfiguriert + Eingang: Hoher Widerstand -] else if lang == "zh" [ +], +zh: [ 如果外设开始于`关闭`模式,切换到`输入: 高阻态`模式,我们必须执行下面的步骤: + 关闭 + 使能 + 配置成输入 + 输入: 高阻态 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ If we wanted to move from `Input: High Resistance` to `Input: Pulled Low`, we must perform the following steps: + Input: High Resistance + Input: Pulled Low -] else if lang == "de" [ +], +de: [ Wenn wir von „Eingang: Hoher Widerstand" zu „Eingang: Auf Low-Pegel gezogen" wechseln möchten, müssen wir die folgenden Schritte ausführen: + Eingang: Hoher Widerstand 2. Eingang: Auf Low-Pegel gezogen -] else if lang == "zh" [ +], +zh: [ 如果我们想要从`输入: 高阻态`切换到`输入: 下拉`,我们必须执行下列的步骤: + 输入: 高阻抗 + 输入: 下拉 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Similarly, if we want to move a GPIO pin from configured as `Input: Pulled Low` to `Output: High`, we must perform the following steps: @@ -94,7 +104,8 @@ + Configured as Input + Configured as Output + Output: High -] else if lang == "de" [ +], +de: [ Wenn wir einen GPIO-Pin von der Konfiguration „Eingang: Auf Low-Pegel gezogen" auf „Ausgang: High" umstellen wollen, müssen wir ebenfalls die folgenden Schritte ausführen: @@ -102,56 +113,62 @@ + Als Eingang konfiguriert + Als Ausgang konfiguriert + Ausgang: High -] else if lang == "zh" [ +], +zh: [ 同样地,如果我们想要把一个GPIO管脚从`输入: 下拉`切换到`输出: 高`,我们必须执行下列的步骤: + 输入: 下拉 + 配置成输入 + 配置成输出 + 输出: 高 -] else { todo } +])) -= #(if lang == "en" [Hardware Representation] - else if lang == "de" [Hardware-Darstellung] - else if lang == "zh" [硬件表征(Hardware Representation)] - else { todo }) += #tr(( + en: [Hardware Representation], + de: [Hardware-Darstellung], + zh: [硬件表征(Hardware Representation)], +)) -#if lang == "en" [ +#tr(( +en: [ Typically the states listed above are set by writing values to given registers mapped to a GPIO peripheral. Let's define an imaginary GPIO Configuration Register to illustrate this: -] else if lang == "de" [ +], +de: [ Typischerweise werden die oben aufgeführten Zustände eingestellt, indem Werte in bestimmte Register geschrieben werden, die einer GPIO-Peripherieeinheit zugeordnet sind. Definieren wir zur Veranschaulichung ein fiktives GPIO-Konfigurationsregister: -] else if lang == "zh" [ +], +zh: [ 通常,通过向映射到GPIO外设上的指定的寄存器中写入值可以配置上面列出的状态。让我们定义一个假想的GPIO配置寄存器来解释下它: -] else { todo } +])) #include "gpio-table.typ" -#if lang == "en" [ +#tr(( +en: [ We _could_ expose the following structure in Rust to control this GPIO: -] else if lang == "de" [ +], +de: [ Wir _könnten_ die folgende Struktur in Rust bereitstellen, um diesen GPIO zu steuern: -] else if lang == "zh" [ +], +zh: [ _可以_ 在Rust中暴露下列的结构体来控制这个GPIO: -] else { todo } +])) #raw(block: true, lang: "rust", -"/// " + if lang in ("en", "de") { - "GPIO interface" - } else if lang == "zh" { - "GPIO接口" - } else { todos } + " +"/// " + ts(( + en: "GPIO interface", + de: "GPIO interface", + zh: "GPIO接口", + )) + " struct GpioConfig { - /// " + if lang == "en" { - "GPIO Configuration structure generated by svd2rust" - } else if lang == "de" { - "Von svd2rust generierte GPIO-Konfigurationsstruktur" - } else if lang == "zh" { - "由svd2rust生成的GPIO配置结构体" - } else { todos } + " + /// " + ts(( + en: "GPIO Configuration structure generated by svd2rust", + de: "Von svd2rust generierte GPIO-Konfigurationsstruktur", + zh: "由svd2rust生成的GPIO配置结构体", + )) + " periph: GPIO_CONFIG, } @@ -186,41 +203,50 @@ impl GpioConfig { } ") -#if lang == "en" [ +#tr(( +en: [ However, this would allow us to modify certain registers that do not make sense. For example, what happens if we set the `output_mode` field when our GPIO is configured as an input? -] else if lang == "de" [ +], +de: [ Dies würde uns jedoch erlauben, bestimmte Register zu verändern, was keinen Sinn ergibt. Was passiert beispielsweise, wenn wir das Feld `output_mode` setzen, während unser GPIO als Eingang konfiguriert ist? -] else if lang == "zh" [ +], +zh: [ 然而,这会允许我们修改某些没有意义的寄存器。比如,如果当我们的GPIO被配置为输入时我们设置`output_mode`字段,将会发生什么? -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ In general, use of this structure would allow us to reach states not defined by our state machine above: e.g.~an output that is pulled low, or an input that is set high. For some hardware, this may not matter. On other hardware, it could cause unexpected or undefined behavior! -] else if lang == "de" [ +], +de: [ Im Allgemeinen würde die Verwendung dieser Struktur es uns ermöglichen, Zustände zu erreichen, die nicht durch unsere obige Zustandsmaschine definiert sind: z. B. einen Ausgang, der auf LOW gesetzt wird, oder einen Eingang, der auf HIGH gesetzt wird. Bei mancher Hardware mag dies keine Rolle spielen. Bei anderer Hardware könnte es jedoch zu unerwartetem oder undefiniertem Verhalten führen! -] else if lang == "zh" [ +], +zh: [ 通常使用这个结构体会允许我们访问到上面的状态机没有定义的状态:比如,一个被上拉的输出,或者一个被拉高的输入。对于一些硬件,这并没有关系。对另外一些硬件来说,这将会导致不可预期或者没有定义的行为! -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Although this interface is convenient to write, it doesn't enforce the design contracts set out by our hardware implementation. -] else if lang == "de" [ +], +de: [ Obwohl diese Schnittstelle bequem zu implementieren ist, erzwingt sie nicht die in unserer Hardware-Implementierung festgelegten Designvorgaben. -] else if lang == "zh" [ +], +zh: [ 虽然这个接口很方便写入,但是它没有强制我们遵守硬件实现所设的设计约定。 -] else { todo } +])) diff --git a/typ/static-guarantees/typestate-programming.typ b/typ/static-guarantees/typestate-programming.typ index 7ad09fbb..b94e94d7 100644 --- a/typ/static-guarantees/typestate-programming.typ +++ b/typ/static-guarantees/typestate-programming.typ @@ -1,21 +1,23 @@ #import "../config.typ": * -#h1(offset: whole, - if lang == "en" [Typestate Programming] - else if lang == "de" [Typgestützte Programmierung] - else if lang == "zh" [类型状态编程(Typestate Programming)] - else { todo }) +#h1(offset: whole, tr(( + en: [Typestate Programming], + de: [Typgestützte Programmierung], + zh: [类型状态编程(Typestate Programming)], +))) #let url_ts = "https://en.wikipedia.org/wiki/Typestate_analysis" #let url_builders = "https://doc.rust-lang.org/1.0.0/style/ownership/builders.html" -#if lang == "en" [ +#tr(( +en: [ The concept of #link(url_ts)[typestates] describes the encoding of information about the current state of an object into the type of that object. Although this can sound a little arcane, if you have used the #link(url_builders)[Builder Pattern] in Rust, you have already started using Typestate Programming! -] else if lang == "de" [ +], +de: [ Das Konzept der #link(url_ts)[typestates] beschreibt die Kodierung von Informationen über den aktuellen Zustand eines Objekts direkt in dessen Typ. Auch wenn dies zunächst etwas @@ -23,9 +25,10 @@ #link(url_builders)[Builder-Muster] verwendet haben, sind Sie schon mit der Typestate-Programmierung in Berührung gekommen! -] else if lang == "zh" [ +], +zh: [ #link(url_ts)[typestates]的概念是指将有关对象当前状态的信息编码进该对象的类型中。虽然这听起来有点神秘,如果你在Rust中用过#link(url_builders))[建造者模式],你就已经开始使用类型状态编程了! -] else { todo } +])) ```rust @@ -72,48 +75,57 @@ fn main() { } ``` -#if lang == "en" [ +#tr(( +en: [ In this example, there is no direct way to create a `Foo` object. We must create a `FooBuilder`, and properly initialize it before we can obtain the `Foo` object we want. -] else if lang == "de" [ +], +de: [ In diesem Beispiel gibt es keine direkte Möglichkeit, ein `Foo`-Objekt zu erstellen. Wir müssen einen `FooBuilder` erstellen und ihn korrekt initialisieren, bevor wir das gewünschte `Foo`-Objekt erhalten können. -] else if lang == "zh" [ +], +zh: [ 在这个例子里,不能直接生成一个`Foo`对象。必须先生成一个`FooBuilder`,并且恰当地初始化`FooBuilder`后,才能获取到需要的`Foo`对象。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ This minimal example encodes two states: - `FooBuilder`, which represents an "unconfigured", or "configuration in process" state - `Foo`, which represents a "configured", or "ready to use" state. -] else if lang == "de" [ +], +de: [ Dieses Minimalbeispiel kodiert zwei Zustände: - `FooBuilder`, das einen „unkonfigurierten" Zustand oder einen Zustand der „laufenden Konfiguration" repräsentiert - `Foo`, das einen „konfigurierten" oder „einsatzbereiten" Zustand repräsentiert. -] else if lang == "zh" [ +], +zh: [ 这个最小的例子编码了两个状态: - `FooBuilder`,其表示一个"没有被配置",或者"正在配置"状态 - `Foo`,其表示了一个"被配置",或者"可以使用"状态。 -] else { todo } +])) -= #(if lang == "en" [Strong Types] - else if lang == "de" [Starke Typen] - else if lang == "zh" [强类型] - else { todo }) += #tr(( + en: [Strong Types], + de: [Starke Typen], + zh: [强类型], +)) #let url_strong = "https://en.wikipedia.org/wiki/Strong_and_weak_typing" -#if lang == "en" [ +#tr(( +en: [ Because Rust has a #link(url_strong)[Strong Type System], there is no easy way to magically create an instance of `Foo`, or to turn a `FooBuilder` into a `Foo` without calling the `into_foo()` method. Additionally, calling the `into_foo()` method consumes the original `FooBuilder` structure, meaning it can not be reused without the creation of a new instance. -] else if lang == "de" [ +], +de: [ Da Rust über ein #link(url_strong)[starkes Typsystem] verfügt, gibt es keine einfache Möglichkeit, auf magische Weise eine @@ -122,24 +134,28 @@ fn main() { der Aufruf der Methode `into_foo()` die ursprüngliche `FooBuilder`-Struktur; das bedeutet, dass sie nicht ohne die Erstellung einer neuen Instanz wiederverwendet werden kann. -] else if lang == "zh" [ +], +zh: [ 因为Rust有一个#link(url_strong)[强类型系统],没有什么简单的方法可以奇迹般地生成一个`Foo`实例,也没有简单的方法可以不用调用`into_foo()`方法而把一个`FooBuilder`变成一个`Foo`。另外,调用`into_foo()`方法消费了最初的`FooBuilder`结构体,意味着不生成一个新的实例就不能被再次使用它。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ This allows us to represent the states of our system as types, and to include the necessary actions for state transitions into the methods that exchange one type for another. By creating a `FooBuilder`, and exchanging it for a `Foo` object, we have walked through the steps of a basic state machine. -] else if lang == "de" [ +], +de: [ Dies ermöglicht es uns, die Zustände unseres Systems als Typen darzustellen und die für Zustandsübergänge erforderlichen Aktionen in jene Methoden zu integrieren, die einen Typ gegen einen anderen austauschen. Indem wir einen `FooBuilder` erstellen und diesen gegen ein `Foo`-Objekt austauschen, haben wir die Schritte einer einfachen Zustandsmaschine durchlaufen. -] else if lang == "zh" [ +], +zh: [ 这允许我们可以将系统的状态表示成类型,把状态转换必须的动作包括进转换两个类型的方法中。通过生成一个 `FooBuilder`,转换成一个 `Foo` 对象,我们已经使用了一个基本的状态机。 -] else { todo } +])) diff --git a/typ/static-guarantees/zero-cost-abstractions.typ b/typ/static-guarantees/zero-cost-abstractions.typ index 1271f7ae..7e94d205 100644 --- a/typ/static-guarantees/zero-cost-abstractions.typ +++ b/typ/static-guarantees/zero-cost-abstractions.typ @@ -1,18 +1,20 @@ #import "../config.typ": * -#h1(offset: whole, - if lang == "en" [Zero Cost Abstractions] - else if lang == "de" [Abstraktionen ohne Kosten] - else if lang == "zh" [零成本抽象] - else { todo }) +#h1(offset: whole, tr(( + en: [Zero Cost Abstractions], + de: [Abstraktionen ohne Kosten], + zh: [零成本抽象], +))) -#if lang == "en" [ +#tr(( +en: [ Type states are also an excellent example of Zero Cost Abstractions - the ability to move certain behaviors to compile time execution or analysis. These type states contain no actual data, and are instead used as markers. Since they contain no data, they have no actual representation in memory at runtime: -] else if lang == "de" [ +], +de: [ Typzustände (Type States) sind zudem ein hervorragendes Beispiel für „Zero-Cost Abstractions" -- also die Möglichkeit, bestimmte Verhaltensweisen in die Kompilierzeit zu verlagern (sei es zur @@ -20,10 +22,11 @@ eigentlichen Daten, sondern dienen als Markierungen. Da sie keine Daten enthalten, besitzen sie zur Laufzeit keine tatsächliche Repräsentation im Speicher: -] else if lang == "zh" [ +], +zh: [ 类型状态是一个零成本抽象的杰出案例 - 把某些行为移到编译时执行或者分析的能力。这些类型状态不包含真实的数据,只用来作为标记。因为它们不包含数据,在运行时它们在内存中不存在实际的表示。 -] else { todo } +])) ```rust use core::mem::size_of; @@ -34,38 +37,45 @@ let _ = size_of::(); // == 0 let _ = size_of::>(); // == 0 ``` -= #(if lang == "en" [Zero Sized Types] - else if lang == "de" [Typen der Größe Null] - else if lang == "zh" [零大小的类型(Zero Sized Types)] - else { todo }) += #tr(( + en: [Zero Sized Types], + de: [Typen der Größe Null], + zh: [零大小的类型(Zero Sized Types)], +)) ```rust struct Enabled; ``` -#if lang == "en" [ +#tr(( +en: [ Structures defined like this are called Zero Sized Types, as they contain no actual data. Although these types act "real" at compile time - you can copy them, move them, take references to them, etc., however the optimizer will completely strip them away. -] else if lang == "de" [ +], +de: [ Strukturen dieser Art werden als Null-Typen bezeichnet, da sie keine tatsächlichen Daten enthalten. Obwohl sich diese Typen zur Kompilierzeit „real" verhalten -- man kann sie kopieren, verschieben, Referenzen darauf erstellen usw. --, werden sie vom Optimierer vollständig entfernt. -] else if lang == "zh" [ +], +zh: [ 像这样定义的结构体被称为零大小的类型,因为它们不包含实际数据。虽然这些类型在编译时像是"真实的"(real) - 你可以拷贝它们,移动它们,引用它们,等等,然而优化器将会完全跳过它们。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ In this snippet of code: -] else if lang == "de" [ +], +de: [ In diesem Code-Ausschnitt: -] else if lang == "zh" [ +], +zh: [ 在这个代码片段里: -] else { todo } +])) ```rust pub fn into_input_high_z(self) -> GpioConfig { @@ -79,14 +89,16 @@ pub fn into_input_high_z(self) -> GpioConfig { } ``` -#if lang == "en" [ +#tr(( +en: [ The GpioConfig we return never exists at runtime. Calling this function will generally boil down to a single assembly instruction - storing a constant register value to a register location. This means that the type state interface we've developed is a zero cost abstraction - it uses no more CPU, RAM, or code space tracking the state of `GpioConfig`, and renders to the same machine code as a direct register access. -] else if lang == "de" [ +], +de: [ Das von uns zurückgegebene `GpioConfig`-Objekt existiert zur Laufzeit nicht. Der Aufruf dieser Funktion läuft im Wesentlichen auf einen einzigen Assembler-Befehl hinaus: das Speichern eines konstanten @@ -96,39 +108,47 @@ pub fn into_input_high_z(self) -> GpioConfig { zusätzliche CPU- oder RAM-Ressourcen noch zusätzlichen Programmspeicher für die Zustandsverwaltung von `GpioConfig` und führt zu demselben Maschinencode wie ein direkter Registerzugriff. -] else if lang == "zh" [ +], +zh: [ 我们返回的GpioConfig在运行时并不存在。对这个函数的调用通常会被归纳为一条汇编指令 - 把一个常量寄存器值存进一个寄存器里。这意味着我们开发的类型状态接口是一个零成本抽象 - 它不会用更多的CPU,RAM,或者代码空间去跟踪`GpioConfig`的状态,会被渲染成和直接访问寄存器一样的机器码。 -] else { todo } +])) -= #(if lang == "en" [Nesting] - else if lang == "de" [Verschachtelung] - else if lang == "zh" [嵌套] - else { todo }) += #tr(( + en: [Nesting], + de: [Verschachtelung], + zh: [嵌套], +)) -#if lang == "en" [ +#tr(( +en: [ In general, these abstractions may be nested as deeply as you would like. As long as all components used are zero sized types, the whole structure will not exist at runtime. -] else if lang == "de" [ +], +de: [ Im Allgemeinen lassen sich diese Abstraktionen beliebig tief verschachteln. Solange es sich bei allen verwendeten Komponenten um Typen ohne Größe (Zero-Sized Types) handelt, existiert die gesamte Struktur zur Laufzeit nicht. -] else if lang == "zh" [ +], +zh: [ 通常,这些抽象可能会被深深地嵌套起来。一旦结构体使用的所有的组件是零大小类型的,整个结构体将不会在运行时存在。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ For complex or deeply nested structures, it may be tedious to define all possible combinations of state. In these cases, macros may be used to generate all implementations. -] else if lang == "de" [ +], +de: [ Bei komplexen oder tief verschachtelten Strukturen kann es mühsam sein, alle möglichen Zustandskombinationen zu definieren. In solchen Fällen lassen sich Makros verwenden, um sämtliche Implementierungen zu generieren. -] else if lang == "zh" [ +], +zh: [ 对于复杂或者深度嵌套的结构体,定义所有可能的状态组合可能很乏味。在这些例子中,宏可能可以被用来生成所有的实现。 -] else { todo } +])) diff --git a/typ/unsorted/index.typ b/typ/unsorted/index.typ index 95ae4bbe..c723fb68 100644 --- a/typ/unsorted/index.typ +++ b/typ/unsorted/index.typ @@ -1,7 +1,8 @@ #import "../config.typ": * -#h1(if lang == "en" [Unsorted topics] - else if lang == "de" [Unsortierte Themen] - else if lang == "zh" [没有排序的主题] - else { todo }) +#h1(tr(( + en: [Unsorted topics], + de: [Unsortierte Themen], + zh: [没有排序的主题], +))) diff --git a/typ/unsorted/math.typ b/typ/unsorted/math.typ index 97ef118e..7d1f76a8 100644 --- a/typ/unsorted/math.typ +++ b/typ/unsorted/math.typ @@ -1,32 +1,33 @@ #import "../config.typ": * -#h1(offset: whole, - if lang == "en" [Performing math functionality with `#[no_std]`] - else if lang == "de" [Mathematische Funktionen mit `#[no_std]` nutzen] - else if lang == "zh" [在`#[no_std]`下执行数学运算] - else { todo }) - -#if lang == "en" [ +#h1(offset: whole, tr(( + en: [Performing math functionality with `#[no_std]`], + de: [Mathematische Funktionen mit `#[no_std]` nutzen], + zh: [在`#[no_std]`下执行数学运算], +))) + +#tr(( +en: [ If you want to perform math related functionality like calculating the squareroot or the exponential of a number and you have the full standard library available, your code might look like this: -] else if lang == "de" [ +], +de: [ Wenn Sie mathematische Operationen durchführen möchten -- wie etwa die Berechnung der Quadratwurzel aus der Exponentialfunktion einer Zahl -- und Ihnen die vollständige Standardbibliothek zur Verfügung steht, könnte Ihr Code folgendermaßen aussehen: -] else if lang == "zh" [ +], +zh: [ 如果你想要执行数学相关的函数,像是计算平方根或者一个数的指数并有完整的标准库支持,代码可能看起来像这样: -] else { todo } +])) #raw(block: true, lang: "rust", -"//! " + if lang == "en" { - "Some mathematical functions with standard support available" - } else if lang == "de" { - "Einige mathematische Funktionen mit Standardunterstützung verfügbar" - } else if lang == "zh" { - "可用一些标准支持的数学函数" - } else { todos } + " +"//! " + ts(( + en: "Some mathematical functions with standard support available", + de: "Einige mathematische Funktionen mit Standardunterstützung verfügbar", + zh: "可用一些标准支持的数学函数", + )) + " fn main() { let float: f32 = 4.82832; @@ -48,18 +49,21 @@ fn main() { ") #let ln_libm = link("https://crates.io/crates/libm")[`libm`] -#if lang == "en" [ +#tr(( +en: [ Without standard library support, these functions are not available. An external crate like #ln_libm can be used instead. The example code would then look like this: -] else if lang == "de" [ +], +de: [ Ohne Unterstützung durch die Standardbibliothek stehen diese Funktionen nicht zur Verfügung. Stattdessen kann ein externes Crate wie #ln_libm verwendet werden. Der Beispielcode sähe dann folgendermaßen aus: -] else if lang == "zh" [ +], +zh: [ 没有标准库支持的时候,这些函数不可用。反而可以使用像是#ln_libm;这样一个外部库。示例的代码将会看起来像这样: -] else { todo } +])) #raw(block: true, lang: "rust", "#![no_main] @@ -89,34 +93,35 @@ fn main() -> ! { exponential_of_four ) .unwrap(); - // " + if lang == "en" { - "exit QEMU - // NOTE do not run this on hardware; it can corrupt OpenOCD state" - } else if lang == "de" { - "exit QEMU + // " + ts(( + en: "exit QEMU + // NOTE do not run this on hardware; it can corrupt OpenOCD state", + de: "exit QEMU // HINWEIS: Fuehren Sie dies nicht auf der Hardware aus; es kann den - // OpenOCD-Zustand beschaedigen." - } else if lang == "zh" { - "退出QEMU + // OpenOCD-Zustand beschaedigen.", + zh: "退出QEMU // 注意不要在硬件上使用这个; 它能破坏OpenOCD的状态" - } else { todos } + " + )) + " // debug::exit(debug::EXIT_SUCCESS); loop {} } ") -#if lang == "en" [ +#tr(( +en: [ If you need to perform more complex operations like DSP signal processing or advanced linear algebra on your MCU, the following crates might help you -] else if lang == "de" [ +], +de: [ Wenn Sie komplexere Operationen wie DSP-Signalverarbeitung oder fortgeschrittene lineare Algebra auf Ihrem Mikrocontroller durchführen müssen, könnten Ihnen die folgenden Crates weiterhelfen. -] else if lang == "zh" [ +], +zh: [ 如果需要在MCU上执行更复杂的操作,像是DSP信号处理或者更高级的线性代数,下列的crates可能可以帮到你 -] else { todo } +])) - #link("https://github.com/jacobrosenthal/cmsis-dsp-sys")[CMSIS DSP library binding] - #link("https://crates.io/crates/constgebra")[`constgebra`] - #link("https://github.com/tarcieri/micromath")[`micromath`] diff --git a/typ/unsorted/speed-vs-size.typ b/typ/unsorted/speed-vs-size.typ index b16e37ff..31830c82 100644 --- a/typ/unsorted/speed-vs-size.typ +++ b/typ/unsorted/speed-vs-size.typ @@ -1,167 +1,194 @@ #import "../config.typ": * -#h1(offset: whole, - if lang == "en" [Optimizations: the speed size tradeoff] - else if lang == "de" [Optimierungen: Der Kompromiss zwischen Geschwindigkeit und Größe] - else if lang == "zh" [优化: 速度与大小之间的博弈] - else { todo }) - -#if lang == "en" [ +#h1(offset: whole, tr(( + en: [Optimizations: the speed size tradeoff], + de: [Optimierungen: Der Kompromiss zwischen Geschwindigkeit und Größe], + zh: [优化: 速度与大小之间的博弈], +))) + +#tr(( +en: [ Everyone wants their program to be super fast and super small but it's usually not possible to have both characteristics. This section discusses the different optimization levels that `rustc` provides and how they affect the execution time and binary size of a program. -] else if lang == "de" [ +], +de: [ Jeder möchte, dass sein Programm extrem schnell und extrem klein ist, doch meist lassen sich nicht beide Eigenschaften zugleich verwirklichen. Dieser Abschnitt behandelt die verschiedenen Optimierungsstufen von `rustc` und deren Auswirkungen auf die Ausführungszeit sowie die Größe der Binärdatei eines Programms. -] else if lang == "zh" [ +], +zh: [ 每个人都想要程序变得即快又小,但是同时满足这两个条件是不可能的。这部分讨论`rustc`提供的不同的优化等级,和它们是如何影响执行时间和一个程序的二进制项的大小。 -] else { todo } +])) -= #(if lang == "en" [No optimizations] - else if lang == "de" [Keine Optimierungen] - else if lang == "zh" [无优化] - else { todo }) += #tr(( + en: [No optimizations], + de: [Keine Optimierungen], + zh: [无优化], +)) -#if lang == "en" [ +#tr(( +en: [ This is the default. When you call `cargo build` you use the development (AKA `dev`) profile. This profile is optimized for debugging so it enables debug information and does _not_ enable any optimizations, i.e.~it uses `-C opt-level = 0`. -] else if lang == "de" [ +], +de: [ Dies ist die Standardeinstellung. Wenn Sie `cargo build` aufrufen, wird das Entwicklungs-Profil (auch bekannt als `dev`-Profil) verwendet. Da dieses Profil für das Debugging optimiert ist, werden Debug-Informationen aktiviert, jedoch _keinerlei_ Optimierungen vorgenommen; es wird also `-C opt-level=0` verwendet. -] else if lang == "zh" [ +], +zh: [ 这是默认的。当你调用`cargo build`时,你使用的是development(又叫`dev`)配置。这个配置优化的目的是为了调试,因此它使能了调试信息且_关闭_了所有优化,i.e.~它使用 `-C opt-level = 0` 。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ At least for bare metal development, debuginfo is zero cost in the sense that it won't occupy space in Flash / ROM so we actually recommend that you enable debuginfo in the release profile -- it is disabled by default. That will let you use breakpoints when debugging release builds. -] else if lang == "de" [ +], +de: [ Zumindest bei der Bare-Metal-Entwicklung verursachen Debug-Informationen keine Kosten im Sinne von Speicherplatzbedarf im Flash- oder ROM-Speicher. Daher empfehlen wir, Debug-Informationen auch im Release-Profil zu aktivieren -- standardmäßig sind sie dort deaktiviert. Dies ermöglicht Ihnen die Verwendung von Breakpoints beim Debuggen von Release-Builds. -] else if lang == "zh" [ +], +zh: [ 至少对于裸机开发来说,调试信息不会占用Flash/ROM中的空间,意味着在这种情况下,调试信息是零开销的,因此实际上我们推荐你在release配置中使能调试信息 -- 默认它被关闭了。那会让你调试release版本的固件时可以使用断点。 -] else { todo } +])) #raw(block: true, lang: "toml", "[profile.release] -# " + if lang == "en" { - "symbols are nice and they don't increase the size on Flash" - } else if lang == "de" { - "Symbole sind praktisch und vergroessern die Dateigroesse im Flash nicht" - } else if lang == "zh" { - "调试符号很好且它们不会增加Flash上的大小" - } else { todos } + " +# " + ts(( + en: "symbols are nice and they don't increase the size on Flash", + de: "Symbole sind praktisch und vergroessern die Dateigroesse im Flash nicht", + zh: "调试符号很好且它们不会增加Flash上的大小", + )) + " debug = true ") -#if lang == "en" [ +#tr(( +en: [ No optimizations is great for debugging because stepping through the code feels like you are executing the program statement by statement, plus you can `print` stack variables and function arguments in GDB. When the code is optimized, trying to print variables results in `$0 = ` being printed. -] else if lang == "de" [ +], +de: [ Der Verzicht auf Optimierungen ist ideal für das Debugging, da sich das schrittweise Durchlaufen des Codes anfühlt, als würde man das Programm Anweisung für Anweisung ausführen; zudem lassen sich Stack-Variablen und Funktionsargumente in GDB per `print` ausgeben. Bei optimiertem Code führt der Versuch, Variablen auszugeben, hingegen zur Meldung `$0 = `. -] else if lang == "zh" [ +], +zh: [ 无优化对于调试来说是最好的选择,因为单步调试代码感觉像是你正在逐条语句地执行程序,且你能在GDB中`print`栈变量和函数参数。当代码被优化了,尝试打印变量会导致`$0 = `被打印出来。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ The biggest downside of the `dev` profile is that the resulting binary will be huge and slow. The size is usually more of a problem because unoptimized binaries can occupy dozens of KiB of Flash, which your target device may not have -- the result: your unoptimized binary doesn't fit in your device! -] else if lang == "de" [ +], +de: [ Der größte Nachteil des `dev`-Profils besteht darin, dass die resultierende Binärdatei sehr groß und langsam ist. Meist wiegt vor allem die Größe schwer, da nicht optimierte Binärdateien Dutzende KiB an Flash-Speicher belegen können -- Speicherplatz, über den das Zielgerät womöglich gar nicht verfügt. Die Folge: Die nicht optimierte Binärdatei passt nicht auf das Gerät! -] else if lang == "zh" [ +], +zh: [ `dev`配置最大的缺点就是最终的二进制项将会变得巨大且缓慢。大小通常是一个更大的问题,因为未优化的二进制项会占据大量KiB的Flash,你的目标设备可能没这么多Flash -- 结果: 你未优化的二进制项无法烧录进你的设备中! -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Can we have smaller, debugger friendly binaries? Yes, there's a trick. -] else if lang == "de" [ +], +de: [ Gibt es eine Möglichkeit, kleinere und dennoch für das Debugging geeignete Binärdateien zu erhalten? Ja, es gibt einen Trick. -] else if lang == "zh" [ +], +zh: [ 我们可以有更小的,调试友好的二进制项吗?是的,这里有一个技巧。 -] else { todo } +])) -== #(if lang == "en" [Optimizing dependencies] - else if lang == "de" [Optimierung der Abhängigkeiten] - else if lang == "zh" [优化依赖] - else { todo }) +== #tr(( + en: [Optimizing dependencies], + de: [Optimierung der Abhängigkeiten], + zh: [优化依赖], +)) #let url_overrides = "https://doc.rust-lang.org/cargo/reference/profiles.html#overrides" -#if lang == "en" [ +#tr(( +en: [ There's a Cargo feature named #link(url_overrides)[`profile-overrides`] that lets you override the optimization level of dependencies. You can use that feature to optimize all dependencies for size while keeping the top crate unoptimized and debugger friendly. -] else if lang == "de" [ +], +de: [ Cargo bietet mit #link(url_overrides)[`Profil-Überschreibung`] eine Funktion, mit der Sie den Optimierungsgrad von Abhängigkeiten überschreiben können. So lassen sich alle Abhängigkeiten hinsichtlich ihrer Größe optimieren, während die oberste Crate unoptimiert und debuggfreundlich bleibt. -] else if lang == "zh" [ +], +zh: [ 这里有个名为#link(url_overrides)[`profile-overrides`]的Cargo feature,其可以让你覆盖依赖项的优化等级。你能使用这个feature去优化所有依赖的大小,而保持顶层的crate没有被优化以致调试起来友好。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Beware that generic code can sometimes be optimized alongside the crate where it is instantiated, rather than the crate where it is defined. If you create an instance of a generic struct in your application and find that it pulls in code with a large footprint, it may be that increasing the optimisation level of the relevant dependencies has no effect. -] else if lang == "de" [ +], +de: [ Beachten Sie, dass generischer Code manchmal zusammen mit der Crate optimiert werden kann, in der er instanziiert wird, anstatt mit der Crate, in der er definiert ist. Wenn Sie in Ihrer Anwendung eine Instanz einer generischen Struktur erstellen und feststellen, dass diese Code mit großem Speicherbedarf einbindet, kann es sein, dass eine Erhöhung des Optimierungsgrads der relevanten Abhängigkeiten keine Wirkung zeigt. -] else if lang == "zh" [ +], +zh: [ 需要知道,泛型代码有时是在它被实例化的库中被优化的,而不是它被定义的地方.如果你在你的应用中生成了一个泛型结构体的实例, 并且发现它让代码体积变得更大,那可能是因为相关的依赖的优化等级的增加没有造成影响. -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Here's an example: -] else if lang == "de" [ +], +de: [ Hier ist ein Beispiel: -] else if lang == "zh" [ +], +zh: [ 这是一个示例: -] else { todo } +])) ```toml # Cargo.toml @@ -173,13 +200,16 @@ name = "app" opt-level = "z" # + ``` -#if lang == "en" [ +#tr(( +en: [ Without the override: -] else if lang == "de" [ +], +de: [ Ohne die Überschreibung: -] else if lang == "zh" [ +], +zh: [ 没有覆盖: -] else { todo } +])) ```text $ cargo size --bin app -- -A @@ -192,13 +222,16 @@ section size addr .bss 4 0x20000000 ``` -#if lang == "en" [ +#tr(( +en: [ With the override: -] else if lang == "de" [ +], +de: [ Mit der Überschreibung -] else if lang == "zh" [ +], +zh: [ 有覆盖: -] else { todo } +])) ```text $ cargo size --bin app -- -A @@ -211,7 +244,8 @@ section size addr .bss 4 0x20000000 ``` -#if lang == "en" [ +#tr(( +en: [ That's a 6 KiB reduction in Flash usage without any loss in the debuggability of the top crate. If you step into a dependency then you'll start seeing those `` messages again but @@ -219,7 +253,8 @@ section size addr dependencies. And if you _do_ need to debug a dependency then you can use the `profile-overrides` feature to exclude a particular dependency from being optimized. See example below: -] else if lang == "de" [ +], +de: [ Das bedeutet eine Verringerung des Flash-Speicherverbrauchs um 6 KiB, ohne die Debug-Fähigkeit des Haupt-Crates zu beeinträchtigen. Wenn man in eine Abhängigkeit hineinspringt, erscheinen zwar wieder die Meldungen @@ -228,68 +263,73 @@ section size addr sein, eine Abhängigkeit zu debuggen, lässt sich die Funktion `profile-overrides` nutzen, um diese spezifische Abhängigkeit von der Optimierung auszunehmen. Siehe dazu das folgende Beispiel: -] else if lang == "zh" [ +], +zh: [ 在Flash的使用上减少了6KiB,而不会损害顶层crate的可调试性。如果你步进一个依赖项,然后你将开始再次看到那些``信息,但是通常的情况下你只想调试顶层的crate而不是依赖项。如果你 _需要_ 调试一个依赖项,那么你可以使用`profile-overrides` feature去防止一个特定的依赖项被优化。看下面的例子: -] else { todo } +])) #raw(block: true, lang: "toml", "# .. -# " + if lang == "en" { - "don't optimize the `cortex-m-rt` crate" - } else if lang == "de" { - "Optimiere das `cortex-m-rt`-Crate nicht" - } else if lang == "zh" { - "不要优化`cortex-m-rt` crate" - } else { todos } + " +# " + ts(( + en: "don't optimize the `cortex-m-rt` crate", + de: "Optimiere das `cortex-m-rt`-Crate nicht", + zh: "不要优化`cortex-m-rt` crate", + )) + " [profile.dev.package.cortex-m-rt] # + opt-level = 0 # + -# " + if lang == "en" { - "but do optimize all the other dependencies" - } else if lang == "de" { - "optimieren Sie jedoch alle anderen Abhaengigkeiten" - } else if lang == "zh" { - "但是优化所有其它依赖项" - } else { todos } + " +# " + ts(( + en: "but do optimize all the other dependencies", + de: "optimieren Sie jedoch alle anderen Abhaengigkeiten", + zh: "但是优化所有其它依赖项", + )) + " [profile.dev.package.\"*\"] -codegen-units = 1 # " + if lang in ("en", "de", "zh") { - "better optimizations" - } else { todos } + " +codegen-units = 1 # " + ts(( + en: "better optimizations", + )) + " opt-level = \"z\" ") -#if lang == "en" [ +#tr(( +en: [ Now the top crate and `cortex-m-rt` are debugger friendly! -] else if lang == "de" [ +], +de: [ Jetzt sind das Top-Level-Crate und `cortex-m-rt` debuggerfreundlich! -] else if lang == "zh" [ +], +zh: [ 现在顶层的crate和`cortex-m-rt`对调试器很友好! -] else { todo } +])) -= #(if lang == "en" [Optimize for speed] - else if lang == "de" [Auf Geschwindigkeit optimieren] - else if lang == "zh" [优化速度] - else { todo }) += #tr(( + en: [Optimize for speed], + de: [Auf Geschwindigkeit optimieren], + zh: [优化速度], +)) -#if lang == "en" [ +#tr(( +en: [ As of 2018-09-18 `rustc` supports three "optimize for speed" levels: `opt-level = 1`, `2` and `3`. When you run `cargo build --release` you are using the release profile which defaults to `opt-level = 3`. -] else if lang == "de" [ +], +de: [ Seit dem 18.09.2018 unterstützt `rustc` drei Stufen der Geschwindigkeitsoptimierung: `opt-level = 1`, `2` und `3`. Beim Ausführen von `cargo build --release` wird das Release-Profil verwendet, das standardmäßig auf `opt-level = 3` eingestellt ist. -] else if lang == "zh" [ +], +zh: [ 自2018-09-18开始 `rustc` 支持三个 "优化速度" 的等级: `opt-level = 1`, `2` 和 `3` 。当你运行 `cargo build --release` 时,你正在使用的是release配置,其默认是 `opt-level = 3` 。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Both `opt-level = 2` and `3` optimize for speed at the expense of binary size, but level `3` does more vectorization and inlining than level `2`. In particular, you'll see that at `opt-level` equal to or greater than @@ -297,7 +337,8 @@ opt-level = \"z\" terms of Flash / ROM (e.g.~from 26 bytes to 194 for a zero this array loop) but can also halve the execution time given the right conditions (e.g.~number of iterations is big enough). -] else if lang == "de" [ +], +de: [ Sowohl `opt-level = 2` als auch `3` optimieren auf Geschwindigkeit zulasten der Binärgröße; allerdings führt Stufe `3` mehr Vektorisierung und Inlining durch als Stufe `2`. Insbesondere ist zu beobachten, dass @@ -307,68 +348,81 @@ opt-level = \"z\" Bytes für eine Schleife zum Nullsetzen eines Arrays), kann aber unter geeigneten Bedingungen (etwa bei einer ausreichend hohen Anzahl von Iterationen) die Ausführungszeit halbieren. -] else if lang == "zh" [ +], +zh: [ `opt-level = 2` 和 `3` 都以二进制项大小为代价优化速度,但是等级`3`比等级`2`做了更多的向量化和内联。特别是,你将看到在`opt-level`等于或者大于`2`时LLVM将展开循环。循环展开在 Flash / ROM 方面的成本相当高(e.g.~from 26 bytes to 194 for a zero this array loop)但是如果条件合适(迭代次数足够大),也可以将执行时间减半。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ Currently there's no way to disable loop unrolling in `opt-level = 2` and `3` so if you can't afford its cost you should optimize your program for size. -] else if lang == "de" [ +], +de: [ Derzeit gibt es keine Möglichkeit, das Entrollen von Schleifen bei `opt-level = 2` und `3` zu deaktivieren; wenn Sie sich diesen Speicheraufwand also nicht leisten können, sollten Sie Ihr Programm stattdessen auf eine geringe Größe hin optimieren. -] else if lang == "zh" [ +], +zh: [ 现在还没有办法在`opt-level = 2`和`3`的情况下关闭循环展开,因此如果你不能接受它的开销,你应该选择优化你的程序的大小。 -] else { todo } +])) -= #(if lang == "en" [Optimize for size] - else if lang == "de" [Nach Größe optimieren] - else if lang == "zh" [优化大小] - else { todo }) += #tr(( + en: [Optimize for size], + de: [Nach Größe optimieren], + zh: [优化大小], +)) -#if lang == "en" [ +#tr(( +en: [ As of 2018-09-18 `rustc` supports two "optimize for size" levels: `opt-level = "s"` and `"z"`. These names were inherited from clang / LLVM and are not too descriptive but `"z"` is meant to give the idea that it produces smaller binaries than `"s"`. -] else if lang == "de" [ +], +de: [ Seit dem 18.09.2018 unterstützt `rustc` zwei Stufen zur Größenoptimierung: `opt-level = "s"` und `"z"`. Diese Bezeichnungen wurden von Clang/LLVM übernommen und sind nicht besonders aussagekräftig; allerdings soll das `"z"` signalisieren, dass damit kleinere Binärdateien erzeugt werden als mit `"s"`. -] else if lang == "zh" [ +], +zh: [ 自2018-09-18开始`rustc`支持两个"优化大小"的等级: `opt-level = "s"` 和 `"z"` 。这些名字传承自 clang / LLVM 且不具有描述性,但是`"z"`意味着它产生的二进制文件比`"s"`更小。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ If you want your release binaries to be optimized for size then change the `profile.release.opt-level` setting in `Cargo.toml` as shown below. -] else if lang == "de" [ +], +de: [ Wenn Ihre Release-Binärdateien hinsichtlich der Größe optimiert werden sollen, ändern Sie die Einstellung `profile.release.opt-level` in der Datei `Cargo.toml` wie unten dargestellt. -] else if lang == "zh" [ +], +zh: [ 如果你想要发布一个优化了大小的二进制项,那么改变下面展示的`Cargo.toml`中的`profile.release.opt-level`配置。 -] else { todo } +])) #raw(block: true, lang: "toml", "[profile.release] -# " + if lang in ("en", "zh") { "or \"z\"" } - else if lang == "de" { "oder \"z\"" } - else { todos } + " +# " + ts(( + en: "or \"z\"", + de: "oder \"z\"", + )) + " opt-level = \"s\" ") -#if lang == "en" [ +#tr(( +en: [ These two optimization levels greatly reduce LLVM's inline threshold, a metric used to decide whether to inline a function or not. One of Rust principles are zero cost abstractions; these abstractions tend to use a @@ -376,7 +430,8 @@ opt-level = \"s\" that borrow an inner value like `deref`, `as_ref`) so a low inline threshold can make LLVM miss optimization opportunities (e.g.~eliminate dead branches, inline calls to closures). -] else if lang == "de" [ +], +de: [ Diese beiden Optimierungsstufen senken den Inline-Schwellenwert von LLVM erheblich -- einen Kennwert, der darüber entscheidet, ob eine Funktion „geinlined" (direkt an der Aufrufstelle eingebettet) wird oder nicht. @@ -387,36 +442,38 @@ opt-level = \"s\" Ein niedriger Inline-Schwellenwert kann daher dazu führen, dass LLVM Optimierungsmöglichkeiten verpasst (etwa das Entfernen von nicht erreichbaren Programmzweigen oder das Inlinen von Closure-Aufrufen). -] else if lang == "zh" [ +], +zh: [ 这两个优化等级极大地减小了LLVM的内联阈值,一个用来决定是否内联或者不内联一个函数的度量。Rust其中一个概念是零成本抽象;这些抽象趋向于去使用许多新类型和小函数去保持不变量(e.g.~像是`deref`,`as_ref`这样借用内部值的函数)因此一个低内联阈值会使LLVM失去优化的机会(e.g.~去掉死分支(dead branches),内联对闭包的调用)。 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ When optimizing for size you may want to try increasing the inline threshold to see if that has any effect on the binary size. The recommended way to change the inline threshold is to append the `-C inline-threshold` flag to the other rustflags in `.cargo/config.toml`. -] else if lang == "de" [ +], +de: [ Bei der Optimierung auf eine geringe Binärgröße kann es sinnvoll sein, den Inline-Schwellenwert zu erhöhen und zu prüfen, ob sich dies auf die Größe der Binärdatei auswirkt. Die empfohlene Methode zur Änderung dieses Schwellenwerts besteht darin, das Flag `-C inline-threshold` zu den übrigen `rustflags` in der Datei `.cargo/config.toml` hinzuzufügen. -] else if lang == "zh" [ +], +zh: [ 当优化大小时,你可能想要尝试增加内联阈值去观察是否会对你的二进制项的大小有影响。推荐的改变内联阈值的方法是在`.cargo/config.toml`中往其它rustflags后插入`-C inline-threshold` -] else { todo } +])) #raw(block: true, lang: "toml", "# .cargo/config.toml -# " + if lang == "en" { - "this assumes that you are using the cortex-m-quickstart template" - } else if lang == "de" { - "Dies setzt voraus, dass Sie die cortex-m-quickstart-Vorlage verwenden" - } else if lang == "zh" { - "这里假设你正在使用cortex-m-quickstart模板" - } else { todos } + " +# " + ts(( + en: "this assumes that you are using the cortex-m-quickstart template", + de: "Dies setzt voraus, dass Sie die cortex-m-quickstart-Vorlage verwenden", + zh: "这里假设你正在使用cortex-m-quickstart模板", + )) + " [target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))'] rustflags = [ # .. @@ -425,33 +482,39 @@ rustflags = [ ") #let url_opt_lvls = "https://github.com/rust-lang/rust/blob/1.29.0/src/librustc_codegen_llvm/back/write.rs#L2105-L2122" -#if lang == "en" [ +#tr(( +en: [ What value to use? #link(url_opt_lvls)[As of 1.29.0 these are the inline thresholds that the different optimization levels use]: - `opt-level = 3` uses 275 - `opt-level = 2` uses 225 - `opt-level = "s"` uses 75 - `opt-level = "z"` uses 25 -] else if lang == "de" [ +], +de: [ Welchen Wert verwenden? #link(url_opt_lvls)[Ab Version 1.29.0 gelten für die verschiedenen Optimierungsstufen folgende Inline-Schwellenwerte]: - `opt-level = 3` verwendet 275 - `opt-level = 2` verwendet 225 - `opt-level = "s"` verwendet 75 - `opt-level = "z"` verwendet 25 -] else if lang == "zh" [ +], +zh: [ 用什么值?#link(url_opt_lvls)[从1.29.0开始,这些是不同优化级别使用的内联阈值]: - `opt-level = 3` 使用 275 - `opt-level = 2` 使用 225 - `opt-level = "s"` 使用 75 - `opt-level = "z"` 使用 25 -] else { todo } +])) -#if lang == "en" [ +#tr(( +en: [ You should try `225` and `275` when optimizing for size. -] else if lang == "de" [ +], +de: [ Du solltest `225` und `275` ausprobieren, wenn du auf die Größe optimierst. -] else if lang == "zh" [ +], +zh: [ 当优化大小时,你应该尝试`225`和`275` 。 -] else { todo } +])) From 6d3c343ee0e52aa203edf215bf45556a45434d92 Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Mon, 10 Aug 2026 18:19:40 +0300 Subject: [PATCH 09/11] finish Japanese --- .github/workflows/ci-typ.yml | 4 + typ/appendix/glossary.typ | 5 +- typ/c-tips/index.typ | 260 +++++++++++++- typ/collections/index.typ | 153 +++++++- typ/concurrency/index.typ | 336 +++++++++++++++++- typ/config.typ | 17 +- typ/design-patterns/hal/checklist.typ | 5 +- typ/design-patterns/hal/gpio.typ | 5 +- typ/design-patterns/hal/index.typ | 5 +- typ/design-patterns/hal/interoperability.typ | 5 +- typ/design-patterns/hal/naming.typ | 5 +- typ/design-patterns/hal/predictability.typ | 5 +- typ/design-patterns/index.typ | 5 +- typ/interoperability/c-with-rust.typ | 126 ++++++- typ/interoperability/index.typ | 34 +- typ/interoperability/rust-with-c.typ | 79 +++- typ/intro/hardware.typ | 5 +- typ/intro/index.typ | 29 +- typ/intro/install.typ | 5 +- typ/intro/install/linux.typ | 4 +- typ/intro/install/macos.typ | 4 +- typ/intro/install/verify.typ | 5 +- typ/intro/install/windows.typ | 4 +- typ/intro/no-std.typ | 5 +- typ/intro/tooling.typ | 10 +- typ/peripherals/a-first-attempt.typ | 85 ++++- typ/peripherals/borrowck.typ | 42 ++- typ/peripherals/index.typ | 46 ++- typ/peripherals/singletons.typ | 75 +++- typ/portability/index.typ | 88 ++++- typ/start/exceptions.typ | 5 +- typ/start/hardware.typ | 9 +- typ/start/index.typ | 5 +- typ/start/interrupts.typ | 5 +- typ/start/panicking.typ | 17 +- typ/start/qemu.typ | 67 +++- typ/start/registers.typ | 140 +++++++- typ/start/semihosting.typ | 42 ++- typ/static-guarantees/design-contracts.typ | 60 +++- typ/static-guarantees/gpio-table.typ | 16 + typ/static-guarantees/index.typ | 25 +- typ/static-guarantees/state-machines.typ | 59 ++- .../typestate-programming.typ | 30 +- .../zero-cost-abstractions.typ | 34 +- typ/unsorted/index.typ | 6 +- typ/unsorted/math.typ | 5 +- typ/unsorted/speed-vs-size.typ | 111 +++++- 47 files changed, 1896 insertions(+), 196 deletions(-) diff --git a/.github/workflows/ci-typ.yml b/.github/workflows/ci-typ.yml index 57142eb5..341495ce 100644 --- a/.github/workflows/ci-typ.yml +++ b/.github/workflows/ci-typ.yml @@ -52,12 +52,16 @@ jobs: run: | typst compile typ/book.typ --input lang=de --input target=html book --features bundle,html -f bundle typst compile typ/book.typ --input lang=de --input target=pdf book/book_de.pdf + typst compile typ/book.typ --input lang=de --input target=pdf book/book_de_tr.pdf --input goal=translation typst compile typ/book.typ --input lang=ja --input target=html book --features bundle,html -f bundle typst compile typ/book.typ --input lang=ja --input target=pdf book/book_ja.pdf + typst compile typ/book.typ --input lang=ja --input target=pdf book/book_ja_tr.pdf --input goal=translation typst compile typ/book.typ --input lang=uk --input target=html book --features bundle,html -f bundle typst compile typ/book.typ --input lang=uk --input target=pdf book/book_uk.pdf + typst compile typ/book.typ --input lang=uk --input target=pdf book/book_uk_tr.pdf --input goal=translation typst compile typ/book.typ --input lang=zh --input target=html book --features bundle,html -f bundle typst compile typ/book.typ --input lang=zh --input target=pdf book/book_zh.pdf + typst compile typ/book.typ --input lang=zh --input target=pdf book/book_zh_tr.pdf --input goal=translation - name: Check links run: linkchecker book diff --git a/typ/appendix/glossary.typ b/typ/appendix/glossary.typ index 20ba00b1..dea06b1e 100644 --- a/typ/appendix/glossary.typ +++ b/typ/appendix/glossary.typ @@ -1,10 +1,9 @@ #import "../config.typ": * -#h1(tr(( - en: [Appendix A: Glossary], +#h1((en: [Appendix A: Glossary], de: [Anhang A: Glossar], zh: [附录A: 词汇表], -))) +)) #tr(( en: [ diff --git a/typ/c-tips/index.typ b/typ/c-tips/index.typ index 3181b84c..f5c8b3d6 100644 --- a/typ/c-tips/index.typ +++ b/typ/c-tips/index.typ @@ -1,10 +1,10 @@ #import "../config.typ": * -#h1(tr(( - en: [Tips for embedded C developers], +#h1((en: [Tips for embedded C developers], de: [Tipps für Embedded-C-Entwickler], + ja: [組込みC開発者へのヒント], zh: [给嵌入式C开发者的贴士], -))) +)) #tr(( en: [ @@ -19,6 +19,10 @@ de: [ in Rust beginnen möchten. Dabei wird insbesondere hervorgehoben, wie sich Dinge, die man aus C bereits kennt, in Rust unterscheiden. ], +ja: [ + この章では、組込みC開発の経験者が、Rustを書き始める時に役に立つ様々なヒントをまとめます。 + 特に、既にC言語で慣れ親しんでいることが、Rustではどう違うのかを強調します。 +], zh: [ 这个章节收集了可能对于刚开始编写Rust的,有经验的嵌入式C开发者来说,有用的各种各样的贴士。它将解释你在C中可能已经用到的那些东西与Rust中的有何不同。 ])) @@ -26,6 +30,7 @@ zh: [ == #tr(( en: [Preprocessor], de: [Präprozessor], + ja: [プリプロセッサ], zh: [预处理器], )) @@ -45,6 +50,12 @@ de: [ - Makros zur Vereinfachung häufiger Muster (um den Overhead von Funktionsaufrufen zu vermeiden) ], +ja: [ + 組込みCでは、次のような様々な目的でプリプロセッサを使うことが一般的です。 + - `#ifdef`を使ったコンパイル時のコードブロック選択 + - コンパイル時の配列サイズやコンパイル時計算 + - (関数呼び出しのオーバーヘッドを避けるための)共通パターンを簡単化するマクロ +], zh: [ 在嵌入式C中,为了各种各样的目的使用预处理器是很常见的,比如: - 使用`#ifdef`编译时选择代码块 @@ -64,6 +75,10 @@ de: [ Abschnitts behandeln wir verschiedene Alternativen zur Verwendung des Präprozessors. ], +ja: [ + Rustにはプリプロセッサはありません。上記のユースケースは異なる方法で解決されます。 + セクションの残りの部分では、プリプロセッサの様々な代替手段について説明します。 +], zh: [ 在Rust中没有预处理器,所以许多案例有不同的处理方法。本章节剩下的部分,我们将介绍各种替代预处理器的方法。 ])) @@ -71,6 +86,7 @@ zh: [ === #tr(( en: [Compile-Time Code Selection], de: [Code-Auswahl zur Kompilierzeit], + ja: [コンパイル時コード選択], zh: [编译时的代码选择], )) @@ -95,6 +111,13 @@ de: [ irgendein Crate im Abhängigkeitsbaum ein Feature für ein anderes Crate aktiviert, ist dieses Feature für alle Nutzer jenes Crates aktiviert. ], +ja: [ + `#ifdef ... #endif`に最も近いRustの機能は、#link(url_features)[Cargoフィーチャ]です。 + Cargoフィーチャは、Cプリプロセッサよりももう少し秩序だったものです。 + フィーチャの候補は、クレートごとに明示的にリスト化されており、オンまたはオフのいずれかになります。 + 依存関係としてクレートを記載すると、フィーチャが有効になります。またこのフィーチャは追加式です。 + 依存ツリー内の何らかのクレートが、別クレートのフィーチャを有効化した場合、そのフィーチャは、そのクレートを使う全てのユーザに対して有効化されます。 +], zh: [ Rust中最接近`#ifdef ... #endif`的是#link(url_features)[Cargo features]。这些比C预处理器更正式一点: 每个crate显式列举的,所有可能的features只能是关了的或者打开了的。当你把一个crate列为依赖项时,Features被打开,且是可添加的:如果你依赖树中的任何crate为另一个crate打开了一个feature,那么这个feature将为所有使用那个crate的用户而打开。 @@ -114,6 +137,11 @@ de: [ von Konstanten definieren, die du gerne vermeiden möchtest. Du könntest für jede Komponente in deiner `Cargo.toml` ein Cargo-Feature definieren: ], +ja: [ + 例えば、信号処理プリミティブを提供するライブラリがあるとします。 + それぞれが、大きな定数テーブルをコンパイルまたは宣言するのに余分な時間がかかるとすると、その時間を回避したいと思うでしょう。 + `Cargo.toml`内で各コンポーネントのフィーチャを宣言することができます。 +], zh: [ 比如,你可能有一个crate,其提供一个信号处理的基本类型库(library of signal processing @@ -136,6 +164,9 @@ de: [ Verwenden Sie dann in Ihrem Code `#[cfg(feature="FIR")]`, um zu steuern, was einbezogen wird. ], +ja: [ + それから、コード内で、何をインクルードするか制御するために`#[cfg(feature="FIR")]`を使います。 +], zh: [ 然后,在你的代码中,使用`#[cfg(feature="FIR")]`去控制要包含什么东西。 ])) @@ -144,6 +175,7 @@ zh: [ "/// " + ts(( en: "In your top-level lib.rs", de: "In deiner lib.rs auf oberster Ebene", + ja: "トップレベルのlib.rs内", zh: "在你的顶层的lib.rs中" )) + " @@ -164,6 +196,11 @@ de: [ _nicht_ aktiviert ist oder wenn eine beliebige Kombination von Funktionen aktiviert oder deaktiviert ist. ], +ja: [ + 同様に、フィーチャが有効になって _いない_ + 場合にだけコードブロックをインクルードすることができます。 + また、フィーチャの組み合わせや、フィーチャが有効か無効かに関わらず、コードブロックをインクルードすることもできます。 +], zh: [ 同样地,你可以控制,只有当某个feature _没有_ 被打开时,包含代码块,或者某些features的组合被打开或者被关闭时。 @@ -186,6 +223,10 @@ de: [ #link(url_conditional)[Bedingte Kompilierung] der Rust-Referenz. ], +ja: [ + さらに、Rustは、自動的に設定される数々の条件を提供します。例えば、アーキテクチャに基づいて異なるコードを選択する`target_arch`です。 + 条件コンパイルがサポートしている全ての詳細については、Rustリファレンスの#link(url_conditional)[条件コンパイル]の章を参照して下さい。 +], zh: [ 另外,Rust提供了许多可以使用的自动配置了的条件,比如`target_arch`用来选择不同的代码所基于的架构。对于条件编译的全部细节,可以参看the Rust @@ -212,6 +253,12 @@ de: [ und Ihre Nutzer einfacher, und der Compiler leistet im Allgemeinen gute Arbeit beim Entfernen von ungenutztem Code. ], +ja: [ + 条件コンパイルは、次のステートメントまたはブロックにのみ適用されます。 + 現在のスコープ内でブロックが使えない場合、`cfg`アトリビュートは複数回必要になります。 + ほとんどの場合、単純に全てのコードをインクルードして、コンパイラが最適化時にデッドコードを削除できるようにするほうが良いことに、注意すべきです。 + これは、あなたにも、あなたのユーザにとってもより簡単です。そして、通常、コンパイラは使用されていないコードをうまく削除します。 +], zh: [ 条件编译将只应用于下一条语句或者块。如果一个块不能在现在的作用域中被使用,那么`cfg`属性将需要被多次使用。值得注意的是大多数时间,仅是包含所有的代码而让编译器在优化时去删除死代码(dead code)更好,通常,在移除不使用的代码方面的工作,编译器做得很好。 @@ -220,6 +267,7 @@ zh: [ === #tr(( en: [Compile-Time Sizes and Computation], de: [Größen und Berechnungen zur Kompilierzeit], + ja: [コンパイル時サイズとコンパイル時計算], zh: [编译时大小和计算], )) @@ -237,6 +285,10 @@ de: [ von Arrays. Dies lässt sich mit den zuvor genannten Funktionen kombinieren, zum Beispiel: ], +ja: [ + Rustは`const fn`を提供しています。この関数はコンパイル時に評価されることが保証されているため、配列のサイズなど、定数が求められる場所で使用できます。 + const fnは、上述したフィーチャと同時に使う事ができます。例を示します。 +], zh: [ Rust支持`const fn`,`const fn`是在编译时可以被计算的函数,因此可以被用在需要常量的地方,比如在数组的大小中。这个能与上述的features一起使用,比如: ])) @@ -266,6 +318,12 @@ de: [ Textes stark eingeschränkt; es ist jedoch zu erwarten, dass die in einer `const fn` zulässigen Operationen in künftigen Rust-Versionen erweitert werden. ], +ja: [ + これらは、Rust + 1.31以降の新しい機能であるため、ドキュメントはまだわずかしかありません。 + 執筆時点では、`const fn`で利用可能な機能は、非常に限られています。 + 将来のRustでは、`const fn`内で許可されることが拡張されていくでしょう。 +], zh: [ 这些对于stable版本的Rust来说是新的特性,从1.31开始引入,因此文档依然很少。在写这篇文章的时候`const fn`可用的功能也非常有限; 在未来的Rust release版本中,我们可以期望`const fn`将带来更多的功能。 @@ -274,6 +332,7 @@ zh: [ === #tr(( en: [Macros], de: [Makros], + ja: [マクロ], zh: [宏], )) @@ -302,6 +361,14 @@ de: [ äußerst leistungsfähige Erweiterungen der Sprache Rust: Sie können beliebige Rust-Syntax in neue Rust-Syntax umwandeln. ], +ja: [ + Rustは、極めて強力な#link(url_macros)[マクロシステム]を提供しています。 + Cプリプロセッサがソースコードのテキストにほぼ直接作用するのに対して、Rustのマクロシステムはより上位レベルで作用します。 + Rustのマクロは2種類あります。_例によるマクロ_ と + _手続きマクロ_ です。 + 前者はより単純で最も一般的なものです。関数呼び出しのように見えて、完全な式やステートメント、アイテム、パターンに展開できます。 + 手続きマクロは、より複雑ですが、Rust言語に非常に強力な拡張を許可します。任意のRust構文を、新しいRust構文に変換することができます。 +], zh: [ Rust提供一个极度强大的#link(url_macros)[宏系统]。虽然C预处理器几乎直接在你的源代码之上进行操作,但是Rust宏系统可以在一个更高的级别上操作。存在两种Rust宏: _声明宏_ 和 _过程宏_ 。前者更简单也最常见; @@ -330,6 +397,12 @@ de: [ funktionieren daher nicht -- etwa Makros, die nur einen Teil eines Variablennamens oder unvollständige Listenelemente erzeugen. ], +ja: [ + 通常、Cプリプロセッサマクロを使っていた場所に、例によるマクロで同じことができるかどうか、確認したいと思います。 + マクロは、クレート内に定義でき、自身のクレート内で簡単に使ったり、他のユーザにエクスポートしたりできます。 + マクロは、完全な式や、ステートメント、アイテム、パターンに展開されなければならないため、Cプリプロセッサマクロのいくつかのユースケースではうまく機能しません。 + 例えば、変数名や、リスト内の不完全なアイテムの一部に展開するようなマクロです。 +], zh: [ 通常,你可能想知道在那些使用一个C预处理器宏的地方,能否使用一个声明宏做同样的工作。你可以在crate中定义它们,且在你的crate中轻松使用它们或者导出给其他人用。但是请注意,因为它们必须扩展成完整的表达式,语句,项或者模式,因此C预处理器宏的某些用例没法用,比如可以扩展成一个变量名的一部分的宏或者可以把列表中的项扩展成不完整的集合的宏。 ])) @@ -357,6 +430,13 @@ de: [ Crate ohnehin automatisch, wenn dies sinnvoll ist; ein erzwungenes Inlining in ungeeigneten Fällen kann die Leistung sogar verschlechtern. ], +ja: [ + Cargoフィーチャと同様、本当にマクロが必要かどうか、は検討する価値があります。 + 多くの場合、通常の関数は理解しやすく、マクロと同様にインライン化されます。 + `#[inline]`および`#[inline(always)]`の#link(url_inline)[アトリビュート]を使用すると、このプロセスをさらに細かく制御できます。 + ここでも注意が必要です。コンパイラは、適切な場合、関数を自動的にインライン化します。 + そのため、不適切なインライン化を強制すると、パフォーマンスが低下する可能性があります。 +], zh: [ 和Cargo features一样,值得考虑下你是否真的需要宏。在一些例子中一个常规的函数更容易被理解,它也能被内联成和一个和宏一样的代码。`#[inline]`和`#[inline(always)]` @@ -376,6 +456,10 @@ de: [ Tipps-Seite sprengen; für alle Einzelheiten sei daher auf die Rust-Dokumentation verwiesen. ], +ja: [ + Rustのマクロシステムの全体を説明することは、このヒントページのスコープ範囲外です。 + 詳細については、Rustのドキュメントの参照をお勧めします。 +], zh: [ 研究完整的Rust宏系统超出了本节内容,因此我们鼓励你去查阅Rust文档了解完整的细节。 ])) @@ -383,6 +467,7 @@ zh: [ == #tr(( en: [Build System], de: [Build System], + ja: [ビルドシステム], zh: [编译系统], )) @@ -406,6 +491,12 @@ de: [ bereit. Dabei handelt es sich um Rust-Skripte, die bei Bedarf mit dem Cargo-Build-System interagieren können. ], +ja: [ + (必須ではありませんが)ほとんどのRustのクレートは、Cargoを使ってビルドされます。 + Cargoは、従来のビルドシステムに関する多くの難しい問題の面倒を見ています。 + しかし、ビルドプロセスをカスタマイズしたいと思うかもしれません。このため、Cargoは#link(url_build_scripts)[`build.rs`スクリプト]を提供しています。 + build.rsスクリプトはRustで書かれたスクリプトで、必要に応じてCargoのビルドシステムとやり取りします。 +], zh: [ 大多数Rust crates使用Cargo编译 (即使这不是必须的)。这解决了传统编译系统带来的许多难题。然而,你可能希望自定义编译过程。为了实现这个目的,Cargo提供了#link(url_build_scripts)[`build.rs`脚本]。它们是可以根据需要与Cargo编译系统进行交互的Rust脚本。 @@ -431,6 +522,13 @@ de: [ - die Cargo-Build-Konfiguration ändern - Zusätzliche statische Bibliotheken für den Linkvorgang hinzufügen ], +ja: [ + ビルドスクリプトの一般的なユースケースを示します。 + - ビルド時の情報を提供します。例えば、実行ファイルにビルド日時やGitのコミットハッシュを静的に埋め込みます。 + - 選択されたフィーチャやその他のロジックに応じて、リンカスクリプトをビルド時に生成します。 + - Cargoのビルド設定を変更します。 + - リンクする静的ライブラリを追加します。 +], zh: [ 与编译脚本有关的常见用例包括: - 提供编译时信息,比如静态嵌入编译日期或者Git commit @@ -452,6 +550,10 @@ de: [ Binärdateien aus den Build-Objekten oder die Ausgabe von Build-Informationen verwendet hat. ], +ja: [ + 現状、ビルド後に実行するスクリプトは提供されていません。 + そのようなスクリプトは、従来では、ビルドしたオブジェクトからバイナリを自動的に生成したり、ビルド情報を表示したりするタスクに使われています。 +], zh: [ 现在还不支持post-build脚本,通常将它用于像是从编译的对象自动生生成二进制文件或者打印编译信息这类任务中。 ])) @@ -459,6 +561,7 @@ zh: [ === #tr(( en: [Cross-Compiling], de: [Cross-Kompilierung], + ja: [クロスコンパイル], zh: [交叉编译], )) @@ -474,32 +577,43 @@ de: [ `--target thumbv6m-none-eabi` mitzugeben und die entsprechende ausführbare Datei unter `target/thumbv6m-none-eabi/debug/myapp` zu finden. ], +ja: [ + Cargoをビルドシステムに使用するとクロスコンパイルも簡単になります。 + 多くの場合、Cargoに`--target thumbv6m-none-eabi`を伝えるだけで十分です。 + そうすると、適切な実行ファイルが`target/thumbv6m-none-eabi/debug/myapp`に見つかります。 +], zh: [ 为你的编译系统使用Cargo也能简化交叉编译。在大多数例子里,告诉Cargo `--target thumbv6m-none-eabi`就行了,可以在`target/thumbv6m-none-eabi/debug/myapp`中找到一个合适的可执行文件。 ])) +#let ln_xargo = link("https://github.com/japaric/xargo")[Xargo] #tr(( en: [ For platforms not natively supported by Rust, you will need to build `libcore` for that target yourself. On such platforms, - #link("https://github.com/japaric/xargo")[Xargo] can be used as a + #ln_xargo can be used as a stand-in for Cargo which automatically builds `libcore` for you. ], de: [ Für Plattformen, die nicht nativ von Rust unterstützt werden, müssen Sie `libcore` für das jeweilige Zielsystem selbst kompilieren. Auf solchen - Plattformen lässt sich #link("https://github.com/japaric/xargo")[Xargo] + Plattformen lässt sich #ln_xargo als Ersatz für Cargo verwenden, da es `libcore` automatisch für Sie erstellt. ], +ja: [ + Rustが本来サポートしていないプラットフォームの場合、ターゲットの`libcore`を自分自身でビルドする必要があります。 + そのようなプラットフォームでは、#ln_xargo;をCargoの代わりに使うことができ、自動的に`libcore`をビルドしてくれます。 +], zh: [ - 对于那些并不是Rust原生支持的平台,将需要自己为那个目标平台编译`libcore`。遇到这样的平台,#link("https://github.com/japaric/xargo")[Xargo]可以作为Cargo的替代来使用,它可以自动地为你编译`libcore`。 + 对于那些并不是Rust原生支持的平台,将需要自己为那个目标平台编译`libcore`。遇到这样的平台,#ln_xargo;可以作为Cargo的替代来使用,它可以自动地为你编译`libcore`。 ])) == #tr(( en: [Iterators vs Array Access], de: [Iteratoren vs.~Array-Zugriff], + ja: [イテレータ vs 配列アクセス], zh: [迭代器与数组访问], )) @@ -511,6 +625,9 @@ de: [ In C sind Sie es wahrscheinlich gewohnt, direkt über den Index auf Arrays zuzugreifen: ], +ja: [ + Cでは、おそらくインデックスによって直接配列にアクセスしているでしょう。 +], zh: [ 在C中,你可能习惯于通过索引直接访问数组: ])) @@ -539,6 +656,12 @@ de: [ Zugriffe außerhalb der Grenzen, um Speichersicherheit zu gewährleisten, während C problemlos auf Bereiche außerhalb des Arrays zugreift. ], +ja: [ + Rustでは、これはアンチパターンです。インデックスによるアクセスは、低速(境界チェックが必要なため)で様々なコンパイラの最適化を妨げます。 + これは重要な違いであり、繰り返す価値があります。 + Rustは、メモリ安全性を保証するために、手動で配列のインデックスを指定する際、境界を越えたアクセスをチェックします。 + 一方、Cでは配列外のインデックスにアクセスできてしまいます。 +], zh: [ 在Rust中,这是一个反模式(anti-pattern):索引访问可能会更慢(因为它可能需要做边界检查)且可能会阻止编译器的各种优化。这是一个重要的区别,值得再重复一遍: Rust会在手动的数组索引上进行越界检查以保障内存安全性,而C允许索引数组外的内容。 @@ -551,6 +674,9 @@ en: [ de: [ Verwenden Sie stattdessen Iteratoren. ], +ja: [ + 代わりに、イテレータを使います。 +], zh: [ 可以使用迭代器来替代: ])) @@ -576,6 +702,10 @@ de: [ Iteratormethoden lassen sich ebenfalls verketten, was zu sehr lesbarem Code für die Datenverarbeitung führt. ], +ja: [ + イテレータは、chaining、zipping、enumerating、最小値や最大値の検索、合計の算出など、Cでは手動で実装する必要がある強力な配列の機能を提供します。 + イテレータのメソッドは、連鎖することができ、非常に読みやすいデータ処理のコードになります。 +], zh: [ 迭代器提供了一个有强大功能的数组,在C中你不得不手动实现它,比如chaining,zipping,enumerating,找到最小或最大值,summing,等等。迭代器方法也能被链式调用,提供了可读性非常高的数据处理代码。 ])) @@ -592,6 +722,9 @@ de: [ #link(url_iter_book)[Iteratoren im Buch] und #link(url_iter_doc)[Iterator-Dokumentation]. ], +ja: [ + 詳細は#link(url_iter_book)[the Bookのイテレータ]と#link(url_iter_doc)[イテレータのドキュメント]を参照して下さい。 +], zh: [ 阅读#link(url_iter_book)[Iterators in the Book]和#link(url_iter_doc)[Iterator documentation]获取更多细节。 ])) @@ -599,6 +732,7 @@ zh: [ == #tr(( en: [References vs Pointers], de: [Referenzen vs.~Zeiger], + ja: [参照 vs ポインタ], zh: [引用和指针], )) @@ -617,6 +751,12 @@ de: [ Dereferenzierung stets als `unsafe` gilt -- Rust kann nämlich nicht die üblichen Garantien darüber geben, was sich hinter dem Zeiger befindet. ], +ja: [ + Rustでも、ポインタ(\[_生ポインタ_ + と呼びます\])は存在しますが、限られた状況でしか使いません。 + ポインタの参照外しは、常に`unsafe`と考えられるからです。 + Rustは、ポインタの背後にあるかもしれないものについて、通常の保証を提供できません。 +], zh: [ 在Rust中,存在指针(被叫做 #link(url_derefraw)[_裸指针_])但是只能在特殊的环境中被使用,因为解引用裸指针总是被认为是`unsafe`的 -- Rust通常不能保障指针背后有什么。 @@ -643,6 +783,13 @@ de: [ _oder_ mehrere unveränderbare Referenzen auf denselben Wert existieren dürfen. ], +ja: [ + 代わりに、ほとんどの場合、`&`のシンボルで表現される _参照_ もしくは + `&mut`で表現される _ミュータブルな参照_ を使います。 + 参照は、ポインタと似た働きをします。つまり、裏にある値にアクセスするために参照外しができます。 + しかし、参照は、Rustの所有権システムの重要な一部です。 + Rustは、どんな時でも同じ値に対して、唯一のミュータブル参照を持つか、_あるいは_、複数のイミュータブル参照を持つか、を厳密に強制します。 +], zh: [ 在大多数例子里,我们使用 _引用_ 来替代,由`&`符号指出,或者 _可变引用_,由`&mut`指出。引用与指针相似,因为它能被解引用来访问底层的数据,但是它们是Rust的所有权系统的一个关键部分: @@ -662,6 +809,10 @@ de: [ der Standard ist und `const` explizit angegeben werden muss, verhält es sich in Rust genau umgekehrt. ], +ja: [ + 実際のところ、データへのミュータブルアクセスが必要かどうか、をより慎重に検討する必要があることを意味します。 + Cではデフォルトがミュータブルであり、明示的に`const`をつける必要があります。Rustではその反対です。 +], zh: [ 在实践中,这意味着你必须要更加小心你是否需要对数据的可变访问:在C中默认是可变的,你必须显式地使用`const`,在Rust中正好相反。 ])) @@ -682,6 +833,10 @@ de: [ Peripheriezugriff zum Einsatz, um das Lesen und Schreiben speicherabgebildeter Register (memory-mapped registers) zu ermöglichen. ], +ja: [ + 生ポインタを使う可能性のある状況の1つは、直接ハードウェアとやり取りする時です(DMAペリフェラルのレジスタにバッファのポインタを書き込むなど)。 + また、生ポインタは、メモリマップドレジスタの読み書きを可能にするために、ペリフェラルアクセスクレートの内部で使われています。 +], zh: [ 某个情况下,你可能仍然要使用裸指针直接与硬件进行交互(比如,写入一个指向DMA外设寄存器中的缓存的指针),它们也被所有的外设访问crates在底层使用,让你可以读取和写入存储映射寄存器。 ])) @@ -689,6 +844,7 @@ zh: [ == #tr(( en: [Volatile Access], de: [Volatiler (unsicherer) Zugriff], + ja: [Volatileアクセス], zh: [Volatile访问], )) @@ -705,6 +861,11 @@ de: [ ändern kann. Volatile Variablen werden häufig in eingebetteten Systemen für im Speicher abgebildete Register verwendet. ], +ja: [ + Cでは、個別の変数に`volatile`を付けることができます。 + これは、変数の値がアクセスごとに変わるかもしれない、ということをコンパイラに伝えます。 + 組込みでは、Volatile変数はメモリマップドレジスタに広く使用されています。 +], zh: [ 在C中,某个变量可能被标记成`volatile`,向编译器指出,变量中的值在访问间可能改变。Volatile变量通常用于一个与存储映射的寄存器有关的嵌入式上下文中。 ])) @@ -726,6 +887,12 @@ de: [ (Rohzeiger, wie oben beschrieben) und führen einen flüchtigen Lese- bzw. Schreibvorgang durch. ], +ja: [ + Rustでは、変数に`volatile`を付けるのではなく、volatileアクセスをするための特定のメソッドを使います。 + #ln_read;と#ln_write;です。 + これらのメソッドは、`*const T`か`*mut T`(上述の通り _生ポインタ_ + です)を受け取り、volatileな読み書きを行います。 +], zh: [ 在Rsut中,并不使用`volatile`标记变量,我们使用特定的方法去执行volatile访问: #ln_read 和 #ln_write。这些方法使用一个 @@ -740,6 +907,9 @@ en: [ de: [ In C könnten Sie zum Beispiel schreiben: ], +ja: [ + 例えば、Cでは次のように書きます。 +], zh: [ 比如,在C中你可能这样写: ])) @@ -751,6 +921,7 @@ void ISR() { // " + ts(( en: "Signal that the interrupt has occurred", de: "Signalisieren, dass der Interrupt aufgetreten ist", + ja: "割り込みが発生したというシグナル", zh: "提醒中断已经发生了" )) + " signalled = true; @@ -761,18 +932,21 @@ void driver() { // " + ts(( en: "Sleep until signalled", de: "Schlafen bis zum Signal", + ja: "シグナルがあるまでスリープします", zh: "睡眠直到信号来了", )) + " while(!signalled) { WFI(); } // " + ts(( en: "Reset signalled indicator", de: "Signalisierten Indikator zuruecksetzen", + ja: "シグナルをリセットします", zh: "重置信号提示符" )) + " signalled = false; // " + ts(( en: "Perform some task that was waiting for the interrupt", de: "Fuehren Sie eine Aufgabe aus, die auf den Interrupt gewartet hat", + ja: "割り込みを待っていた何らかのタスクを実行します", zh: "执行一些正在等待这个中断的任务", )) + " run_task(); @@ -787,6 +961,9 @@ en: [ de: [ Das Äquivalent in Rust würde bei jedem Zugriff volatile Methoden verwenden: ], +ja: [ + Rustで同じことをするには、各アクセスにvolatileメソッドを使用します。 +], zh: [ 在Rust中对每个访问使用volatile方法能达到相同的效果: ])) @@ -803,6 +980,8 @@ fn ISR() { de: "Signalisieren, dass eine Unterbrechung aufgetreten ist // (Im realen Code sollten Sie eine hoeherwertige primitive Datenstruktur, // wie z. B. einen atomaren Datentyp, in Betracht ziehen).", + ja: "割り込みが発生したというシグナル + // (実際のコードでは、アトミック型のような、より上位レベルのプリミティブを検討して下さい)", zh: "提醒中断已经发生 // (在正在的代码中,你应该考虑一个更高级的基本类型, // 比如一个原子类型)" @@ -815,18 +994,21 @@ fn driver() { // " + ts(( en: "Sleep until signalled", de: "Schlafen bis zum Signal", + ja: "シグナルがあるまでスリープします", zh: "睡眠直到信号来了", )) + " while unsafe { !core::ptr::read_volatile(&SIGNALLED) } {} // " + ts(( en: "Reset signalled indicator", de: "Signalisierten Indikator zuruecksetzen", + ja: "シグナルをリセットします", zh: "重置信号指示符", )) + " unsafe { core::ptr::write_volatile(&mut SIGNALLED, false) }; // " + ts(( en: "Perform some task that was waiting for the interrupt", de: "Fuehren Sie eine Aufgabe aus, die auf den Interrupt gewartet hat", + ja: "割り込みを待っていた何らかのタスクを実行します", zh: "执行一些正在等待中断的任务", )) + " run_task(); @@ -854,6 +1036,14 @@ de: [ zu sorgen; weitere Einzelheiten sind der Dokumentation der jeweiligen Methoden zu entnehmen. ], +ja: [ + このコードサンプルには、いくつかの注目すべき点があります。 + - `*mut T`を要求する関数に、`&mut SIGNALLED`を渡すことができます。 + これは、`&mut T`が`*mut T`に自動的に変換されるためです(`*const T`についても同じです)。 + - `read_volatile`/`write_volatile`メソッドに`unsafe`ブロックが必要です。 + これらの関数は`unsafe`だからです。安全な使用を保証することはプログラマの責任です。 + 詳細は、メソッドのドキュメントを参照して下さい。 +], zh: [ 在示例代码中有些事情值得注意: - 我们可以把`&mut SIGNALLED`传递给要求`*mut T`的函数中,因为`&mut T`会自动转换成一个`*mut T` @@ -879,6 +1069,11 @@ de: [ bessere Abstraktionen zur Verfügung stehen (siehe das Kapitel #link()[Nebenläufigkeit]). ], +ja: [ + これらの関数をコードに直接書くことは稀です。通常、より上位レベルのライブラリで面倒を見てくれます。 + メモリマップドペリフェラルについては、ペリフェラルアクセスクレートがvolatileアクセスを自動的に実装します。 + 並行性プリミティブの場合、より優れた抽象化が利用できます(#link()[並行性の章]を参照して下さい)。 +], zh: [ 在你的代码中直接使用这些函数是很少见的,因为它们通常由更高级的库封装起来为你提供服务。对于存储映射的外设,提供外设访问的crates将自动实现volatile访问,而对于并发的基本类型,存在更好的抽象可用。(看#link()[并发章节]) ])) @@ -886,6 +1081,7 @@ zh: [ == #tr(( en: [Packed and Aligned Types], de: [Gepackte und ausgerichtete Datentypen], + ja: [パック型と整列型], zh: [填充和对齐类型], )) @@ -901,6 +1097,10 @@ de: [ aufweisen oder eine Struktur „gepackt" (packed) statt ausgerichtet sein muss -- meist, um spezifische Hardware- oder Protokollanforderungen zu erfüllen. ], +ja: [ + 組込みCでは、通常、特定のハードウェアやプロトコルの要件を満たすために、変数に特定のアライメントが必要なことや、 + 構造体が整列されているだけでなくパックされている必要があることを、コンパイラに指示することが一般的です。 +], zh: [ 在嵌入式C中,告诉编译器一个变量必须遵守某个对齐或者一个结构体必须被填充而不是对齐,是很常见的行为,通常是为了满足特定的硬件或者协议要求。 ])) @@ -921,6 +1121,11 @@ de: [ Strukturmitglieder ändern oder Füllbytes (Padding) einfügen, und das Verhalten kann sich in zukünftigen Rust-Versionen ändern. ], +ja: [ + Rustでは、これは構造体または共用体の`repr`アトリビュートによって制御されます。 + デフォルトでは、レイアウトは保証されないため、ハードウェアやCとやり取りするコードでは使うべきではありません。 + コンパイラは、構造体のメンバを並べ替えたり、パディングを挿入したりする可能性があります。この動作は将来のバージョンのRustで変更になる可能性があります。 +], zh: [ 在Rust中,这由一个结构体或者联合体上的`repr`属性来控制。默认的表示(representation)不保障布局,因此不应该被用于与硬件或者C互用的代码。编译器可能会对结构体成员重新排序或者插入填充,且这种行为可能在未来的Rust版本中改变。 ])) @@ -942,6 +1147,7 @@ fn main() { en: "Note ordering has been changed to x, z, y to improve packing.", de: "Die Reihenfolge der Noten wurde auf x, z, y geaendert, um die Packdichte zu // verbessern.", + ja: "データの詰め方を改善するために、x, y, zの順序が入れ替わっていることに注目して下さい。", zh: "0x7ffecb3511d0 0x7ffecb3511d4 0x7ffecb3511d2 // 注意为了改进填充,顺序已经被变成了x, z, y" )) + " @@ -954,6 +1160,9 @@ en: [ de: [ Um Layouts zu gewährleisten, die mit C interoperabel sind, verwenden Sie `repr(C)`: ], +ja: [ + Cと相互にやり取りできるレイアウトを保証するためには、`repr(C)`を使います。 +], zh: [ 使用`repr(C)`可以确保布局可以与C互用。 ])) @@ -979,6 +1188,8 @@ fn main() { // Zeit nicht. // `z` ist auf Zwei-Byte-Grenzen ausgerichtet, sodass sich zwischen `y` und `z` // ein Padding-Byte befindet.", + ja: "順序は維持され、レイアウトは時間が経っても変化しません。 +// `z`は2バイトで整列されており、`y`と`z`の間には、1バイトのパディングが存在します。", zh: "0x7fffd0d84c60 0x7fffd0d84c62 0x7fffd0d84c64 // 顺序被保留了,布局将不会随着时间而改变 // `z`是两个字节对齐,因此在`y`和`z`之间填充了一个字节。" @@ -992,6 +1203,9 @@ en: [ de: [ Um eine kompakte Darstellung zu gewährleisten, verwenden Sie `repr(packed)`: ], +ja: [ + パックされた表現を保証する場合、`repr(packed)`を使います。 +], zh: [ 使用`repr(packed)`去确保表示(representation)被填充了: ])) @@ -1014,6 +1228,7 @@ fn main() { // Adressen der Struct-Felder zu ueberpruefen, verwenden wir // `std::ptr::addr_of!()`, um einen Rohzeiger (Raw Pointer) zu erhalten, // anstatt einfach `&v.x` auszugeben.", + ja: "パックされた構造体のフィールドを借用するには、アンセーフが必要です。", zh: "引用必须总是对齐的,因此为了检查结构体字段的地址,我们使用 // `std::ptr::addr_of!()`去获取一个裸指针而不仅是打印`&v.x`" )) + " @@ -1028,6 +1243,7 @@ fn main() { en: "No padding has been inserted between `y` and `z`, so now `z` is unaligned.", de: "Zwischen `y` und `z` wurde kein Padding eingefuegt, daher ist `z` nun // nicht ausgerichtet.", + ja: "`y`と`z`の間にパディングは挿入されていません。そのため、`z`は整列されていません。", zh: "0x7ffd33598490 0x7ffd33598492 0x7ffd33598493 // 在`y`和`z`没有填充被插入,因此现在`z`没有被对齐。" )) + " @@ -1041,6 +1257,9 @@ de: [ Beachten Sie, dass die Verwendung von `repr(packed)` die Ausrichtung des Typs ebenfalls auf `1` setzt. ], +ja: [ + `repr(packed)`を使うと、型のアライメントも`1`に設定されることに注意して下さい。 +], zh: [ 注意使用`repr(packed)`也会将类型的对齐设置成`1` 。 ])) @@ -1055,6 +1274,10 @@ de: [ `repr(align(n))`, wobei `n` die Anzahl der auszurichtenden Bytes ist (und eine Zweierpotenz sein muss). ], +ja: [ + 最後に、特定のアライメントを指定するために、`repr(align(n))`を使います。 + ここで`n`は、整列するバイト数です(2の累乗である必要があります)。 +], zh: [ 最后,为了指定一个特定的对齐,可以使用`repr(align(n))`,`n`是要对齐的字节数(必须是2的幂): ])) @@ -1082,6 +1305,8 @@ fn main() { // evidenced by the `000` at the end of their addresses.", de: "Die beiden Instanzen `u` und `v` wurden an 4096-Byte-Grenzen ausgerichtet, // was an den `000` am Ende ihrer Adressen erkennbar ist.", + ja: "2つのインスタンス`u`と`v`は4096バイトのアライメントで配置されます。 +// インスタンスのアドレスの最後は`000`になっています。", zh: "`u`和`v`两个实例已经被放置在4096字节的对齐上。 // 它们地址结尾处的`000`证明了这件事。" )) + " @@ -1103,6 +1328,11 @@ de: [ zulässig, dass ein `repr(packed)`-Typ einen `repr(align(n))`-Typ enthält. ], +ja: [ + 整列されていてCと互換性のあるレイアウトを取得するため、`repr(C)`と`repr(align(n))`とを組み合わせることができます。 + `repr(align(n))`と`repr(packed)`とを組み合わせることはできません。`repr(packed)`はアライメントを`1`に設定するからです。 + `repr(packed)`の型を`repr(align(n))`の型に含めることもできません。 +], zh: [ 注意我们可以结合`repr(C)`和`repr(align(n))`来获取一个对齐的c兼容的布局。不允许将`repr(align(n))`和`repr(packed)`一起使用,因为`repr(packed)`将对齐设置为`1`。也不允许一个`repr(packed)`类型包含一个`repr(align(n))`类型。 ])) @@ -1115,17 +1345,19 @@ en: [ ], de: [ Weitere Details zu Typ-Layouts finden Sie im Kapitel - #link(url_layout)[Typ-Layout] - der Rust-Referenz. + #link(url_layout)[Typ-Layout] der Rust-Referenz. +], +ja: [ + 型レイアウトに関するさらなる詳細は、Rustリファレンスの#link(url_layout)[型レイアウト]の章を参照して下さい。 ], zh: [ - 关于类型布局更多的细节,参考the Rust - Reference的#link("https://doc.rust-lang.org/reference/type-layout.html")[type layout]章节。 + 关于类型布局更多的细节,参考the Rust Reference的#link(url_layout)[type layout]章节。 ])) == #tr(( en: [Other Resources], de: [Weitere Ressourcen], + ja: [その他のリソース], zh: [其它资源], )) @@ -1149,6 +1381,14 @@ de: [ - #link(url_for_c)[Rust-Pointer für C-Programmierer] - #link(url_pointers)[Früher habe ich Pointer verwendet -- und jetzt?] ], +ja: [ + - 本書内 + - #link()[Rustと少しのC] + - #link()[Cと少しのRust] + - #link(url_faq)[組込みRustよくある質問] + - #link(url_for_c)[CプログラマのためのRustのポインタ] + - #link(url_pointers)[昔はポインタを使っていました。今は?] +], zh: [ - 这本书中: - #link()[使用C的Rust] diff --git a/typ/collections/index.typ b/typ/collections/index.typ index 479af1a4..e01d20b0 100644 --- a/typ/collections/index.typ +++ b/typ/collections/index.typ @@ -1,10 +1,10 @@ #import "../config.typ": * -#h1(tr(( - en: [Collections], +#h1((en: [Collections], de: [Sammlungen (Collections)], + ja: [コレクション], zh: [集合], -))) +)) #let ln_vec = link("https://doc.rust-lang.org/std/vec/struct.Vec.html")[`Vec`] #let ln_string = link("https://doc.rust-lang.org/std/string/struct.String.html")[`String`] @@ -25,6 +25,11 @@ de: [ Alle in `std` implementierten Collections nutzen einen globalen dynamischen Speicherverwalter (den sogenannten Heap). ], +ja: [ + いずれは、プログラム内で動的なデータ構造(別名コレクション)を使いたいでしょう。 + `std`は、#ln_vec;や#ln_string;、#ln_map;といった、一般的なコレクションを提供しています。 + `std`で実装されている全てのコレクションは、グローバルな動的アロケータ(別名ヒープ)を使用します。 +], zh: [ 最后,还希望在程序里使用动态数据结构(也称为集合)。`std` 提供了一组常见的集合: @@ -42,6 +47,10 @@ de: [ diese Implementierungen dort nicht zur Verfügung; sie sind jedoch im `alloc`-Crate zu finden, das mit dem Compiler ausgeliefert wird. ], +ja: [ + `core`は、定義上、メモリアロケーションがないためコレクションの実装を使うことができません。 + しかし、コンパイラと共に配布されている_安定化していない_`alloc`クレートの中にコレクションの実装があります。 +], zh: [ 因为`core`的定义中是没有内存分配的,所以这些实现在`core`中是没有的,但是我们可以在编译器附带的`alloc` crate中找到。 @@ -60,6 +69,10 @@ de: [ mit _fester Kapazität_ verwenden; eine solche Implementierung findet sich im #ln_heapless;-Crate. ], +ja: [ + もしコレクションが必要であれば、ヒープに割り当てる実装だけが選択肢ではありません。 + _サイズが固定された_コレクションを使うことができます。そのような実装は#ln_heapless;クレートの中にあります。 +], zh: [ 如果需要集合,一个基于堆分配的实现不是唯一的选择。也可以使用 _fixed capacity_ 集合; 其实现可以在 #ln_heapless crate中被找到。 @@ -73,6 +86,10 @@ de: [ In diesem Abschnitt werden wir diese beiden Implementierungen untersuchen und vergleichen. ], +ja: [ + `alloc`クレートは、標準のRust配布物に同梱されています。このクレートをインポートするには、 + `Cargo.toml`ファイルに依存関係を宣言_することなしに_直接`use`します。 +], zh: [ 在这部分,我们将研究和比较这两个实现。 ])) @@ -80,6 +97,7 @@ zh: [ == #tr(( en: [Using `alloc`], de: [Verwendung von `alloc`], + ja: [`alloc`を使用], zh: [使用 `alloc`], )) @@ -94,6 +112,10 @@ de: [ das Crate zu importieren, können Sie es direkt per `use` einbinden, _ohne_ es als Abhängigkeit in Ihrer `Cargo.toml`-Datei zu deklarieren. ], +ja: [ + `alloc`クレートは、標準のRust配布物に同梱されています。このクレートをインポートするには、 + `Cargo.toml`ファイルに依存関係を宣言_することなしに_直接`use`します。 +], zh: [ `alloc` crate与标准的Rust发行版在一起。你可以直接 `use` 导入这个crate,而不需要在`Cargo.toml`文件中把它声明为一个依赖。 @@ -121,6 +143,10 @@ de: [ deklarieren, den Ihr Programm einsetzen soll. Der gewählte Allocator muss zwingend das Trait #ln_alloc implementieren. ], +ja: [ + コレクションを使うには、まず最初に、プログラム中のグローバルアロケータを宣言する`global_allocator`アトリビュートを使う必要があります。 + 選択したアロケータが#ln_alloc;トレイトを実装することが求められます。 +], zh: [ 为了能使用集合,首先需要使用`global_allocator`属性去声明程序将使用的全局分配器。它要求选择的分配器实现了#link("https://doc.rust-lang.org/core/alloc/trait.GlobalAlloc.html")[`GlobalAlloc`] trait 。 @@ -140,6 +166,10 @@ de: [ empfehlen Ihnen jedoch _dringend_, in Ihrem Programm stattdessen einen praxiserprobten Allokator von crates.io zu verwenden. ], +ja: [ + このセクションを可能な限り自己完結させるため、グローバルアロケータとして、単純にポインタを増加するだけのアロケータを実装します。 + しかしながら、あなたのプログラムではこのアロケータでなく、crates.ioから歴戦のアロケータを使用することを_強く_お勧めします。 +], zh: [ 为了完整性和尽可能保持本节的自包含性,我们将实现一个简单线性指针分配器且用它作为全局分配器。然而,我们 _强烈地_ @@ -150,6 +180,7 @@ zh: [ "// " + ts(( en: "Bump pointer allocator implementation", de: "Implementierung eines Bump-Pointer-Allocators", + ja: "ポインタを増加するだけのアロケータ実装", zh: "线性指针分配器实现", )) + " @@ -162,6 +193,7 @@ use cortex_m::interrupt; // " + ts(( en: "Bump pointer allocator for *single* core systems", de: "Bump-Pointer-Allocator fuer Systeme mit *einem* Rechenkern", + ja: "*シングル*コアシステム用のポインタを増加するだけのアロケータ", zh: "用于单核系统的线性指针分配器", )) + " struct BumpPointerAlloc { @@ -178,6 +210,8 @@ unsafe impl GlobalAlloc for BumpPointerAlloc { // to use from within interrupts", de: "`interrupt::free` ist ein kritischer Abschnitt, der die sichere // Verwendung unseres Allocators aus Interrupts heraus ermoeglicht.", + ja: "`interrupt::free`は、割り込み内でアロケータを安全に使用するための + // クリティカルセクションです。", zh: "`interrupt::free`是一个临界区,临界区让我们的分配器在中断中用起来安全", )) + " interrupt::free(|_| { @@ -189,6 +223,7 @@ unsafe impl GlobalAlloc for BumpPointerAlloc { // " + ts(( en: "move start up to the next alignment boundary", de: "Verschiebe den Startpunkt zur naechsten Ausrichtungsgrenze.", + ja: "ヌルポインタはメモリ不足の状態を知らせます", zh: "将start移至下一个对齐边界。", )) + " let start = (*head + align - 1) & align_mask; @@ -197,6 +232,7 @@ unsafe impl GlobalAlloc for BumpPointerAlloc { // " + ts(( en: "a null pointer signal an Out Of Memory condition", de: "ein Nullzeiger signalisiert einen „Out of Memory“-Zustand", + ja: "このアロケータはメモリを解放しません", zh: "一个空指针通知内存不足", )) + " ptr::null_mut() @@ -211,6 +247,7 @@ unsafe impl GlobalAlloc for BumpPointerAlloc { // " + ts(( en: "this allocator never deallocates memory", de: "Dieser Allokator gibt niemals Speicher frei.", + ja: "このアロケータはメモリを解放しません", zh: "这个分配器从不释放内存", )) + " } @@ -224,6 +261,9 @@ unsafe impl GlobalAlloc for BumpPointerAlloc { // HINWEIS: Der Benutzer muss sicherstellen, dass der Speicherbereich // `[0x2000_0100, 0x2000_0200]` nicht von anderen Teilen des Programms // verwendet wird.", + ja: "グローバルメモリアロケータの宣言 +// ユーザはメモリ領域の`[0x2000_0100, 0x2000_0200]`がプログラムの他の部分で使用されないことを +// 保証しなければなりません", zh: "全局内存分配器的声明 // 注意 用户必须确保`[0x2000_0100, 0x2000_0200]`内存区域 // 没有被程序的其它部分使用" @@ -247,6 +287,10 @@ de: [ hierfür wird das _instabile_ Attribut `alloc_error_handler` verwendet. ], +ja: [ + グローバルアロケータの選択とは別に、ユーザはメモリ不足(OOM)エラーの処理方法を、 + _安定化していない_`alloc_error_handler`アトリビュートを使って定義する必要があります。 +], zh: [ 除了选择一个全局分配器,用户也必须要定义如何使用_不稳定的_`alloc_error_handler`属性来处理内存溢出错误。 ])) @@ -272,6 +316,9 @@ de: [ Sobald alles vorhanden ist, kann der Benutzer endlich die Sammlungen in „alloc" verwenden. ], +ja: [ + 全ての準備が整うと、ユーザはついに`alloc`のコレクションを使うことができます。 +], zh: [ 一旦一切都完成了,用户最后就可以在`alloc`中使用集合。 ])) @@ -300,6 +347,9 @@ de: [ werden Ihnen diese vertraut vorkommen, da es sich um exakt dieselbe Implementierung handelt. ], +ja: [ + `std`クレートのコレクションを使ったことがあれば、実装が全く同じものであるため、これらのコレクションはお馴染みでしょう。 +], zh: [ 如果你已经使用了`std` crate中的集合,那么这些对你来说将非常熟悉,因为他们的实现一样。 @@ -308,6 +358,7 @@ zh: [ == #tr(( en: [Using `heapless`], de: [Verwendung von `heapless`], + ja: [`heapless`の使用], zh: [使用 `heapless`], )) @@ -321,6 +372,10 @@ de: [ von einem globalen Speicher-Allocator abhängen. Binde die Datenstrukturen einfach per `use` ein und instanziiere sie: ], +ja: [ + `heapless`のコレクションはグローバルメモリアロケータに依存しないため、準備は不要です。 + 単にコレクションを`use`して、インスタンスを作成するだけです。 +], zh: [ `heapless`无需设置,因为它的集合不依赖一个全局内存分配器。只是`use`它的集合然后实例化它们: ])) @@ -348,6 +403,9 @@ de: [ Sie werden zwei Unterschiede zwischen diesen Sammlungen und denen in `alloc` feststellen. ], +ja: [ + `alloc`のコレクションとは違う点が2つあることに留意して下さい。 +], zh: [ 你会注意到这些集合与`alloc`中的集合有两个不一样的地方。 ])) @@ -371,6 +429,12 @@ de: [ wird durch das `u8` (siehe #ln_typenum) in der Typsignatur angezeigt. ], +ja: [ + 1つ目は、コレクションの容量を最初に宣言しなければならないことです。 + `heapless`コレクションは、再割り当てが発生せず、固定の容量になります。この容量はコレクションの型シグネチャの一部になります。 + 上記の例では、`xs`は8要素の容量を持つように宣言しています。このベクタは最大で8つの要素を保持することができます。 + 型シグネチャの`U8`(#ln_typenum;を参照)がこのことを表しています。 +], zh: [ 第一,你必须预先声明集合的容量。`heapless`集合从来不会发生重分配且具有固定的容量;这个容量是集合的类型签名的一部分。在这个例子里,我们已经声明了`xs`的容量为8个元素,也就是说,这个vector最多只能有八个元素。这是通过类型签名中的`U8` (看#ln_typenum)来指定的。 @@ -395,6 +459,12 @@ de: [ `alloc`-Datenstrukturen ihre Kapazität durch eine erneute Speicherzuweisung auf dem Heap an. ], +ja: [ + 2つ目は、`push`メソッドおよび他の多くのメソッドが`Result`を返すことです。 + `heapless`コレクションは固定の容量を持つため、コレクションに要素を挿入する全ての操作は、失敗する可能性があります。 + APIは、操作が成功したかどうかを示すための`Result`を返すことで、この問題に対処しています。 + 一方、`alloc`コレクションは、ヒープ上で再割り当てするため、容量を増やすことができます。 +], zh: [ 第二,`push`方法和另外一些方法返回的是一个`Result`。因为`heapless`集合有一个固定的容量,所以所有插入的操作都可能会失败。通过返回一个`Result`,API反应了这个问题,指出操作是否成功还是失败。相反,`alloc`集合自己将会在堆上重新分配去增加它的容量。 ])) @@ -414,6 +484,12 @@ de: [ es ist jedoch auch möglich, sie in einer `static`-Variablen oder sogar auf dem Heap (`Box>`) zu speichern. ], +ja: [ + v0.4.x以降、全ての`heapless`コレクションは、全ての要素をインラインで格納しています。 + つまり、 + `let x = heapless::Vec::new();`のような操作は、スタック上にコレクションを割り当てます。 + また、コレクションを`static`変数や、ヒープ上(`Box>`)にさえ、に割り当てることが可能です。 +], zh: [ 自v0.4.x版本起,所有的`heapless`集合将所有的元素内联地存储起来了。这意味着像是`let x = heapless::Vec::new()`这样的一个操作将会在栈上分配集合,但是它也能够在一个`static`变量上分配集合,或者甚至在堆上(`Box>`)。 ])) @@ -421,6 +497,7 @@ zh: [ == #tr(( en: [Trade-offs], de: [Abwägungen], + ja: [トレードオフ], zh: [取舍], )) @@ -434,6 +511,9 @@ de: [ alloziierten, verschiebbaren Sammlungen und Sammlungen mit fester Kapazität wählen. ], +ja: [ + ヒープに割り当てられる再配置可能なコレクションと固定容量のコレクションとを選定する時は、次のことに留意して下さい。 +], zh: [ 当在堆分配的可重定位的集合和固定容量的集合间进行选择的时候,记住这些内容。 ])) @@ -441,6 +521,7 @@ zh: [ === #tr(( en: [Out Of Memory and error handling], de: [„Out of Memory" und Fehlerbehandlung], + ja: [メモリ不足とエラー処理], zh: [内存溢出和错误处理], )) @@ -464,6 +545,13 @@ de: [ potenzielle OOM-Situationen beim Vergrößern der Collection überprüfen lassen; man muss diese Methoden jedoch aktiv einsetzen. ], +ja: [ + ヒープアロケーションでは、メモリ不足は常に発生する可能性があり、コレクションが拡大する場所であれば、どこでも発生する可能性があります。 + 例えば、全ての`alloc::Vec.push`呼び出しは、OOM状態を引き起こす可能性があります。 + そのため、一部の操作は_暗黙的に_失敗する可能性があります。 + 一部の`alloc`コレクションは`try_reserve`メソッドを提供しています。 + このメソッドにより、コレクションを拡大する時にOOM状態が発生するかどうかを確認できますが、先を見越して使用する必要があります。 +], zh: [ 使用堆分配,内存溢出总是有可能出现的且会发生在任何一个集合需要增长的地方: 比如,所有的 `alloc::Vec.push` 调用会潜在地产生一个OOM(Out of @@ -486,6 +574,11 @@ de: [ _alle_ `Result`-Werte behandeln, die von Methoden wie `Vec.push` zurückgegeben werden. ], +ja: [ + `heapless`コレクションだけを使っていて、メモリアロケータを使用しないのであれば、OOM状態は発生しません。 + その代わりに、コレクションの容量オーバーを個別に処理しなければなりません。 + つまり、`Vec.push`のようなメソッドが返す全ての`Result`を処理することになります。 +], zh: [ 如果你只使用`heapless`集合,而不使用内存分配器,那么一个OOM条件不可能出现。反而,你必须逐个处理容量不足的集合。也就是必须处理_所有_的`Result`,`Result`由像是`Vec.push`这样的方法返回的。 ])) @@ -508,6 +601,12 @@ de: [ Allocator nahezu erschöpft ist, weil eine andere Collection Speicher verloren hat (Speicherlecks sind auch in „Safe Rust" möglich). ], +ja: [ + OOM障害は、`heapless::Vec.push`が返す全ての`Result`を`unwrap`するより、デバッグが難しいでしょう。 + なぜなら、障害が発生した場所は、問題の原因となる場所と一致_しない_可能性があるからです。 + 例えば、他のコレクションがメモリリークを起こしているせいでアロケータが枯渇しそうな場合、`vec.reserve(1)`がOOMを発生させる可能性があります + (メモリリークは安全なRustでも発生します)。 +], zh: [ 与在所有由`heapless::Vec.push`返回的`Result`上调用`unwrap`相比,OOM错误更难调试,因为错误被发现的位置可能与导致问题的位置_不_一致。比如,甚至如果分配器接近消耗完`vec.reserve(1)`都能触发一个OOM,因为一些其它的集合正在泄露内存(内存泄露在安全的Rust是会发生的)。 ])) @@ -515,6 +614,7 @@ zh: [ === #tr(( en: [Memory usage], de: [Speichernutzung], + ja: [メモリ使用量], zh: [内存使用], )) @@ -542,6 +642,13 @@ de: [ Speicherfragmentierung umgehen, was den _scheinbaren_ Speicherverbrauch erhöhen kann. ], +ja: [ + 長期間使われるコレクションの容量は、実行時に変わる可能性があるため、ヒープ割り当てされたコレクションのメモリ使用量を推測することは難しいです。 + 一部の操作は、暗黙的にコレクションを再割り当てし、メモリ使用量が増加します。 + 一部のコレクションは、`shrink_to_fit`のようなメソッドを持っており、コレクションが使用しているメモリを減らすこともあります。 + 最終的に、実際にメモリアロケーションを縮小するかどうかは、アロケータ次第です。 + さらに、アロケータは、メモリフラグメンテーションを扱う必要があります。このことは_見かけ上の_メモリ使用量を増やす可能性があります。 +], zh: [ 推理堆分配集合的内存使用是很难的因为长期使用的集合的大小会在运行时改变。一些操作可能隐式地重分配集合,增加了它的内存使用,一些集合暴露的方法,像是`shrink_to_fit`,会潜在地减少集合使用的内存 -- 最终,它由分配器去决定是否确定减小内存的分配或者不。另外,分配器可能不得不处理内存碎片,它会_明显_增加内存的使用。 @@ -560,6 +667,10 @@ de: [ Größe für den Aufruf-Stack (Call Stack) fest, so erkennt der Linker, wenn mehr Speicher beansprucht wird, als physisch verfügbar ist. ], +ja: [ + 一方で、固定容量のコレクションだけを使用して、そのほとんどを`static`変数に格納し、コールスタックの最大サイズを設定すると、 + リンカは、物理的に利用可能なメモリより大きな容量を使おうとしたかどうか検出します。 +], zh: [ 另一方面,如果你只使用固定容量的集合,请把大多数的数据保存在`static`变量中,并为调用栈设置一个最大尺寸,随后如果你尝试使用大于可用的物理内存的内存大小时,链接器会发现它。 ])) @@ -579,6 +690,10 @@ de: [ erfasst; das bedeutet, dass Werkzeuge zur Analyse der Stack-Nutzung (wie #ln_stack_sizes) diese in ihre Auswertung einbeziehen. ], +ja: [ + その上、スタックに割り当てられた固定容量のコレクションは、#ln_z_emit;フラグによって報告されます。 + このフラグは、(#ln_stack_sizes;のような)スタック使用量を解析するツールがスタック使用量を解析することを意味します。 +], zh: [ 另外,在栈上分配的固定容量的集合可以通过#ln_z_emit;标识来报告,其意味着用来分析栈使用的工具(像是#ln_stack_sizes)将会把在栈上分配的集合包含进它们的分析中。 ])) @@ -595,6 +710,10 @@ de: [ der Größe der Sammlung und ihrer Kapazität) führen kann, als sie bei Sammlungen mit veränderbarer Kapazität möglich sind. ], +ja: [ + しかし、固定容量のコレクションは、縮小することが_できません_。 + 再配置可能なコレクションよりも負荷率(コレクションのサイズとその容量の比率)が低くなる可能性があります。 +], zh: [ 然而,固定容量的集合_不_能被减少,与可重定位集合所能达到的负载系数(集合的大小和它的容量之间的比值)相比,它能产生更低的负载系数。 ])) @@ -602,6 +721,7 @@ zh: [ === #tr(( en: [Worst Case Execution Time (WCET)], de: [Maximale Ausführungszeit (WCET)], + ja: [最悪実行時間(WCET; Worst Case Execution Time)], zh: [最坏执行时间 (WCET)], )) @@ -616,6 +736,10 @@ de: [ die Worst-Case-Ausführungszeit (WCET) der verschiedenen Programmteile wahrscheinlich sehr wichtig. ], +ja: [ + 時間制約のあるアプリケーションやハードリアルタイムアプリケーションを作成している場合、 + プログラムの様々な部分で最悪実行時間が気になるでしょう。 +], zh: [ 如果你正在搭建时间敏感型应用或者硬实时应用,那么你可能更关心你程序的不同部分的最坏执行时间。 ])) @@ -637,6 +761,13 @@ de: [ Operation `alloc::Vec.push` zu bestimmen, da sie sowohl vom verwendeten Allokator als auch von dessen Laufzeitkapazität abhängt. ], +ja: [ + `alloc`コレクションは再割り当てする可能性があるため、コレクションが拡大する操作の最悪実行時間は、 + コレクションが再割り当てされるのにかかる時間も含みます。 + コレクションが再割り当てされるかどうかは、_実行時_のコレクションの容量に依存します。 + このことは、`alloc::Vec.push`といった操作の最悪実行時間の決定を難しくします。 + この操作の最悪実行時間は、使用するアロケータとコレクションの実行時容量との両方に依存するためです。 +], zh: [ `alloc`集合能重分配,所以操作的WCET可能会增加,集合也将包括它用来重分配集合所需的时间,它取决于集合的_运行时_容量。这使得它更难去决定操作,比如`alloc::Vec.push`,的WCET,因为它依赖被使用的分配器和它的运行时容量。 ])) @@ -652,6 +783,11 @@ de: [ sodass alle Operationen eine vorhersehbare Ausführungszeit haben. Beispielsweise wird `heapless::Vec.push` in konstanter Zeit ausgeführt. ], +ja: [ + 一方、固定容量のコレクションは再割り当てが発生しないため、全ての操作の実行時間が予測可能です。 + 例えば、`heapless::Vec.push`は定数時間で実行します。 + +], zh: [ 另一方面固定容量的集合不会重分配,因此所有的操作有个可预期的执行时间。比如,`heapless::Vec.push`以固定时间执行。 ])) @@ -659,6 +795,7 @@ zh: [ === #tr(( en: [Ease of use], de: [Benutzerfreundlichkeit], + ja: [使いやすさ], zh: [易用性], )) @@ -673,6 +810,10 @@ de: [ `heapless` hingegen nicht. Allerdings erfordert `heapless`, dass man bei der Instanziierung jeder Collection deren Kapazität festlegt. ], +ja: [ + `alloc`はグローバルアロケータの準備が必要ですが、`heapless`はそうではありません。 + しかし、`heapless`は、インスタンスを作成する各コレクションの容量を指定する必要があります。 +], zh: [ `alloc`要求配置一个全局分配器而`heapless`不需要。然而,`heapless`要求你去选择你要实例化的每一个集合的容量。 ])) @@ -691,6 +832,12 @@ de: [ manche Entwickler empfinden diese explizite Fehlerbehandlung womöglich als übertrieben oder zu umständlich. ], +ja: [ + `alloc` APIは、事実上、全てのRust開発者がなじみのあるものです。 + `heapless` APIは、`alloc` + APIに似せてはいますが、明示的なエラー処理のため、全く同じにはなりません。 + 一部の開発者はこの明示的なエラー処理を、度が過ぎていたり、面倒すぎる、と感じるかもしれません。 +], zh: [ `alloc` API几乎为每一个Rust开发者所熟知。`heapless` API尝试模仿`alloc` API,但是因为`heapless`的显式错误处理,它们不可能会一模一样 -- diff --git a/typ/concurrency/index.typ b/typ/concurrency/index.typ index 82b3f18d..0fa7f17c 100644 --- a/typ/concurrency/index.typ +++ b/typ/concurrency/index.typ @@ -1,10 +1,10 @@ #import "../config.typ": * -#h1(tr(( - en: [Concurrency], +#h1((en: [Concurrency], de: [Nebenläufigkeit], + ja: [並行性], zh: [并发], -))) +)) #tr(( @@ -33,6 +33,13 @@ de: [ Kern gleichzeitig und unabhängig voneinander einen anderen Teil Ihres Programms ausführen kann. ], +ja: [ + プログラムの異なる部分が様々なタイミングで実行されたり、アウトオブオーダに実行されると、並行性が発生します。 + 組込みでは、次のものが該当します。 + - 割り込みが発生するたびに実行される割り込みハンドラ + - マイクロプロセッサがプログラムの一部を定期的にスワップする様々な形式のマルチスレッド + - システムによっては、各コアがプログラムの異なる部分を同時に独立して実行できるマルチコアマイクロプロセッサ +], zh: [ 当程序的不同部分有可能会在不同的时刻被执行或者不按顺序地被执行时,那并发就出现了。在一个嵌入式环境中,这包括: - 中断处理函数,一旦相关的中断发生时,中断处理函数就会运行, @@ -54,6 +61,11 @@ de: [ Glücklicherweise bietet Rust eine Reihe von Abstraktionen und Sicherheitsgarantien, die uns dabei helfen, korrekten Code zu schreiben. ], +ja: [ + 多くの組込みプログラムは割り込みを処理する必要があるため、早かれ遅かれ、並行性は発生します。 + 割り込みは、捉えにくく、難しいバグが数多く発生し得る場所でもあります。 + 幸運なことに、Rustは正しいコードを書く助けになる抽象化と安全性保証とを、いくつか提供しています。 +], zh: [ 因为许多嵌入式程序需要处理中断,因此并发迟早会出现,这也是许多微妙和困难的bugs会出现的地方。幸运地是,Rust提供了许多抽象和安全保障去帮助我们写正确的代码。 ])) @@ -61,6 +73,7 @@ zh: [ == #tr(( en: [No Concurrency], de: [Keine Nebenläufigkeit], + ja: [並行性なし], zh: [没有并发], )) @@ -81,6 +94,11 @@ de: [ Schleife Eingabewerte ein, führt Berechnungen oder Verarbeitungen durch und gibt Ergebnisse aus. ], +ja: [ + 組込みプログラムの最も簡単な並行性は、並行性がないことです。ソフトウェアは1つの動作し続けるメインループからなり、割り込みも発生しません。 + 時には、これが手元の問題の最適解かもしれません。 + 通常、ループは何か入力を受け付け、何らかの処理を行い、何かを出力します。 +], zh: [ 对于一个嵌入式程序来说最简单的并发是没有并发: 软件由一个保持运行的main循环组成,一点中断也没有。有时候这非常适合手边的问题! @@ -112,6 +130,10 @@ de: [ die Synchronisierung des Zugriffs auf Peripheriegeräte machen. Wenn ein solch einfacher Ansatz ausreicht, kann dies eine hervorragende Lösung sein. ], +ja: [ + 並行性がないため、プログラム間でのデータ共有や、ペリフェラルへのアクセス同期に悩む必要はありません。 + このような単純なアプローチに逃れることができるのであれば、素晴らしい解決策かもしれません。 +], zh: [ 因为这里没有并发,因此不需要担心程序不同部分间的共享数据或者同步对外设的访问。如果可以使用一个简单的方法来解决问题,这种方法是个不错的选择。 ])) @@ -119,6 +141,7 @@ zh: [ == #tr(( en: [Global Mutable Data], de: [Globale veränderliche Daten], + ja: [グローバルでミュータブルなデータ], zh: [全局可变数据], )) @@ -143,6 +166,13 @@ de: [ müssen, auf den sowohl der Interrupt-Handler als auch der Hauptprogrammcode zugreifen können. ], +ja: [ + 組込みでないRustと異なり、通常、ヒープ領域を作成し、そのデータへの参照を新しく作成したスレッドに渡す、というような贅沢はできません。 + 代わりに、割り込みハンドラはいつでも呼び出される可能性があり、使用する共有メモリにアクセスする方法を知っていなければなりません。 + 最も低いレベルでは、 _静的に割り当てられた_ + ミュータブルなメモリを持つ必要があることを意味します。 + このメモリは、割り込みハンドラとメインコードの両方が参照できます。 +], zh: [ 不像非嵌入式Rust,我们通常不会奢侈地在堆上分配数据,并将对该数据的引用传递到新创建的线程中。相反,我们的中断处理函数随时可能被调用,且必须知道如何访问我们正在使用的共享内存。从最底层看来,这意味着我们必须有 _静态分配的_ 可变的内存,中断处理函数和main代码都可以引用这块内存。 @@ -166,6 +196,11 @@ de: [ Vorgang durch einen Interrupt unterbrochen, der seinerseits ebenfalls auf diese Variable zugreift. ], +ja: [ + Rustでは、このような#ln_staticmut;変数への読み書きは、常にアンセーフです。 + 特別な注意を払わないと、レースコンディションを引き起こす可能性があります。 + つまり、その変数へのアクセスが、さらにその変数にアクセスする割り込みによって、中断されるということです。 +], zh: [ 在Rust中,#ln_staticmut;这样的变量读取或者写入总是unsafe的,因为不特别关注它们的话,可能会触发一个竞态条件,对变量的访问在中途就被一个也访问那个变量的中断打断了。 ])) @@ -182,6 +217,10 @@ de: [ die steigenden Flanken eines Eingangssignals innerhalb jedes Ein-Sekunden-Intervalls zählt (einen Frequenzzähler): ], +ja: [ + この動作によって、コード内に分かりにくいエラーが発生する可能性があります。 + 例えば、1秒毎に入力信号の立ち上がりエッジをカウントする組込みプログラム(周波数カウンタ)を考えてみましょう。 +], zh: [ 为了举例这种行为如何在代码中导致了微妙的错误,思考一个嵌入式程序,这个程序在每一秒的周期内计数一些输入信号的上升沿(一个频率计数器): ])) @@ -200,6 +239,7 @@ fn main() -> ! { en: "DANGER - Not actually safe! Could cause data races.", de: "GEFAHR – Nicht wirklich sicher! Koennte zu Datenwettlaeufen // fuehren.", + ja: "危険。実際に安全ではありません。データ競合を引き起こす可能性があります。", zh: "危险 - 实际不安全! 可能导致数据竞争。", )) + " unsafe { COUNTER += 1 }; @@ -243,6 +283,17 @@ de: [ Interrupt verloren -- und wir würden für diesen Zeitraum doppelt so viele Signalwechsel zählen. ], +ja: [ + 毎秒、タイマ割り込みはカウンタを0に戻します。同時に、メインループは信号を継続的に測定し、信号がローからハイに変わった時にカウンタをインクリメントします。 + `static mut`な`COUNTER`にアクセスするためには、`unsafe`を使う必要があります。 + これは、未定義動作を引き起こさないことを、コンパイラに約束するということです。 + レースコンディションがどこにあるかわかりますか? + `COUNTER`のインクリメントは、アトミックであることが保証されて + _いません_ 。 + 実際、ほとんどの組込みプラットフォームにおいて、この操作は、ロードし、インクリメントし、ストアする、という動作に分割されます。 + 割り込みがロードの後からストアの前に発生した場合、0に戻すリセットは、割り込みから復帰した後に無視されます。 + そして、その期間では、2倍の遷移をカウントすることになります。 +], zh: [ 每秒计时器中断会把计数器设置回0。这期间,main循环连续地测量信号,且当看到从低电平到高电平的变化时,增加计数器的值。因为它是`static mut`的,我们不得不使用`unsafe`去访问`COUNTER`,意思是我们向编译器保证我们的操作不会导致任何未定义的行为。你能发现竞态条件吗?`COUNTER`上的增加并不一定是原子的 - 事实上,在大多数嵌入式平台上,它将被分开成一个读取操作,然后是增加,然后是写回。如果中断在计数器被读取之后但是在被写回之前被激活,在中断返回后,重置回0的操作会被忽略掉 - 那期间,我们会算出两倍的转换次数。 @@ -251,6 +302,7 @@ zh: [ == #tr(( en: [Critical Sections], de: [Kritische Abschnitte], + ja: [クリティカルセクション], zh: [临界区(Critical Sections)], )) @@ -270,6 +322,13 @@ de: [ einbetten, stellen wir sicher, dass der Timer-Interrupt erst ausgelöst wird, nachdem wir das Inkrementieren von `COUNTER` abgeschlossen haben: ], +ja: [ + それでは、データ競合についてどうすれば良いのでしょうか。 + 単純な方法は、割り込みが無効なコンテキストである + _クリティカルセクション_ を使うことです。 + `main`中の`COUNTER`へのアクセスを、クリティカルセクションでラッピングします。 + そうすることで、`COUNTER`のインクリメントが完了するまで、タイマ割り込みが発生しないようにできます。 +], zh: [ 因此,关于数据竞争可以做些什么?一个简单的方法是使用 _临界区(critical sections)_ @@ -290,6 +349,7 @@ fn main() -> ! { en: "New critical section ensures synchronised access to COUNTER", de: "Ein neuer kritischer Abschnitt gewaehrleistet den // synchronisierten Zugriff auf COUNTER.", + ja: "新しいクリティカルセクションは、COUNTERへの同期アクセスを保証します", zh: "新的临界区确保对COUNTER的同步访问", )) + " cortex_m::interrupt::free(|_| { @@ -320,6 +380,10 @@ de: [ Deaktivieren von Interrupts, der Ausführung von Code und dem anschließenden erneuten Aktivieren der Interrupts. ], +ja: [ + この例では`cortex_m::interrupt::free`を使いました。他のプラットフォームでもクリティカルセクションのコードを実行するための、類似の方法があります。 + これは、割り込みを無効にして、コードを実行し、再び割り込みを有効にすることと同じです。 +], zh: [ 在这个例子里,我们使用 `cortex_m::interrupt::free`,但是其它平台将会有更简单的机制在一个临界区中执行代码。它们都有一样的逻辑,关闭中断,运行一些代码,然后重新使能中断。 @@ -339,6 +403,11 @@ de: [ betroffen sein, da wir den Wert nicht lesen. - Es wird ohnehin niemals vom `main`-Thread unterbrochen werden. ], +ja: [ + タイマ割り込み内クリティカルセクションを置く必要がないことに注意して下さい。これは次の2つの理由からです。 + - 読み込みをしないため、`COUNTER`に0を書くことは、競合の影響を受けません + - いずれにせよ、`main`スレッドによって割り込まれることはありえません +], zh: [ 注意,有两个理由,不需要把一个临界区放进计时器中断中: - 向`COUNTER`写入0不会被一个竞争影响,因为我们不需要读取它 @@ -356,6 +425,11 @@ de: [ die sich gegenseitig _unterbrechen_ könnten, müsste jeder von ihnen ebenfalls einen kritischen Abschnitt verwenden. ], +ja: [ + `COUNTER`がお互いに _プリエンプション_ + する複数の割り込みハンドラから共有される場合、 + それぞれにクリティカルセクションが必要になるでしょう。 +], zh: [ 如果`COUNTER`被多个可能相互 _抢占_ 的中断处理函数共享,那么每一个也需要一个临界区。 @@ -384,6 +458,10 @@ de: [ hängt vom jeweiligen System ab, doch im Allgemeinen sollten wir dies vermeiden. ], +ja: [ + クリティカルセクションは、当面の問題を解決しますが、慎重に検討しなければならない`unsafe`なコードをまだたくさん書いています。 + その結果、必要以上にクリティカルセクションを使用することになり、オーバーヘッドと割り込みレイテンシおよびジッタをもたらします。 +], zh: [ 这解决了我们眼前的问题,但是我们仍然要编写许多unsafe的代码,我们需要仔细推敲这些代码,有些我们可能不需要使用临界区。因为每个临界区暂时暂停了中断处理,就会带来一些相关的成本,一些额外的代码大小,更高的中断延迟和抖动(中断可能花费很长时间去处理,等待被处理的时间变化非常大)。这是否是个问题取决于你的系统,但是通常,我们想要避免它。 ])) @@ -404,6 +482,12 @@ de: [ zugreifen wie Ihr eigener Kern. Wenn Sie mehrere Rechenkerne nutzen, benötigen Sie daher leistungsfähigere Synchronisationsmechanismen. ], +ja: [ + 注目すべき点は、クリティカルセクションでは、割り込みが発生しないことが保証されますが、 + マルチコアシステムでは、排他性の保証は提供されないことです。 + 他のコアは、割り込みでなくても、とあるコアと同じメモリにアクセスできてしまいます。 + マルチコアを使う場合、より強力な同期プリミティブが必要になります。 +], zh: [ 值得注意的是,虽然一个临界区保障了不会发生中断,但是它在多核系统上不提供一个排他性保证(exclusivity guarantee)!其它核可能很开心地访问与你的核一样的内存区域,即使没有中断。如果你正在使用多核,你将需要更强的同步原语(synchronisation primitives)。 @@ -412,6 +496,7 @@ zh: [ == #tr(( en: [Atomic Access], de: [Atomarer Zugriff], + ja: [アトミックアクセス], zh: [原子访问], )) @@ -440,6 +525,13 @@ de: [ auftreten, wird der gesamte Vorgang automatisch erneut versucht. Diese atomaren Operationen sind auch in Mehrkernsystemen sicher. ], +ja: [ + プラットフォームによっては、アトミック命令が利用できます。アトミック命令は、リードモディファイライト操作の保証を提供します。 + 特にCortex-Mの場合、`thumbv6`(Cortex-M0)はアトミック命令を提供しませんが、`thumbv7`(Cortex-M3以上)は提供します。 + これらの命令は、全ての割り込みを無効化する手荒な方法の代替手段を提供します。 + インクリメントを試みる時、ほとんどの場合は成功しますが、割り込まれた場合はインクリメント操作全体を自動的にやり直します。 + このようなアトミック操作は、複数のコアにまたがっても安全です。 +], zh: [ 在一些平台上,可以使用特定的原子指令,它保障了读取-修改-写回操作。针对Cortex-M: `thumbv6`\(Cortex-M0,Cortex-M0+)只提供原子读取和存取指令,而`thumv7`\(Cortex-M3及以上)提供完整的比较和交换(CAS)指令。这些CAS指令可以替代过重的禁用所有中断的方法: @@ -461,6 +553,7 @@ fn main() -> ! { // " + ts(( en: "Use `fetch_add` to atomically add 1 to COUNTER", de: "Verwenden Sie `fetch_add`, um 1 atomar zu COUNTER zu addieren.", + ja: "自動的にCOUNTERに1を加えるために`fetch_add`を使います", zh: "使用 `fetch_add` 原子性地给 COUNTER 加一", )) + " COUNTER.fetch_add(1, Ordering::Relaxed); @@ -474,6 +567,7 @@ fn timer() { // " + ts(( en: "Use `store` to write 0 directly to COUNTER", de: "Verwenden Sie `store`, um 0 direkt in COUNTER zu schreiben.", + ja: "COUNTERに直接0を書くために`store`を使います", zh: "使用 `store` 将 0 直接写入 COUNTER", )) + " COUNTER.store(0, Ordering::Relaxed) @@ -495,6 +589,11 @@ de: [ Wenn möglich, ist dies die bessere Lösung -- wird aber möglicherweise von Ihrer Plattform nicht unterstützt. ], +ja: [ + ここで、`COUNTER`は安全な`static`変数です。`AtomicUsize`のおかげで`COUNTER`の型は、割り込みを無効化することなく、 + 割り込みハンドラとメインスレッドの両方から安全に修正できます。 + 可能であれば、これはより良い解決方法です。しかし、あなたのプラットフォームではサポートされていないかもしれません。 +], zh: [ 这时,`COUNTER`是一个safe的`static`变量。多亏了`AtomicUsize`类型,不需要禁用中断,`COUNTER`能从中断处理函数和main线程被安全地修改。当可以这么做时,这是一个更好的解决方案 - 然而平台上可能不支持这么做。 ])) @@ -523,6 +622,13 @@ de: [ möglicherweise nicht. Die genauen Details des atomaren Modells sind komplex und werden am besten an anderer Stelle beschrieben. ], +ja: [ + #link("https://doc.rust-lang.org/core/sync/atomic/enum.Ordering.html")[`Ordering`]の注釈:これは、コンパイラとハードウェアがどのように命令の順番を入れ替えるか、に影響を与えます。 + また、キャッシュの可視性にも影響します。ターゲットがシングルコアプラットフォームだと仮定すると、`Relaxed`で十分であり、このケースでは最も効率の良い選択です。 + より厳密なオーダリングでは、コンパイラはアトミック操作の前後にメモリバリアを発行します。 + 使用するアトミック操作によって、必要かもしれませんし、必要でないかもしれません! + アトミックモデルの正確な詳細は複雑であり、他の文書内でしっかりと説明されています。 +], zh: [ 关于#ln_ordering;的提醒: 它可能影响编译器和硬件如何重新排序指令,也会影响缓存可见性。假设目标是个单核平台,在这个案例里`Relaxed`是充足的和最有效的选择。更严格的排序将导致编译器在原子操作周围产生内存屏障(Memory @@ -539,6 +645,9 @@ de: [ Weitere Informationen zu atomaren Operationen und deren Reihenfolge finden Sie im #link(url_atomics)[nomicon]. ], +ja: [ + 詳細は、#link(url_atomics)[ノミコン]のアトミックとオーダリングを参照して下さい。 +], zh: [ 关于原子操作和排序的更多细节,可以看这里#link(url_atomics)[nomicon]。 ])) @@ -546,6 +655,7 @@ zh: [ == #tr(( en: [Abstractions, Send, and Sync], de: [Abstraktionen, Send und Sync], + ja: [抽象化、SendとSync], zh: [抽象,Send和Sync], )) @@ -560,6 +670,11 @@ de: [ erfordern unsichere Blöcke, die sehr sorgfältig geprüft werden müssen und nicht ergonomisch sind. In Rust geht das doch bestimmt besser! ], +ja: [ + 上記の解決方法のいずれも、これと言って満足いくものではありません。 + どの解決方法も`unsafe`ブロックを必要とし、非常に注意深くチェックしなければならず、人間工学的ではありません。 + Rustではもっとうまくやれるはずです! +], zh: [ 上面的解决方案都不是特别令人满意。它们需要`unsafe`块,`unsafe`块必须要被十分小心地检查且不符合人体工程学。确实,我们在Rust中可以做得更好! ])) @@ -577,6 +692,10 @@ de: [ verwenden wir den Zähler für kritische Abschnitte, aber mit atomaren Operationen ließe sich etwas sehr Ähnliches realisieren. ], +ja: [ + カウンタを、コード内のどこからでも安全に使えるインタフェースに抽象化することができます。 + 次の例では、クリティカルセクションカウンタを使いますが、アトミックと非常に良く似たことが実現できます。 +], zh: [ 我们可以把我们的计数器抽象进一个安全的接口中,它可以在代码的其它地方被安全地使用。在这个例子里,我们将使用临界区的(cirtical-section)计数器,但是你可以用原子操作做一些非常类似的事情。 ])) @@ -594,6 +713,9 @@ use cortex_m::interrupt; // der „Interior Mutability“ in Rust. Dank dieses Konzepts können wir `COUNTER` // als `static` statt als `static mut` definieren und dennoch den Zaehlerwert // veraendern.", + ja: "カウンタはUnsafeCellの単なるラッパです。UnsafeCellはRustの内部可変性の重要要素です。 +// 内部可変性を使用することで、COUNTERを`static mut`の代わりに`static`として持つことができます。 +// しかし、依然として、カウンタの値は変更することができます。", zh: "我们的计数器只是包围UnsafeCell的一个封装,它是Rust中内部可变性 // (interior mutability)的关键。通过使用内部可变性,我们能让COUNTER // 变成`static`而不是`static mut`,但是仍能改变它的计数器值。" @@ -612,6 +734,9 @@ impl CSCounter { // wir, dass wir uns innerhalb einer `CriticalSection` befinden; daher // koennen wir bedenkenlos diesen `unsafe`-Block verwenden (der für den // Aufruf von `UnsafeCell::get` erforderlich ist).", + ja: "クリティカルセクションを引数として要求することで、クリティカルセクション内で + // 実行されなければならないことがわかります。そのため、このアンセーフブロックを + // 自信を持って使用できます(UnsafeCell::getの呼び出しに必要です)。", zh: "通过要求一个CriticalSection被传递进来,我们知道我们肯定正在一个 // CriticalSection中操作,且因此可以自信地使用这个unsafe块(调用UnsafeCell::get的前提)。", )) + " @@ -627,6 +752,7 @@ impl CSCounter { en: "Required to allow static CSCounter. See explanation below.", de: "Erforderlich, um ein statisches CSCounter zu ermoeglichen. Siehe Erlaeuterung // unten.", + ja: "静的なCSCounterを許可するために必要です。以下の説明を参照して下さい。", zh: "允许静态CSCounter的前提。看下面的解释。" )) + " unsafe impl Sync for CSCounter {} @@ -636,6 +762,8 @@ unsafe impl Sync for CSCounter {} // therefore it also no longer requires unsafe blocks to access.", de: "COUNTER ist nicht mehr `mut`, da es „Interior Mutability“ verwendet; daher // sind fuer den Zugriff auch keine `unsafe`-Bloecke mehr erforderlich.", + ja: "内部可変性を使用するため、COUNTERは、もはや`mut`ではありません。 +// 従って、アクセスの際に、アンセーフなブロックも必要なくなりました。", zh: "COUNTER不再是`mut`的因为它使用内部可变性; // 因此访问它也不再需要unsafe块。" )) + " @@ -650,6 +778,7 @@ fn main() -> ! { if state && !last_state { // " + ts(( en: "No unsafe here!", + ja: "アンセーフはここでは必要ありません!", zh: "这里不用unsafe!", )) + " interrupt::free(|cs| COUNTER.increment(cs)); @@ -667,6 +796,8 @@ fn timer() { de: "Wir muessen hier tatsaechlich einen kritischen Abschnitt betreten, nur um // ein gueltiges CS-Token zu erhalten, auch wenn wir wissen, dass kein // anderer Interrupt diesen unterbrechen koennte.", + ja: "有効なcsトークンを得るため、ここでクリティカルセクションに入る必要があります。 + // 他の割り込みがプリエンプションを起こさないと分かっていても必要です。", zh: "这里我们需要进入一个临界区,只是为了传递进一个有效的cs token,尽管我们知道 // 没有其它中断可以抢占这个中断。" )) + " @@ -678,6 +809,8 @@ fn timer() { de: "Wir koennten „unsicheren“ Code (unsafe code) verwenden, um eine // gefaelschte CriticalSection zu erzeugen, falls wir das wirklich wollten, // und so den Overhead vermeiden:", + ja: "オーバーヘッドを避けるために、本当に必要であれば、偽のクリティカルセクションを生成する + // アンセーフなコードを使うことができます。", zh: "如果我们真的需要,我们可以使用unsafe代码去生成一个假CriticalSection, // 避免开销:" )) + " @@ -696,6 +829,10 @@ de: [ Abstraktion verlagert; nun enthält unser Anwendungscode keine `unsafe`-Blöcke mehr. ], +ja: [ + `unsafe`コードを慎重に検討された抽象の内側に移動しました。 + そして、アプリケーションコードは、`unsafe`ブロックを含んでいません。 +], zh: [ 我们已经把我们的`unsafe`代码移进了精心安排的抽象中,现在我们的应用代码不包含任何`unsafe`块。 ])) @@ -722,6 +859,13 @@ de: [ Zähler, könnten diese alle dasselbe `cs` verwenden, ohne dass mehrere verschachtelte kritische Abschnitte erforderlich wären. ], +ja: [ + この設計は、アプリケーションが`CriticalSection`トークンを渡すことを要求します。 + トークンは、`interrupt::free`によってのみ、安全に生成することができます。 + そのため、このトークンが渡されることを要求することで、自分自身でロックを実際にかけることなしに、クリティカルセクション内で動作していることを保証します。 + この保証は、静的にコンパイラによって提供されます。`cs`による実行時のオーバーヘッドはありません。 + カウンタが複数ある場合、複数の入れ子になったクリティカルセクションなしに、同じ`cs`を与えることができます。 +], zh: [ 这个设计要求应用传递一个`CriticalSection` token进来: 这些tokens仅由`interrupt::free`安全地产生,因此通过要求传递进一个`CriticalSection` @@ -752,6 +896,14 @@ de: [ Variablen, auf die sowohl ein Interrupt als auch der Hauptcode zugreift, `Sync` implementieren. ], +ja: [ + これは、Rustの並行性についても重要なトピックを提起します。#link(url_sendsync)[`Send`と`Sync`]トレイトです。 + the Rust + bookを要約すると、安全に別のスレッドに移動できるとき、型はSendです。 + 一方、複数のスレッド間で安全に共有できるとき、型はSyncです。 + 組込みでは、割り込みがアプリケーションコードとは異なるスレッドで動作すると考えます。 + そのため、割り込みとメインコードとの両方からアクセスされる変数は、Syncでなければなりません。 +], zh: [ 这也带来了Rust中关于并发的一个重要主题: #link(url_sendsync)[`Send` and `Sync`] @@ -775,6 +927,11 @@ de: [ `static CSCounter` definieren, denn `static`-Variablen _müssen_ `Sync` sein, da von mehreren Threads aus auf sie zugegriffen werden kann. ], +ja: [ + Rustのほとんどの型では、コンパイラによってSendとSyncの両方のトレイトが自動的に継承されます。 + しかし、`CSCounter`は#ln_unsafecell;を含んでいるため、Syncではありません。 + 従って、`static CSCounter`を作ることはできません。`static`変数は、複数のスレッドからアクセスされるため、Syncでなければなりません。 +], zh: [ 在Rust中的大多数类型,这两个traits都会由你的编译器为你自动地产生。然而,因为`CSCounter`包含了一个#ln_unsafecell,它不是Sync,因此我们不能使用一个`static CSCounter`: `static` 变量 _必须_ 是Sync,因此它们能被多个线程访问。 @@ -796,6 +953,11 @@ de: [ Single-Core-Plattformen sicher; bei mehreren Kernen wäre ein deutlich höherer Aufwand erforderlich, um die Sicherheit zu gewährleisten. ], +ja: [ + `CSCounter`が実はスレッド間で共有しても安全なように処理していることを、コンパイラに伝えるため、Syncトレイトを明示的に実装します。 + これまでのクリティカルセクションの使用と同様に、シングルコアのプラットフォームでのみ安全です。 + マルチコアのプラットフォームでは、安全性を確保するためにさらなる取り組みが必要です。 +], zh: [ 为了告诉编译器我们已经注意到`CSCounter`事实上在线程间共享是安全的,我们显式地实现了Sync trait。与之前使用的临界区一样,这只在单核平台上是安全的: @@ -805,6 +967,7 @@ zh: [ == #tr(( en: [Mutexes], de: [Mutexe], + ja: [ミューテックス], zh: [互斥量(Mutexs)], )) @@ -818,6 +981,9 @@ de: [ zugeschnittene Abstraktion entwickelt; es gibt jedoch viele gängige Abstraktionen für Nebenläufigkeit. ], +ja: [ + カウンタの問題に特有の便利な抽象化を行いましたが、並行性のために利用されるいくつかの抽象化が存在します。 +], zh: [ 我们已经为我们的计数器问题创造了一个有用的抽象,但是关于并发这里还存在许多通用的抽象。 ])) @@ -852,6 +1018,20 @@ de: [ implementieren; so ist sichergestellt, dass der Mutex immer freigegeben wird, sobald er den Gültigkeitsbereich (Scope) verlässt. ], +ja: [ + そのような _同期プリミティブ_ + の1つはミューテックス(mutex)です。mutexはmutual exclusionの略です。 + ミューテックスは、私達のカウンタのような変数への排他アクセスを保証します。 + あるスレッドは、ミューテックスの _ロック_(または + _獲得_)を試みます。 + すると、すぐに成功するか、ロックが獲得されるのを待ってブロックするか、ミューテックスをロックできなかったエラーを返します。 + そのスレッドがロックを保持している間、保護されたデータへのアクセスが許可されます。 + そのスレッドが実行を完了すると、ミューテックスを + _アンロック_(または + _解放_)することで、他のスレッドがミューテックスをロックできるようにします。 + Rustでは、通常、アンロックを実装するために#ln_drop;トレイトを使用します。 + これは、ミューテックスがスコープの外に到達すると、常に解放されることを保証するためです。 +], zh: [ 一个互斥量(mutex),互斥(mutual exclusion)的缩写,就是这样的一个 _同步原语_ @@ -883,6 +1063,13 @@ de: [ Deadlock gilt nicht als „unsicher" (unsafe): Er ist selbst in sicherem Rust möglich. ], +ja: [ + 割り込みハンドラでミューテックスを使用するのはトリッキーです。割り込みハンドラ内でブロックすることは、通常、好ましくありません。 + 割り込みハンドラ内で、メインスレッドがロックを解放するのを待ってブロックすると、特に悲惨です。 + なぜならば。_デッドロック_ + になるからです。(割り込みハンドラ内に実行がとどまるため、メインスレッドがロックを解放することは決してありません) + デッドロックはアンセーフとは考えられていません。安全なRustでも発生する可能性があります。 +], zh: [ 将中断处理函数与一个互斥量一起使用可能有点棘手: 阻塞中断处理函数通常是不可接受的,如果它阻塞等待main线程去释放一个锁,那将是一场灾难。因为我们会 @@ -907,6 +1094,11 @@ de: [ exklusiven Zugriff auf die gekapselte Variable haben, ohne den Sperrstatus des Mutex explizit nachverfolgen zu müssen. ], +ja: [ + この動作を完全に避けるため、カウンタの例で示すように、ロックのためにクリティカルセクションを必要とするミューテックスを実装できます。 + クリティカルセクションがロックしている間続く限り、ミューテックスのロック/アンロックの状態を追跡することなしに、 + ラップされた変数に排他的にアクセスできます。 +], zh: [ 为了完全避免这个行为,我们可以实现一个要求临界区的互斥量去锁住,就像我们的计数器例子一样。临界区的存在时间必须和锁存在的时间一样长,我们能确保我们对被封装的变量有排他式访问,甚至不需要跟踪互斥量的 lock/unlock 状态。 @@ -921,9 +1113,11 @@ de: [ Genau dies wird uns bereits durch die `cortex_m`-Crate abgenommen! Wir hätten unseren Zähler auch unter Verwendung dieser Crate implementieren können: ], +ja: [ + これは実際に`cortex_m`クレートで行われています! それを使ってカウンタを書くことができます。 +], zh: [ - 实际上我们在 `cortex_m` - crate中就是这么做的!我们可以用它来写入我们的计数器: + 实际上我们在 `cortex_m` crate中就是这么做的!我们可以用它来写入我们的计数器: ])) #raw(block: true, lang: "rust", @@ -952,6 +1146,7 @@ fn timer() { en: "We still need to enter a critical section here to satisfy the Mutex.", de: "Wir muessen hier noch einen kritischen Abschnitt betreten, um die // Mutex-Bedingung zu erfuellen.", + ja: "ミューテックスを満たすために、ここでもクリティカルセクションに入る必要があります。", zh: "这里我们仍然需要进入一个临界区去满足互斥量。", )) + " interrupt::free(|cs| COUNTER.borrow(cs).set(0)); @@ -988,8 +1183,17 @@ de: [ `static`-Variablen einsetzen, da `static`-Elemente die Eigenschaft `Sync` erfüllen müssen. ], +ja: [ + 今回は#ln_cell;を使っています。これは、`RefCell`の同類で安全な内部可変性を提供するために使用されます。 + 既に、`UnsafeCell`が、Rustの内部可変性の最下層であることを見てきました。 + UnsafeCellは、値への複数のミュータブル参照の取得を可能としますが、アンセーフなコードでのみ使用できます。 + `Cell`は`UnsafeCell`と似ていますが、安全なインタフェースを提供します。 + Cellは参照を取得せず、現在値のコピーを取得するか、置き換えることだけを許可します。 + CellはSyncでないため、スレッド間で共有できません。 + これらの制約は安全に使えることを意味しますが、`static`変数として直接使用できません。`static`はSyncである必要があるからです。 +], zh: [ - 我们现在使用了#link("https://doc.rust-lang.org/core/cell/struct.Cell.html")[`Cell`],它与它的兄弟`RefCell`一起被用于提供safe的内部可变性。我们已经见过`UnsafeCell`了,在Rust中它是内部可变性的底层: + 我们现在使用了#ln_cell,它与它的兄弟`RefCell`一起被用于提供safe的内部可变性。我们已经见过`UnsafeCell`了,在Rust中它是内部可变性的底层: 它允许你去获得对某个值的多个可变引用,但是只能与不安全的代码一起工作。一个`Cell`像一个`UnsafeCell`一样但是它提供了一个安全的接口: 它只允许拷贝现在的值或者替换它,不允许获取一个引用,因此它不是Sync,它不能被在线程间共享。这些限制意味着它用起来是safe的,但是我们不能直接将它用于`static`变量因为一个`static`必须是Sync。 ])) @@ -1008,6 +1212,11 @@ de: [ kritischen Abschnitts gewährt wird. Dadurch erhalten wir einen sicheren Zähler ohne jeglichen unsicheren Code! ], +ja: [ + では、なぜ上記の例はうまく動くのでしょうか?`Mutex`は、`Cell`のようなSendな`T`に対してSyncを実装します。 + このことが、Cellをstaticで使うことを安全にします。なぜなら、クリティカルセクションの間だけ、その中身へのアクセスを提供するからです。 + 従って、全くアンセーフなコードなしに、安全なカウンタを手に入れることができます。 +], zh: [ 因此为什么上面的例子可以工作?`Mutex`对于任何是Send的`T`实现了Sync - 比如一个`Cell`。因为它只能在临界区对它的内容进行访问,所以它这么做是safe的。因此我们可以即使没有一点unsafe的代码我们也能获取一个safe的计数器! @@ -1026,6 +1235,11 @@ de: [ Beispiel in eingebetteten Systemen ist eine Peripheriestruktur, die in der Regel nicht `Copy` ist. Für diesen Fall können wir `RefCell` verwenden. ], +ja: [ + この方法は、カウンタの`u32`のような単純な型に適しています。しかし、もっと複雑なCopyでない型についてはどうでしょうか? + 組込みにおいて非常に一般的な例は、ペリフェラル構造体です。これは、通常Copyではありません。 + そのためには、`RefCell`に頼ることができます。 +], zh: [ 对于我们的简单类型,像是我们的计数器的`u32`来说是很棒的,但是对于更复杂的不能拷贝的类型呢?在一个嵌入式上下文中一个极度常见的例子是一个外设结构体,通常它们不是Copy。针对那种情况,我们可以使用`RefCell`。 ])) @@ -1033,6 +1247,7 @@ zh: [ == #tr(( en: [Sharing Peripherals], de: [Gemeinsame Nutzung von Peripheriegeräten], + ja: [ペリフェラルの共有], zh: [共享外设], )) @@ -1053,6 +1268,11 @@ de: [ Peripheriekomponente sowohl aus dem Haupt-Thread als auch aus einem Interrupt-Handler heraus. ], +ja: [ + `svd2rust`および同様の抽象化を使って生成されるデバイスクレートは、ペリフェラルへの安全なアクセスを提供します。 + これは、同時に1つのペリフェラル構造体インスタンスしか存在できないように強制することによって、もたらされます。 + このことは、安全性を保証しますが、メインスレッドと割り込みハンドとの両方からペリフェラルにアクセスすることを難しくします。 +], zh: [ 使用`svd2rust`生成的设备crates和相似的抽象,通过强制要求同时只能存在一个外设结构体的实例,提供了对外设的安全的访问。这个确保了安全性,但是使得它很难从main线程和一个中断处理函数一起访问一个外设。 ])) @@ -1078,6 +1298,12 @@ de: [ müssen wir sicherstellen, dass zu jedem Zeitpunkt nur eine einzige Referenz existiert. ], +ja: [ + ペリフェラルアクセスを安全に共有するため、上で見たように`Mutex`を使うことができます。 + また、#ln_refcell;も必要です。RefCellは、実行時チェックを使って、同時に1つのペリフェラルへの参照だけが渡されるようにします。 + 実行時チェックは、普通の`Cell`よりもオーバーヘッドが大きくなりますが、 + コピーではなく参照を受け渡しするため、同時に存在するのが1つだけであることを確認する必要があります。 +], zh: [ 为了安全地共享对外设的访问,我们能使用我们之前看到的`Mutex`。我们也将需要使用#ln_refcell,它使用一个运行时检查去确保对一个外设每次只有一个引用被给出。这个比纯`Cell`消耗更多,但是因为我们正给出引用而不是拷贝,我们必须确保每次只有一个引用存在。 ])) @@ -1096,6 +1322,10 @@ de: [ können wir den Typ `Option` verwenden, der zunächst mit `None` initialisiert und später auf die Instanz der Peripheriekomponente gesetzt wird. ], +ja: [ + 最後に、メインコード内でペリフェラルを初期化した後、なんとかしてペリフェラルを共有変数に移動する方法が必要です。 + これを実現するために、`Option`型を使います。`None`で初期化し、後でペリフェラルのインスタンスを設定します。 +], zh: [ 最终,我们也必须考虑在main代码中初始化外设后,如何将外设移到共享变量中。为了做这个,我们使用`Option`类型,初始成`None`,之后设置成外设的实例。 ])) @@ -1117,6 +1347,9 @@ fn main() -> ! { de: "Die Peripherie-Singletons abrufen und konfigurieren. // Dieses Beispiel stammt aus einer mit svd2rust erstellten Crate, die // meisten Crates für eingebettete Geräte sind jedoch ähnlich.", + ja: "ペリフェラルのシングルトンを取得し、設定します。 + // この例は、svd2rustで生成されたクレートから持ってきたものですが、 + // ほとんどの組込みデバイスクレートは同様になります。", zh: "获得外设的单例并配置它。这个例子来自一个svd2rust生成的crate, // 但是大多数的嵌入式设备crates都相似。", )) + " @@ -1129,6 +1362,8 @@ fn main() -> ! { de: "Eine Art Konfigurationsfunktion. // Gehen wir davon aus, dass es PA0 als Eingang und PA1 als Ausgang // konfiguriert.", + ja: "一連の設定をする関数です。 + // PA0を入力、PA1を出力に設定すると仮定して下さい。", zh: "某个配置函数。假设它把PA0设置成一个输入和把PA1设置成一个输出。", )) + " configure_gpio(gpioa); @@ -1136,6 +1371,7 @@ fn main() -> ! { // " + ts(( en: "Store the GPIOA in the mutex, moving it.", de: "Speichere GPIOA im Mutex und verschiebe es dabei.", + ja: "GPIOAをミューテックスに格納し、ムーブします。", zh: "把GPIOA存进互斥量中,移动它。", )) + " interrupt::free(|cs| MY_GPIO.borrow(cs).replace(Some(dp.GPIOA))); @@ -1154,6 +1390,12 @@ fn main() -> ! { // Andernfalls koennte der Interrupt ausgeloest werden, solange MY_GPIO // noch None enthaelt, was – wie implementiert (mit `unwrap()`) – zu einer // Panic fuehren wuerde.", + ja: "もはや`gpioa`や`dp.GPIOA`は使いません。 + // 代わりに、ミューテックス経由でアクセスする必要があります。 + + // MY_GPIOを設定した後にのみ、割り込みを有効にするように注意して下さい。 + // そうしなければ、まだNoneが含まれている間に、割り込みが発生する可能性があります。 + // (`unwrap()`を使用して)書き込まれると、パニックになるでしょう。", zh: "我可以不再用`gpioa`或者`dp.GPIOA`,反而必须通过互斥量访问它。 // 请注意,只有在设置MY_GPIO后才能使能中断: 要不然当MY_GPIO还是包含None的时候, @@ -1165,6 +1407,7 @@ fn main() -> ! { // " + ts(( en: "We'll now read state as a digital input, via the mutex", de: "Wir lesen den Status nun als digitalen Eingang ueber den Mutex aus.", + ja: "ミューテックス経由で、デジタル入力としての状態を読み込みます。", zh: "我们现在将通过互斥量,读取其作为数字输入时的状态。", )) + " let state = interrupt::free(|cs| { @@ -1177,6 +1420,7 @@ fn main() -> ! { en: "Set PA1 high if we've seen a rising edge on PA0.", de: "Setze PA1 auf High, wenn an PA0 eine steigende Flanke erkannt // wurde.", + ja: "PA0の立ち上がりエッジを検出した場合、PA1をハイに設定します。", zh: "如果我们在PA0上已经看到了一个上升沿,拉高PA1。" )) + " interrupt::free(|cs| { @@ -1193,6 +1437,7 @@ fn timer() { // " + ts(( en: "This time in the interrupt we'll just clear PA0.", de: "Diesmal setzen wir im Interrupt lediglich PA0 zurueck.", + ja: "今回は、割り込み内では単純にPA0をクリアするだけです。", zh: "这次在中断中,我们将清除PA0。", )) + " interrupt::free(|cs| { @@ -1203,6 +1448,8 @@ fn timer() { de: "Wir koennen `unwrap()` verwenden, da wir wissen, dass der Interrupt // erst aktiviert wurde, nachdem `MY_GPIO` gesetzt war; andernfalls // muessten wir den moeglichen Fall eines `None`-Werts behandeln.", + ja: "`unwrap()`を使うことができます。割り込みはMY_GPIOが設定されるまで有効化されないことを + // 知っているためです。そうでなければ、Noneを処理しなければならないでしょう。", zh: "我们可以使用`unwrap()` 因为我们知道直到MY_GPIO被设置后,中断都是禁用的; // 否则我应该处理会出现一个None值的潜在可能" )) + " @@ -1220,6 +1467,9 @@ de: [ Das ist eine ganze Menge, die man erst einmal verarbeiten muss -- schauen wir uns also die wichtigen Zeilen genauer an. ], +ja: [ + 非常に多くのことを取り入れています。重要な部分を詳細に見ていきましょう。 +], zh: [ 这需要理解的内容很多,所以让我们把重要的内容分解一下。 ])) @@ -1253,6 +1503,14 @@ de: [ sondern nur zur Laufzeit auf das Peripherie-Singleton zugreifen können, ist dieses Vorgehen erforderlich. ], +ja: [ + ここでは、共有変数は`RefCell`を内部に含む`Mutex`です。さらに、RefCellは`Option`を含んでいます。 + `Mutex`はクリティカルセクションの間だけ、アクセスできるようにします。 + その結果、普通の`RefCell`はSyncでないにも関わらず、変数はSyncになります。 + `RefCell`は、`GPIOA`を使うのに必要となる参照によって内部可変性を提供します。 + `Option`を使用すると、この変数を空の値に初期化できます。後で実際に変数を移動します。 + ペリフェラルのシングルトンには静的にアクセスすることはできません。実行時のみアクセスできるため、Optionが必要とされます。 +], zh: [ 我们的共享变量现在是一个包围了一个`RefCell`的`Mutex`,`RefCell`包含一个`Option`。`Mutex`确保只在一个临界区中的时候可以访问,因此使变量变成了Sync,甚至即使一个纯`RefCell`不是Sync。`RefCell`赋予了我们引用的内部可变性,我们将需要使用我们的`GPIOA`。`Option`让我们可以初始化这个变量成空的东西,只在随后实际移动变量进来。只有在运行时,我们才能静态地访问外设单例,因此这是必须的。 @@ -1274,6 +1532,10 @@ de: [ Anschließend rufen wir `replace()` auf, um unseren neuen Wert in die `RefCell` zu verschieben. ], +ja: [ + クリティカルセクションの内部で、ミューテックスの`borrow()`を呼んでいます。borrow()は`RefCell`の参照を提供します。 + その後、`replace()`を呼び出して、`RefCell`に新しい値をムーブします。 +], zh: [ 在一个临界区中,我们可以在互斥量上调用`borrow()`,其给了我们一个指向`RefCell`的引用。然后我们调用`replace()`去移动我们的新值进来`RefCell`。 ])) @@ -1302,6 +1564,12 @@ de: [ der Status der `RefCell` aktualisiert, um anzuzeigen, dass sie nicht mehr ausgeliehen ist. ], +ja: [ + 最後に、`MY_GPIO`を安全で並行なやり方で使います。 + クリティカルセクションは、通常通り割り込みの発生を防ぎ、ミューテックスを借用できます。 + `RefCell`は`&Option`を提供し、その借用がいつまで続くかを追跡します。 + その参照がスコープ外になると、`RefCell`が借用されなくなったことを示すため、更新されます。 +], zh: [ 最终,我们用一种安全和并发的方式使用`MY_GPIO`。临界区禁止了中断像往常一样发生,让我们借用互斥量。`RefCell`然后给了我们一个`&Option`并追踪它还要借用多久 - 一旦引用超出作用域,`RefCell`将会被更新去指出引用不再被借用。 ])) @@ -1319,6 +1587,11 @@ de: [ `&GPIOA` zu erhalten, das uns den Zugriff zur Modifikation der Peripherieeinheit ermöglicht. ], +ja: [ + `&Option`の外に`GPIOA`をムーブすることはできないので、`as_ref()`を使って`&Option<&GPIOA>`に変換します。 + そうすると、最終的に、`unwrap()`で`&GPIOA`を取得できます。 + &GPIOAにより、ペリフェラルを修正することができます。 +], zh: [ 因为我不能把`GPIOA`移出`&Option`,我们需要用`as_ref()`将它转换成一个`&Option<&GPIOA>`,最终我们能使用`unwrap()`获得`&GPIOA`,其让我们可以修改外设。 ])) @@ -1394,6 +1667,9 @@ de: [ Puh! Das ist zwar sicher, aber auch etwas unhandlich. Können wir sonst noch etwas tun? ], +ja: [ + ヒューッ!これは安全ですが、少し大げさすぎて扱いにくいです。他に方法はないのでしょうか? +], zh: [ 呼!这是安全的,但也有点笨拙。我们还能做些什么吗? ])) @@ -1424,6 +1700,14 @@ de: [ etwa die Garantie von Deadlock-Freiheit sowie einen extrem geringen Zeit- und Speicheraufwand. ], +ja: [ + 代替手段の1つは、#link(url_rtic)[RTFMフレームワーク]です。RTFMは、Real + Time For the Massesの略です。 + RTFMは、共有リソースが常に安全にアクセスされることを保証するために、静的な優先度を適用し、 + `static mut`変数(「リソース」)へのアクセスを追跡します。 + この方法は、(`RefCell`のように)常にクリティカルセクションに入り、参照カウントを使うというオーバーヘッドを必要としません。 + デッドロックがないことを保証したり、時間とメモリのオーバーヘッドを極めて小さくするといった、多くの利点があります。 +], zh: [ 另一个方法是使用#link(url_rtic)[RTIC框架],Real Time Interrupt-driven @@ -1459,6 +1743,11 @@ de: [ lassen. Weitere Informationen finden Sie in #link(url_rtic_doc)[der Dokumentation]! ], +ja: [ + このフレームワークは他の機能も含んでいます。例えば、メッセージパッシングは明示的な共有状態の必要性を減らします。 + また、タスクを指定した時間に実行するスケジュールする機能もあります。これは、周期タスクの実装に使えます。 + 詳しくは#link(url_rtic_doc)[ドキュメント]を参照して下さい。 +], zh: [ 这个框架也包括了其它的特性,像是消息传递(message passing),消息传递减少了对显式共享状态的需要,还提供了在一个给定时间调度任务去运行的功能,这功能能被用来实现周期性的任务。看下#link(url_rtic_doc)[文档]可以知道更多的信息! @@ -1516,6 +1805,7 @@ de: [ == #tr(( en: [Real Time Operating Systems], de: [Echtzeitbetriebssysteme], + ja: [リアルタイムオペレーティングシステム], zh: [实时操作系统], )) @@ -1547,6 +1837,16 @@ de: [ Synchronisationsprimitive bereit und interagieren häufig mit Hardwarefunktionen wie DMA-Controllern. ], +ja: [ + #todoupd("ja") + 組込み向け並行性の異なる一般的なモデルとして、リアルタイムオペレーティングシステム(RTOS)があります。 + 現在、Rustではあまり検証されていませんが、従来の組込み開発では広く使用されています。 + オープンソースの例として、#ln_freertos;と#ln_chibi;があります。 + これらのRTOSは、複数のアプリケーションスレッドを動作させる機能を提供しています。 + スレッドが制御を明け渡す時(コオペレーティブマルチタスク)か、 + 周期タイマまたは割り込みに基づく時(プリエンプティブマルチタスク)に、CPUで実行するスレッドを切り替えます。 + RTOSは、通常ミューテックスや他の同期プリミティブを提供します。また、DMAエンジンようなハードウェア機能を同時に使えることも多いです。 +], zh: [ #todoupd("zh") 与嵌入式并发有关的另一个模型是实时操作系统(RTOS)。虽然现在在Rust中的研究较少,但是它们被广泛用于传统的嵌入式开发。开源的例子包括#ln_freertos;和#ln_chibi。这些RTOSs提供对运行多个应用线程的支持,CPU在这些线程间进行切换,切换要么发生在当线程让出控制权的时候(被称为非抢占式多任务),要么是基于一个常规计时器或者中断(抢占式多任务)。RTOS通常提供互斥量或者其它的同步原语,经常与硬件功能相互使用,比如DMA引擎。 @@ -1562,14 +1862,18 @@ de: [ auf die man verweisen könnte; es handelt sich jedoch um ein interessantes Gebiet -- bleiben Sie also dran! ], +ja: [ + この本を書いている時点では、Rustで書かれたRTOSの例はそれほど多くありません。 + しかし、興味深い分野ですので、この分野にご注目下さい! +], zh: [ - 在撰写本文时,没有太多的Rust - RTOS示例可供参考,但这是一个有趣的领域,所以请关注这块! + 在撰写本文时,没有太多的Rust RTOS示例可供参考,但这是一个有趣的领域,所以请关注这块! ])) == #tr(( en: [Multiple Cores], de: [Mehrere Kerne], + ja: [マルチコア], zh: [多个核心], )) @@ -1593,6 +1897,14 @@ de: [ wir Synchronisierungsprimitive, die speziell für Mehrkernsysteme (auch SMP, für symmetrisches Multiprocessing, genannt) entwickelt wurden. ], +ja: [ + 組込みプロセッサにおいても、2個以上のコアを持つことがより一般的になってきています。 + このことは、並行性をさらに複雑にします。(`cortex_m::interrupt::Mutex`を含む)クリティカルセクションで使っている全ての例は、 + 他の実行スレッドは、割り込みスレッドだけであることを前提にしています。 + しかし、マルチコアシステムにおいては、これは当てはまりません。 + 代わりに、マルチコア(SMP; symmetric + multi-proccesingとも呼ばれます)向けに設計した同期プリミティブが必要になります。 +], zh: [ 在嵌入式处理器中有两个或者多个核心很正常,其为并发添加了额外一层复杂性。所有使用临界区的例子(包括`cortex_m::interrupt::Mutex`)都假设了另一个执行的线程仅是中断线程,但是在一个多核系统中,这不再是正确的假设。反而,我们将需要为多核设计的同步原语(也被叫做SMP,symmetric multi-processing的缩写)。 @@ -1607,6 +1919,10 @@ de: [ Diese verwenden typischerweise die bereits erwähnten atomaren Befehle, da das Verarbeitungssystem die Atomarität über alle Kerne hinweg gewährleistet. ], +ja: [ + 通常、これまでに見たアトミック命令を使用します。 + アトミック命令は、処理システムが全てのコア間でのアトミック性を維持してくれるためです。 +], zh: [ 我们之前看到的,这些通常使用原子指令,因为处理系统将确保原子性在所有的核中都保持着。 ])) @@ -1620,6 +1936,10 @@ de: [ Eine detaillierte Behandlung dieser Themen würde den Rahmen dieses Buches sprengen, die allgemeinen Muster sind jedoch dieselben wie im Einzelkernfall. ], +ja: [ + これらのトピックを詳細に説明することは、この本のスコープ範囲外ですが、 + 一般的なパターンはシングルコアの場合と同じです。 +], zh: [ 覆盖这些主题的细节已经超出了本书的范围,但是常规的模式与单核的相似。 ])) diff --git a/typ/config.typ b/typ/config.typ index 7f1eca92..b2390083 100644 --- a/typ/config.typ +++ b/typ/config.typ @@ -61,8 +61,17 @@ #let book_title = "The Embedded Rust Book" -#let h1(it, offset: 0) = if tgt == "pdf" { - heading(it, depth: 1, offset: offset) -} else { - title(it) +#let h1(it, offset: 0) = { + if type(it) == dictionary { + it = tr(it) + } + if tgt == "pdf" { + heading(it, depth: 1, offset: offset) + } else { + title(it) + } + /*show title: it => { + heading(it.body, depth: 1, offset: offset) + } + title(it)*/ } diff --git a/typ/design-patterns/hal/checklist.typ b/typ/design-patterns/hal/checklist.typ index ed686baa..e86929fc 100644 --- a/typ/design-patterns/hal/checklist.typ +++ b/typ/design-patterns/hal/checklist.typ @@ -1,10 +1,9 @@ #import "../../config.typ": * -#h1(offset: whole*2, tr(( - en: [HAL Design Patterns Checklist], +#h1((en: [HAL Design Patterns Checklist], de: [Checkliste HAL-Design-Muster], zh: [HAL设计检查清单], -))) +), offset: whole*2) #tr(( diff --git a/typ/design-patterns/hal/gpio.typ b/typ/design-patterns/hal/gpio.typ index b9e47830..3d8668d8 100644 --- a/typ/design-patterns/hal/gpio.typ +++ b/typ/design-patterns/hal/gpio.typ @@ -1,10 +1,9 @@ #import "../../config.typ": * -#h1(offset: whole*2, tr(( - en: [Recommendations for GPIO Interfaces], +#h1((en: [Recommendations for GPIO Interfaces], de: [Empfehlungen für GPIO-Schnittstellen], zh: [关于GPIO接口的建议], -))) +), offset: whole*2) = #tr(( diff --git a/typ/design-patterns/hal/index.typ b/typ/design-patterns/hal/index.typ index 60e6f16b..86f1e643 100644 --- a/typ/design-patterns/hal/index.typ +++ b/typ/design-patterns/hal/index.typ @@ -1,10 +1,9 @@ #import "../../config.typ": * -#h1(offset: whole, tr(( - en: [HAL Design Patterns], +#h1((en: [HAL Design Patterns], de: [HAL-Design-Muster], zh: [HAL设计模式], -))) +), offset: whole) #let url_quide = "https://rust-lang.github.io/api-guidelines/" #tr(( diff --git a/typ/design-patterns/hal/interoperability.typ b/typ/design-patterns/hal/interoperability.typ index 38855bac..19981169 100644 --- a/typ/design-patterns/hal/interoperability.typ +++ b/typ/design-patterns/hal/interoperability.typ @@ -1,10 +1,9 @@ #import "../../config.typ": * -#h1(offset: whole*2, tr(( - en: [Interoperability], +#h1((en: [Interoperability], de: [Interoperabilität], zh: [互用性], -))) +), offset: whole*2) = #tr(( diff --git a/typ/design-patterns/hal/naming.typ b/typ/design-patterns/hal/naming.typ index 1a95b450..43af582d 100644 --- a/typ/design-patterns/hal/naming.typ +++ b/typ/design-patterns/hal/naming.typ @@ -1,10 +1,9 @@ #import "../../config.typ": * -#h1(offset: whole*2, tr(( - en: [Naming], +#h1((en: [Naming], de: [Benennung], zh: [命名], -))) +), offset: whole*2) = #tr(( diff --git a/typ/design-patterns/hal/predictability.typ b/typ/design-patterns/hal/predictability.typ index db971284..a436f2fc 100644 --- a/typ/design-patterns/hal/predictability.typ +++ b/typ/design-patterns/hal/predictability.typ @@ -1,10 +1,9 @@ #import "../../config.typ": * -#h1(offset: whole*2, tr(( - en: [Predictability], +#h1((en: [Predictability], de: [Vorhersehbarkeit], zh: [可预见性], -))) +), offset: whole*2) = #tr(( diff --git a/typ/design-patterns/index.typ b/typ/design-patterns/index.typ index 5a62551e..1dd0f266 100644 --- a/typ/design-patterns/index.typ +++ b/typ/design-patterns/index.typ @@ -1,10 +1,9 @@ #import "../config.typ": * -#h1(tr(( - en: [Design Patterns], +#h1((en: [Design Patterns], de: [Design-Muster], zh: [设计模式], -))) +)) #tr(( en: [ diff --git a/typ/interoperability/c-with-rust.typ b/typ/interoperability/c-with-rust.typ index 2ee880f3..064e88f6 100644 --- a/typ/interoperability/c-with-rust.typ +++ b/typ/interoperability/c-with-rust.typ @@ -1,10 +1,10 @@ #import "../config.typ": * -#h1(offset: whole, tr(( - en: [A little C with your Rust], +#h1((en: [A little C with your Rust], de: [Ein bisschen C zu Ihrem Rust], + ja: [Rustと少しのC], zh: [使用C的Rust], -))) +), offset: whole) #tr(( @@ -19,6 +19,11 @@ de: [ - Einkapselung der bereitgestellten C-API für die Verwendung mit Rust - Erstellen Ihres C- oder C++-Codes zur Integration mit dem Rust-Code ], +ja: [ + CまたはC++をRustプロジェクトの内部で使うには、主に2つの対応をします。 + - 公開されているCのAPIを、Rustで使えるようにラッピングする + - CまたはC++のコードを、Rustのコードと一緒にビルドする +], zh: [ 要在一个Rust项目中使用C或者C++,主要有两个步骤: - 用Rust封装要暴露出来使用的C API @@ -35,6 +40,9 @@ de: [ abzielen könnte, wird empfohlen, bei der Kombination von Rust mit C oder C++ die `C`-ABI zu verwenden. ], +ja: [ + C++は、Rustコンパイラがターゲットにできる安定したABIを持っていないため、CまたはC++とRustとを組み合わせるときは、`C`のABIを使うのがお勧めです。 +], zh: [ 因为对于Rust编译器来说,C++没有一个稳定的ABI,当要将Rust和C或者C++结合时,建议优先选择`C`。 ])) @@ -42,6 +50,7 @@ zh: [ = #tr(( en: [Defining the interface], de: [Definition der Schnittstelle], + ja: [インタフェースの定義], zh: [定义接口], )) @@ -61,6 +70,11 @@ de: [ Rust ist es erforderlich, diese Definitionen entweder manuell nach Rust zu übertragen oder ein Werkzeug zu ihrer Generierung zu verwenden. ], +ja: [ + RustからCまたはC++のコードを使う前に、リンクされるコードにどのようなデータ型や関数シグネチャが存在するかを、(Rustに)定義する必要があります。 + CまたはC++では、このデータを定義したヘッダファイル(`.h`または`.hpp`)をインクルードします。 + Rustでは、これらの定義を手動で変換するか、定義を自動生成するツールを使うか、のいずれかが必要です。 +], zh: [ 在Rust消费C或者C++代码之前,必须定义(在Rust中定义),在要被链接的代码中存在什么数据类型和函数签名。在C或者C++中,你要包含一个头文件(`.h`或者`.hpp`),其定义了这个数据。而在Rust中,必须手动地将这些定义翻译成Rust,或者使用一个工具去生成这些定义。 ])) @@ -74,6 +88,9 @@ de: [ Zunächst behandeln wir die manuelle Übertragung dieser Definitionen von C/C++ nach Rust. ], +ja: [ + まずは、C/C++からRustに、定義を手動で変換する方法を説明します。 +], zh: [ 首先,我们将介绍如何将这些定义从C/C++手动地转换为Rust。 ])) @@ -81,6 +98,7 @@ zh: [ == #tr(( en: [Wrapping C functions and Datatypes], de: [Einbinden von C-Funktionen und -Datentypen], + ja: [Cの関数とデータ型のラッピング], zh: [封装C函数和数据类型], )) @@ -95,6 +113,10 @@ de: [ Header-Datei bereit, die alle in öffentlichen Schnittstellen verwendeten Typen und Funktionen definiert. Eine Beispieldatei könnte wie folgt aussehen: ], +ja: [ + 通常、CまたはC++で書かれたライブラリは、公開インタフェースで使用する全ての型と関数を定義するヘッダファイルを提供します。 + ヘッダファイルの例は次の通りです。 +], zh: [ 通常,用C或者C++写的库会提供一个头文件,头文件定义了所有的类型和用于公共接口的函数。如下是一个示例文件: ])) @@ -116,6 +138,9 @@ en: [ de: [ Nach Rust übertragen, sähe diese Schnittstelle folgendermaßen aus: ], +ja: [ + Rustに変換すると、このインタフェースは次のようになります。 +], zh: [ 当翻译成Rust时,这个接口将看起来像是: ])) @@ -146,6 +171,9 @@ de: [ Schauen wir uns diese Definition Schritt für Schritt an, um die einzelnen Bestandteile zu erläutern. ], +ja: [ + 各部分の説明をするため、この定義を1つずつ見ていきましょう。 +], zh: [ 让我们一次看一个语句,来解释每个部分。 ])) @@ -169,6 +197,11 @@ de: [ `#[repr(C)]`; dieses weist den Rust-Compiler an, für die Anordnung der Daten innerhalb der Struktur stets dieselben Regeln wie C anzuwenden. ], +ja: [ + デフォルトでは、Rustは`struct`に含まれるデータの順序やパディング、サイズを保証しません。 + Cのコードとの互換性を保証するために、`#[repr(C)]`アトリビュートを使います。 + このアトリビュートにより、Rustコンパイラは、構造体のデータをCと同じルールで構成します。 +], zh: [ 默认,Rust不会保证包含在`struct`中的数据的大小,填充,或者顺序。为了保证与C代码兼容,我们使用`#[repr(C)]`属性,它指示Rust编译器总是使用和C一样的规则去组织一个结构体中的数据。 ])) @@ -190,6 +223,10 @@ de: [ definiert, wird empfohlen, in „cty" definierte primitive Datentypen zu verwenden, die Typen von C auf Typen in Rust abbilden. ], +ja: [ + CまたはC++が`int`や`char`を定義する方法は柔軟であるため、`cty`で定義されているプリミティブデータ型の使用をお勧めします。 + ctyは、Cの型をRustの型にマップします。 +], zh: [ 由于C或者C++定义一个`int`或者`char`的方式很灵活,所以建议使用在`cty`中定义的基础类型,它将类型从C映射到Rust中的类型。 ])) @@ -211,8 +248,11 @@ de: [ `cool_function`, die die C-ABI verwendet. Da die Signatur ohne den Funktionsrumpf definiert wird, muss die eigentliche Funktionsdefinition an anderer Stelle bereitgestellt oder aus einer statischen Bibliothek in - die endgültige Bibliothek bzw. das fertige Binärprogramm eingebunden - werden. + die endgültige Bibliothek bzw. das fertige Binärprogramm eingebunden werden. +], +ja: [ + このステートメントは、`cool_function`という名前の、CのABIを使った関数シグネチャを定義します。 + 関数本体の定義なしにシグネチャを定義することで、この関数の定義は別の場所で与えられるか、静的ライブラリから最終的なライブラリまたはバイナリにリンクされる必要があります。 ], zh: [ 这个语句定义了一个使用C @@ -234,8 +274,11 @@ en: [ de: [ Ähnlich wie bei unserem obigen Datentyp definieren wir die Datentypen der Funktionsargumente mithilfe von C-kompatiblen Definitionen. Der - Übersichtlichkeit halber behalten wir zudem die ursprünglichen - Argumentnamen bei. + Übersichtlichkeit halber behalten wir zudem die ursprünglichen Argumentnamen bei. +], +ja: [ + 上記のデータ型と同様に、Cと互換の定義を使って、関数の引数のデータ型を定義します。 + わかりやすくするために、引数の名前は同じままにしてあります。 ], zh: [ 与我们上面的数据类型一样,我们使用C兼容的定义去定义函数参数的数据类型。为了清晰可见,我们还保留了相同的参数名。 @@ -259,6 +302,12 @@ de: [ mit C- oder C++-Code besondere Sorgfalt geboten, um die für Rust typischen Garantien zu wahren. ], +ja: [ + `*mut CoolStruct`という新しい型があります。Cは、`&mut CoolStruct`のようなRustの参照という概念を持っていません。 + 代わりに、生ポインタを使います。 + このポインタの参照外しは、`unsafe`です。また、このポインタは実際に`null`ポインタになる可能性があります。 + CまたはC++のコードとやり取りする時には、Rustが通常行う保証を確実にするように気をつける必要があります。 +], zh: [ 这里我们有了一个新类型,`*mut CoolStruct` 。因为C没有Rust中像 `&mut CoolStruct` @@ -268,6 +317,7 @@ zh: [ == #tr(( en: [Automatically generating the interface], de: [Automatische Generierung der Schnittstelle], + ja: [インタフェースの自動生成], zh: [自动产生接口], )) @@ -326,6 +376,18 @@ de: [ verwenden, falls die Typen in den generierten Bindings das Präfix `cty` aufweisen. ], +ja: [ + 面倒であり間違いの元である手動のインタフェース生成ではなく、#link(url_bindgen)[bindgen]と呼ばれるインタフェース変換を自動で行ってくれるツールがあります。 + #link(url_bindgen)[bindgen]の使用手順は、#link(url_bindgen_manual)[bindgenユーザマニュアル]を参照して下さい。 + #link(url_bindgen)[bindgen]を使用するための一般的なプロセスは、次の通りです。 + + Rustで使いたいインタフェースやデータ型を定義している全てのCまたはC++ヘッダを集めます。 + + ステップ1で集めたヘッダファイルを`#include "..."`する`bindings.h`ファイルを書きます。 + + `bindgen`にコンパイルフラグとコードと共に、`bindings.h`を与えます。 + ヒント:`#![no_std]`互換なコードを生成するために、`Builder.ctypes_prefix("cty")`と`--ctypes-prefix=cty`を使って下さい。 + + `bindgen`は、端末のウィンドウに生成したRustコードを出力します。これは、`bindings.rs`のようなプロジェクトのファイルにパイプすることができます。 + 外部ライブラリとしてコンパイル、リンクされたC/C++コードとやり取りするために、このファイルをRustプロジェクトで使用できます。 + #todoupd("ja") +], zh: [ 有一个叫做#link(url_bindgen)[bindgen]的工具,它可以自动执行这些转换,而不用手动生成这些接口,手动进行这样的操作非常繁琐且容易出错。关于#link(url_bindgen)[bindgen]的使用指令,可以参考#link(url_bindgen_manual)[bindgen user's manual],常用的步骤大致如下: + 收集所有定义了你可能在Rust中会用到的数据类型或者接口的C或者C++头文件。 @@ -341,6 +403,7 @@ zh: [ = #tr(( en: [Building your C/C++ code], de: [Erstellen Ihres C/C++-Codes], + ja: [C/C++コードのビルド], zh: [编译你的 C/C++ 代码], )) @@ -355,6 +418,9 @@ de: [ Code einer anderen Sprache mit C-Schnittstelle) kompiliert, muss der Nicht-Rust-Code vorab kompiliert werden. ], +ja: [ + Rustコンパイラは、CまたはC++のコード(または、Cのインタフェースを提供する他の言語のコード)をコンパイルする方法を直接は知らないため、Rustでないコードは事前にコンパイルする必要があります。 +], zh: [ 因为Rust编译器并不直接知道如何编译C或者C++代码(或者从其它语言来的代码,其提供了一个C接口),所以必须要静态编译你的非Rust代码。 ])) @@ -370,6 +436,10 @@ de: [ statischen Archiv (z. B. `cool-library.a`) kompiliert wird, welches dann im abschließenden Link-Schritt mit dem Rust-Code zusammengeführt werden kann. ], +ja: [ + 組込みプロジェクトでは、C/C++のコードを(`cool-library.a`のような)静的なアーカイブにコンパイルすることを意味します。 + このアーカイブは、最終リンク時に、Rustのコードに組み込むことができます。 +], zh: [ 对于嵌入式项目,这通常意味着把C/C++代码编译成一个静态库文档(比如 `cool-library.a`),然后其能在最后链接阶段与你的Rust代码组合起来。 @@ -389,6 +459,11 @@ de: [ beschrieben umzuwandeln und das statische Archiv beim Kompilieren bzw. Linken einzubinden. ], +ja: [ + 使おうとしているライブラリが、既に静的なアーカイブとして配布されている場合、そのコードを再度ビルドする必要はありません。 + 提供されているインタフェースのヘッダファイルを、上述の方法で、単に変換するだけです。 + そして、その静的なアーカイブをコンパイル/リンク時に組み込みます。 +], zh: [ 如果你要使用的库已经作为一个静态库文档被发布,那就没必要重新编译你的代码。只需按照上面所述转换提供的接口头文件,且在编译/链接时包含静态库文档。 ])) @@ -409,6 +484,11 @@ de: [ `cc`-Crate erfolgen. Für beide Vorgehensweisen ist die Verwendung eines `build.rs`-Skripts erforderlich. ], +ja: [ + もしコードがソースファイルとして存在するのであれば、C/C++のコードを静的ライブラリとしてコンパイルする必要があります。 + (`make`や`CMake`のような)ビルドシステムを使うか、必要なコンパイルステップを`cc`クレートを使って移植するか、いずれかの方法を取れます。 + どちらの方法でも、`build.rs`スクリプトを使う必要があります。 +], zh: [ 如果你的代码作为一个源项目(source project)存在,将你的C/C++代码编译成一个静态库将是必须的,要么通过使用你现存的编译系统(比如 @@ -419,6 +499,7 @@ zh: [ == #tr(( en: [Rust `build.rs` build scripts], de: [Rust-`build.rs`-Build-Skripte], + ja: [Rustの`build.rs`ビルドスクリプト], zh: [Rust的 `build.rs` 编译脚本], )) @@ -434,6 +515,10 @@ de: [ Abhängigkeiten Ihres Projekts erstellt wurden, aber _bevor_ Ihr Projekt selbst kompiliert wird. ], +ja: [ + `build.rs`スクリプトは、Rustの構文で書かれたファイルです。 + このスクリプトは、プロジェクトの依存をビルドした後、プロジェクトをビルドする前に、コンパイルを行っているコンピュータ上で実行されます。 +], zh: [ 一个 `build.rs` 脚本是一个用Rust语法编写的文件,它被运行在你的编译机器上,发生在你项目的依赖项被编译*之后*,但是在你的项目被编译*之前* 。 @@ -455,15 +540,21 @@ de: [ zum Aufruf externer Build-Systeme wie `Make` oder zur direkten Kompilierung von C/C++-Code unter Verwendung des `cc`-Crates. ], +ja: [ + 完全なリファレンスは#link(url_build_scripts)[ここ]にあります。 + `build.rs`スクリプトは、(#link(url_bindgen)[bindgen]のようなツールを用いて)コードを生成したり、 + `Make`のような外部ビルドシステムを呼び出したり、`cc`クレートを使ってC/C++を直接ビルドしたりするのに便利です。 +], zh: [ - 可能能在#link("https://doc.rust-lang.org/cargo/reference/build-scripts.html")[这里]发现完整的参考。`build.rs` - 脚本能用来生成代码(比如通过#link("https://github.com/rust-lang/rust-bindgen")[bindgen]),调用外部编译系统,比如`Make`,或者直接通过使用`cc` + 可能能在#link(url_build_scripts)[这里]发现完整的参考。`build.rs` + 脚本能用来生成代码(比如通过#link(url_bindgen)[bindgen]),调用外部编译系统,比如`Make`,或者直接通过使用`cc` crate来直接编译C/C++ 。 ])) == #tr(( en: [Triggering external build systems], de: [Auslösen externer Build-Systeme], + ja: [外部ビルドシステムの使用], zh: [使用外部编译系统], )) @@ -486,6 +577,10 @@ de: [ die erzeugte statische Bibliothek an den entsprechenden Ort im `target`-Build-Verzeichnis. ], +ja: [ + 複雑な外部プロジェクトや外部ビルドシステムを使うプロジェクトに関しては、#ln_command;を使って、他のビルドシステムに対して「シェルを呼び出す」のが最も簡単でしょう。 + これにより、相対パスを渡り歩いたり、(`make library`のような)固定のコマンドを呼び出したり、その後、`target`ビルドディレクトリにある静的ライブラリをコピーしたりすることができます。 +], zh: [ 对于有复杂的外部项或者编译系统的项目,使用#ln_command;通过遍历相对路径来向其它编译系统"输出",调用一个固定的命令(比如 `make library`),然后拷贝最终的静态库到`target`编译文件夹中恰当的位置,可能是最简单的方法。 @@ -504,6 +599,10 @@ de: [ bedeutet, dass Sie beliebige Rust-Crates verwenden können, die auf Ihrem Kompilier-Host lauffähig sind. ], +ja: [ + 作っているクレートが`no_std`な組込みプラットフォームをターゲットにしていたとしても、`build.rs`はクレートをコンパイルしているコンピュータ上でのみ動作します。 + そのため、コンパイルしているホスト上で動作する、あらゆるRustのクレートを使うことができます。 +], zh: [ 虽然你的crate目标可能是一个`no_std`嵌入式平台,但你的`build.rs`只运行在负责编译你的crate的机器上。这意味着你能使用任何Rust crates,其将运行在你的编译主机上。 @@ -512,6 +611,7 @@ zh: [ == #tr(( en: [Building C/C++ code with the `cc` crate], de: [Kompilieren von C/C++-Code mit dem `cc`-Crate], + ja: [C/C++コードを`cc`クレートでビルド], zh: [使用`cc` crate构建C/C++代码], )) @@ -535,6 +635,11 @@ de: [ dieses bietet eine idiomatische Rust-Schnittstelle zu dem vom Host bereitgestellten Compiler. ], +ja: [ + 依存や複雑さが少ないプロジェクトや、(最終バイナリや実行ファイルではなく)静的ライブラリを作成するためにビルドシステムを修正するのが難しいプロジェクトであれば、 + 代わりに#link(url_cc)[`cc`クレート]を使う方が簡単でしょう。 + ccクレートは、ホスト上のコンパイラへの、扱いやすいRustインタフェースを提供します。 +], zh: [ 对于具有有限的依赖项或者复杂度的项目,或者对于那些难以修改编译系统去生成一个静态库(而不是一个二进制文件或者可执行文件)的项目,使用#url_cc;可能更容易,它提供了一个符合Rust语法的接口,这个接口是关于主机提供的编译器的。 ])) @@ -550,6 +655,9 @@ de: [ eine statische Bibliothek kompiliert wird, sähe ein Beispiel für ein `build.rs`-Skript, das das #link(url_cc)[`cc`-Crate] verwendet, folgendermaßen aus: ], +ja: [ + 単一のCファイルを静的ライブラリにコンパイルする最も単純な例では、#link(url_cc)[`cc`クレート]を使った`build.rs`スクリプトは次のようになります。 +], zh: [ 在把一个C文件编译成一个静态库的依赖项的最简单的场景下,可以使用#link(url_cc)[`cc` crate],示例`build.rs`脚本看起来像这样: ])) diff --git a/typ/interoperability/index.typ b/typ/interoperability/index.typ index cb5101ff..2842394f 100644 --- a/typ/interoperability/index.typ +++ b/typ/interoperability/index.typ @@ -1,10 +1,10 @@ #import "../config.typ": * -#h1(tr(( - en: [Interoperability], +#h1((en: [Interoperability], de: [Interoperabilität], + ja: [相互運用性], zh: [互操性], -))) +)) #let ln_ffi = link("https://doc.rust-lang.org/std/ffi/index.html")[`std::ffi`] #tr(( @@ -18,6 +18,12 @@ de: [ Umwandlung von Daten zwischen den beiden Sprachen ab. Hierfür gibt es in der Standardbibliothek (`stdlib`) ein spezielles Modul namens #ln_ffi. ], +ja: [ + #todoupd("ja") + RustとCとの相互運用性は、常に2つの言語間のデータ変換に依存しています。 + そこで、2つの専用モジュールが`stdlib`内にあります。 + #ln_ffi;と呼ばれるものです。 +], zh: [ Rust和C代码之间的互操性始终依赖于数据在两个语言间的转换.为了互操性,在`stdlib`中有一个专用的模块,叫作 #ln_ffi. ])) @@ -72,16 +78,19 @@ zh: [ tr(( en: [Rust type], de: [Rusttyp], + ja: [Rustの型], zh: [Rust类型], )), tr(( en: [Intermediate], de: [Dazwischenliegend], + ja: [中間表現], zh: [间接], )), tr(( en: [C type], de: [C-Typ], + ja: [Cの型], zh: [C类型], )), ), @@ -91,12 +100,14 @@ zh: [ tr(( en: [`u32` or `u64`], de: [`u32` or `u64`], + ja: [`u32` or `u64`], )), )), `c_uint`, `unsigned int`, tr(( en: [etc], de: [usw.], + ja: [他], )), […], […], ) @@ -131,6 +142,7 @@ fn foo(num: u32) { == #tr(( en: [Interoperability with other build systems], de: [Interoperabilität mit anderen Build-Systemen], + ja: [他のビルドシステムとの相互運用性], zh: [与其它编译系统的互用性], )) @@ -144,6 +156,9 @@ de: [ Embedded-Projekte ist die Kombination von Cargo mit Ihrem bestehenden Build-System, beispielsweise Make oder CMake. ], +ja: [ + 組込みプロジェクトにRustを組み込むための共通の要件は、Cargoとmakeやcmakeのような既存のビルドシステムとを組み合わせることです。 +], zh: [ 在嵌入式项目中引入Rust的一个常见需求是,把Cargo结合进你现存的编译系统中,比如make或者cmake。 ])) @@ -158,6 +173,9 @@ de: [ Wir sammeln Beispiele und Anwendungsfälle hierzu in unserem Problem-Verfolgungswerkzeug unter #link(url_issue61)[Issue \#61]. ], +ja: [ + #link(url_issue61)[issue \#61]でこれに関する事例とユースケースを集めています。 +], zh: [ 在#link(url_issue61)[issue \#61]的issue tracker上,我们正在为这个需求收集例子和用例。 @@ -166,6 +184,7 @@ zh: [ == #tr(( en: [Interoperability with RTOSs], de: [Interoperabilität mit RTOS], + ja: [RTOSとの相互運用性], zh: [与RTOSs的互操性], )) @@ -180,6 +199,10 @@ de: [ sich noch in der Entwicklung; insbesondere der Aufruf von RTOS-Funktionen aus Rust heraus kann sich als schwierig erweisen. ], +ja: [ + RustをFreeRTOSやChibiOSといったRTOSに統合することは、まだ作業を進めている状態です。 + 特に、RTOSの関数をRustから呼び出すことはトリッキーです。 +], zh: [ 将Rust和一个RTOS集成在一起,比如FreeRTOS或者ChibiOS仍然在进行中; 尤其是从Rust调用RTOS函数可能很棘手。 @@ -209,7 +232,10 @@ de: [ Problem-Verfolgungswerkzeug unter #link(url_issue62)[Issue \#62]. ], +ja: [ + #link(url_issue62)[issue \#62]でこれに関する事例とユースケースを集めています。 +], zh: [ - 在#link("https://github.com/rust-embedded/book/issues/62")[issue \#62]的issue + 在#link(url_issue62)[issue \#62]的issue tracker上,我们正为这件事收集例子和用例。 ])) diff --git a/typ/interoperability/rust-with-c.typ b/typ/interoperability/rust-with-c.typ index db90bedd..c997d597 100644 --- a/typ/interoperability/rust-with-c.typ +++ b/typ/interoperability/rust-with-c.typ @@ -1,10 +1,10 @@ #import "../config.typ": * -#h1(offset: whole, tr(( - en: [A little Rust with your C], +#h1((en: [A little Rust with your C], de: [Ein bisschen Rust zu Ihrem C], + ja: [Cと少しのRust], zh: [使用Rust的C], -))) +), offset: whole) #tr(( @@ -19,6 +19,11 @@ de: [ - Erstellung einer C-kompatiblen API in Rust - Einbindung Ihres Rust-Projekts in ein externes Build-System ], +ja: [ + CまたはC++のプロジェクト内でRustのコードを使うためには、主に次の2つの対応をします。 + - Cが扱いやすいAPIをRustに作ります + - 外部ビルドシステムにRustプロジェクトを組み込みます +], zh: [ 在C或者C++中使用Rust代码通常由两部分组成。 - 用Rust生成一个C友好的API @@ -37,6 +42,10 @@ de: [ besten damit, einfach `cargo` für die Kompilierung deines Crates und aller Abhängigkeiten zu verwenden. ], +ja: [ + `cargo`と`meson`は別として、ほとんどのビルドシステムはRustをサポートしていません。 + そのため、自分のクレートとその依存関係をコンパイルするには、`cargo`を使うのが一番です。 +], zh: [ 除了`cargo`和`meson`,大多数编译系统没有原生Rust支持。因此你最好只用`cargo`编译你的crate和依赖。 ])) @@ -44,6 +53,7 @@ zh: [ = #tr(( en: [Setting up a project], de: [Ein Projekt einrichten], + ja: [プロジェクトの準備], zh: [设置一个项目], )) @@ -53,6 +63,9 @@ en: [ ], de: [ Erstellen Sie wie gewohnt ein neues `cargo`-Projekt. +], +ja: [ + いつもどおり、新しい`cargo`プロジェクトを作成します。 ])) #tr(( @@ -67,6 +80,10 @@ de: [ diese Weise können Sie auch einen abweichenden Namen für die Bibliothek festlegen, falls dieser sich von dem des restlichen Crates unterscheiden soll. ], +ja: [ + 通常のRustのターゲットではなく、システムライブラリを出力するように、`cargo`に指示するフラグがあります。 + クレートの残り部分と異なる名前を付けたい場合、ライブラリに対して、別の名前を設定することもできます。 +], zh: [ 像往常一样创建一个新的`cargo`项目。有一些标志可以告诉`cargo`去生成一个系统库,而不是常规的rust目标文件。如果你想要它与crate的其它部分不一样,你也可以为你的库设置一个不同的输出名。 ])) @@ -77,11 +94,13 @@ name = \"your_crate\" crate-type = [\"cdylib\"] # " + ts(( en: "Creates dynamic lib", de: "Erstellt eine dynamische Bibliothek", + ja: "動的ライブラリを作ります", zh: "生成动态链接库", )) + " # crate-type = [\"staticlib\"] # " + ts(( en: "Creates static lib", de: "Erstellt eine statische Bibliothek", + ja: "静的ライブラリを作ります", zh: "生成静态链接库", )) + " ") @@ -89,6 +108,7 @@ crate-type = [\"cdylib\"] # " + ts(( = #tr(( en: [Building a `C` API], de: [Erstellung einer C-API], + ja: [`C` APIの作成], zh: [构建一个`C` API], )) @@ -104,6 +124,10 @@ de: [ verschiedenen Sprachen. Dies gilt auch für die Verwendung von Rust innerhalb von C- und C++-Code. ], +ja: [ + C++は、Rustコンパイラがターゲットにできる安定したABIを持っていないため、別言語との相互運用には、CのABIを使用します。 + CとC++のコード内でRustを使うとき、このことに例外はありません。 +], zh: [ 因为对于Rust编译器来说,C++没有稳定的ABI,因此对于不同语言间的互操性我们使用`C`。在C和C++代码的内部使用Rust时也不例外。 ])) @@ -122,6 +146,10 @@ de: [ außerhalb von Rust exportiert, mitgeteilt werden, dass der Compiler sie nicht verändern soll. ], +ja: [ + Rustコンパイラは、シンボル名をネイティブコードリンカが期待するものとは異なるものにマングルします。 + そのため、Rustの外にエクスポートするRustの関数は、マングルしないようにコンパイラに指示する必要があります。 +], zh: [ Rust对符号名的修饰与主机的代码链接器所期望的不同。因此,需要告知任何被Rust导出到Rust外部去使用的函数不要被编译器修饰。 ])) @@ -140,6 +168,11 @@ de: [ gerichteten FFI-APIs muss der Compiler daher angewiesen werden, die System-ABI zu verwenden. ], +ja: [ + デフォルトでは、Rustに書いたいずれの関数もRustのABI(これも安定化されていません)を使います。 + 代わりに、外に公開するFFI + APIはシステムABIを使うように、コンパイラに指示する必要があります。 +], zh: [ 默认,任何用Rust写的函数将使用Rust ABI(这也不稳定)。当编译面向外部的FFI APIs时,我们需要告诉编译器去使用系统ABI 。 @@ -155,6 +188,10 @@ de: [ Je nach Plattform möchten Sie möglicherweise eine bestimmte ABI-Version anvisieren; diese sind #link(url_external)[hier] dokumentiert. ], +ja: [ + プラットフォームによっては、特定のABIバージョンをターゲットにしたい場合があります。 + ABIについては、#link(url_external)[ここ]にドキュメントがあります。 +], zh: [ 取决于你的平台,你可能想要针对一个特定的ABI版本,其记录在#link(url_external)[这里]。 ])) @@ -169,6 +206,9 @@ de: [ Wenn man diese Teile zusammensetzt, erhält man eine Funktion, die ungefähr so ​​aussieht. ], +ja: [ + これらの部品をまとめると、おおよそ次のような関数になります。 +], zh: [ 把这些部分放在一起,你得到一个函数,其粗略看起来像是这个。 ])) @@ -191,6 +231,9 @@ de: [ nun Daten in eine Form umwandeln -- und aus dieser zurück --, die der Rest der Anwendung versteht. ], +ja: [ + Rustプロジェクトで`C`コードを使った時と同様に、異なる言語間で理解できるデータ型に変換する必要があります。 +], zh: [ 就像在Rust项目中使用`C`代码时那样,现在需要把数据转换为应用中其它部分可以理解的形式。 ])) @@ -198,6 +241,7 @@ zh: [ = #tr(( en: [Linking and greater project context], de: [Verknüpfung und übergeordneter Projektkontext], + ja: [リンクとより大きなプロジェクトの状況], zh: [链接和更大的项目上下文], )) @@ -208,6 +252,9 @@ en: [ de: [ Damit ist die eine Hälfte des Problems gelöst. Wie verwendet man das nun? ], +ja: [ + ここまでで、問題の半分は解決しました。 これをどうやって使うのでしょうか? +], zh: [ 问题只解决了一半。 @@ -221,6 +268,9 @@ en: [ de: [ *Das hängt stark von Ihrem Projekt bzw. Ihrem Build-System ab.* ], +ja: [ + *それは、プロジェクトやビルドシステムに強く依存します。* +], zh: [ *这很大程度上取决于你的项目或者编译系统* ])) @@ -236,6 +286,10 @@ de: [ namens `my_lib.so`, `my_lib.dll` oder `my_lib.a`. Diese Bibliothek kann einfach von Ihrem Build-System eingebunden (gelinkt) werden. ], +ja: [ + `cargo`は、プラットフォームと設定に依存して、`my_lib.so`、`my_lib.dll`または`my_lib.a`ファイルを作成します。 + このライブラリは、そのプラットフォームのビルドシステムによって簡単にリンクすることができます。 +], zh: [ `cargo`将生成一个`my_lib.so`/`my_lib.dll`或者`my_lib.a`文件,取决于你的平台和配置。可以通过编译系统简单地链接这个库。 ])) @@ -249,6 +303,9 @@ de: [ Um jedoch eine Rust-Funktion aus C heraus aufzurufen, ist eine Header-Datei erforderlich, in der die Funktionssignaturen deklariert werden. ], +ja: [ + しかし、CからRustを呼ぶためには、関数シグネチャを宣言するためのヘッダファイルが必要です。 +], zh: [ 然而,从C调用一个Rust函数要求一个头文件去声明函数的签名。 ])) @@ -261,6 +318,9 @@ de: [ Für jede Funktion in Ihrer Rust-FFI-API muss eine entsprechende Deklaration im Header vorhanden sein. ], +ja: [ + Rust-ffi APIの関数全てが、対応する関数ヘッダを持つ必要があります。 +], zh: [ 在Rust-ffi API中的每个函数需要有一个相关的头文件函数。 ])) @@ -277,6 +337,9 @@ en: [ de: [ würde dann werden ], +ja: [ + 上記は、次のようになるでしょう。 +], zh: [ 将会变成 ])) @@ -292,6 +355,9 @@ en: [ de: [ usw. ], +ja: [ + などなど。 +], zh: [ 等等。 ])) @@ -310,6 +376,10 @@ de: [ Rust-Code analysiert und daraus Header-Dateien für Ihre C- und C++-Projekte generiert. ], +ja: [ + このプロセスを自動化する#ln_cbindgen;というツールがあります。 + このツールは、Rustのコードを解析して、CとC++プロジェクトのためのヘッダを生成します。 +], zh: [ 这里有个工具可以自动化这个过程,叫做#ln_cbindgen,其会分析你的Rust代码然后为C和C++项目生成头文件。 ])) @@ -323,6 +393,9 @@ de: [ An diesem Punkt ist die Verwendung der Rust-Funktionen aus C heraus so einfach, wie den Header einzubinden und die Funktionen aufzurufen! ], +ja: [ + この時点で、CからRustの関数を使うには、単にヘッダをインクルードして、それを呼び出すだけです! +], zh: [ 此时从C中使用Rust函数非常简单,只需包含头文件和调用它们! ])) diff --git a/typ/intro/hardware.typ b/typ/intro/hardware.typ index 33b9a430..dd2f7d6b 100644 --- a/typ/intro/hardware.typ +++ b/typ/intro/hardware.typ @@ -1,12 +1,11 @@ #import "../config.typ": * -#h1(offset: whole, tr(( - en: [Meet Your Hardware], +#h1((en: [Meet Your Hardware], de: [Lernen Sie Ihre Hardware kennen], ja: [ハードウェアとの出会い], uk: [Зустрічайте залізо], zh: [熟悉你的硬件], -))) +), offset: whole) #tr(( en: [ diff --git a/typ/intro/index.typ b/typ/intro/index.typ index d10bdb5c..306080d6 100644 --- a/typ/intro/index.typ +++ b/typ/intro/index.typ @@ -1,12 +1,11 @@ #import "../config.typ": * -#h1(tr(( - en: [Introduction], +#h1((en: [Introduction], de: [Einleitung], ja: [導入], uk: [Вступ], zh: [引言], -))) +)) #set heading(offset: whole*2) #tr(( @@ -440,35 +439,35 @@ zh: [ zh: [翻译], )) -#let jp_ln = "https://tomoyuki-nakabayashi.github.io/book/" -#let jp_rep = "https://github.com/tomoyuki-nakabayashi/book" -#let cn_ln = "https://xxchang.github.io/book/" -#let cn_rep = "https://github.com/XxChang/book" +#let url_ja = "https://tomoyuki-nakabayashi.github.io/book/" +#let url_ja_rep = "https://github.com/tomoyuki-nakabayashi/book" +#let url_zh = "https://xxchang.github.io/book/" +#let url_zh_rep = "https://github.com/XxChang/book" #tr(( en: [ This book has been translated by generous volunteers. If you would like your translation listed here, please open a PR to add it. - - #link(jp_ln)[Japanese] (#link(jp_rep)[repository]) - - #link(cn_ln)[Chinese] (#link(cn_rep)[repository]) + - #link(url_ja)[Japanese] (#link(url_ja_rep)[repository]) + - #link(url_zh)[Chinese] (#link(url_zh_rep)[repository]) ], de: [ Dieses Buch wurde von großzügigen Freiwilligen übersetzt. Wenn Ihre Übersetzung hier aufgeführt werden soll, erstellen Sie bitte einen Pull Request, um sie hinzuzufügen. - - #link(jp_ln)[Japanisch] (#link(jp_rep)[repository]) - - #link(cn_ln)[Chinesisch] (#link(cn_rep)[repository]) + - #link(url_ja)[Japanisch] (#link(url_ja_rep)[repository]) + - #link(url_zh)[Chinesisch] (#link(url_zh_rep)[repository]) ], uk: [ Цю книгу перекладено волонтерами. Якщо ви хочете, щоб ваш переклад був тут, будь ласка, відкрийте запит на злиття, щоб додати його. - - #link(jp_ln)[Японською] (#link(jp_rep)[репозиторій]) - - #link(cn_ln)[Китайською] (#link(cn_rep)[репозиторій]) + - #link(url_ja)[Японською] (#link(url_ja_rep)[репозиторій]) + - #link(url_zh)[Китайською] (#link(url_zh_rep)[репозиторій]) ], zh: [ 这本书是已经被一些慷慨的志愿者们翻译了。如果你想要将你的翻译列在这里,请打开一个PR去添加它。 - - #link(jp_ln)[日文] (#link(jp_rep)[repository]) - - #link(cn_ln)[中文] (#link(cn_rep)[repository]) + - #link(url_ja)[日文] (#link(url_ja_rep)[repository]) + - #link(url_zh)[中文] (#link(url_zh_rep)[repository]) ])) == #tr(( diff --git a/typ/intro/install.typ b/typ/intro/install.typ index 99930951..006991a1 100644 --- a/typ/intro/install.typ +++ b/typ/intro/install.typ @@ -1,11 +1,10 @@ #import "../config.typ": * -#h1(offset: whole, tr(( - en: [Installing the tools], +#h1((en: [Installing the tools], de: [Werkzeuge installieren], ja: [ツールのインストール], zh: [安装工具], -))) +), offset: whole) #tr(( en: [ diff --git a/typ/intro/install/linux.typ b/typ/intro/install/linux.typ index f3d03aed..0eff6de1 100644 --- a/typ/intro/install/linux.typ +++ b/typ/intro/install/linux.typ @@ -1,7 +1,7 @@ #import "../../config.typ": * -#h1(offset: whole*2, - [Linux]) +#h1([Linux], + offset: whole*2) #tr(( diff --git a/typ/intro/install/macos.typ b/typ/intro/install/macos.typ index ded262df..2f873458 100644 --- a/typ/intro/install/macos.typ +++ b/typ/intro/install/macos.typ @@ -1,7 +1,7 @@ #import "../../config.typ": * -#h1(offset: whole*2, - [macOS]) +#h1([macOS], + offset: whole*2) #let brew_ln = link("http://brew.sh/")[Homebrew] diff --git a/typ/intro/install/verify.typ b/typ/intro/install/verify.typ index cf6bb7cc..ad12df6a 100644 --- a/typ/intro/install/verify.typ +++ b/typ/intro/install/verify.typ @@ -1,11 +1,10 @@ #import "../../config.typ": * -#h1(offset: whole*2, tr(( - en: [Verify Installation], +#h1((en: [Verify Installation], de: [Die Installation überprüfen], ja: [インストールの確認], zh: [安装验证], -))) +), offset: whole*2) #tr(( diff --git a/typ/intro/install/windows.typ b/typ/intro/install/windows.typ index 73f062c3..d5b293c0 100644 --- a/typ/intro/install/windows.typ +++ b/typ/intro/install/windows.typ @@ -1,7 +1,7 @@ #import "../../config.typ": * -#h1(offset: whole*2, - [Windows]) +#h1([Windows], + offset: whole*2) = `arm-none-eabi-gdb` diff --git a/typ/intro/no-std.typ b/typ/intro/no-std.typ index 5c2f6e3c..cb0cecdd 100644 --- a/typ/intro/no-std.typ +++ b/typ/intro/no-std.typ @@ -1,11 +1,10 @@ #import "../config.typ": * -#h1(offset: whole, tr(( - en: [A `no_std` Rust Environment], +#h1((en: [A `no_std` Rust Environment], de: [Eine `no_std`-Rust-Umgebung], ja: [Rustの`no_std`環境], zh: [一个 `no_std` Rust环境], -))) +), offset: whole) #let ln_st72325 = link("https://www.st.com/resource/en/datasheet/st72325j6.pdf")[ST72325xx] #let ln_rpi = link("https://en.wikipedia.org/wiki/Raspberry_Pi#Specifications")[Modell B 3+] diff --git a/typ/intro/tooling.typ b/typ/intro/tooling.typ index 14d6e9af..14205523 100644 --- a/typ/intro/tooling.typ +++ b/typ/intro/tooling.typ @@ -1,11 +1,10 @@ #import "../config.typ": * -#h1(offset: whole, tr(( - en: [Tooling], +#h1((en: [Tooling], de: [Werkzeuge], ja: [ツール], zh: [工具], -))) +), offset: whole) #tr(( en: [ @@ -233,11 +232,10 @@ zh: [ QEMU是一个仿真器。在这个例子里,我们使用能完全仿真ARM系统的改良版QEMU。我们使用QEMU在主机上运行嵌入式程序。多亏了它,你可以在没有任何硬件的情况下,尝试这本书的部分示例。 ])) -#h1(offset: whole, tr(( - en: [Tooling for Embedded Rust Debugging], +#h1((en: [Tooling for Embedded Rust Debugging], de: [Werkzeuge für das Debugging von Embedded Rust], zh: [用于调试嵌入式Rust的工具], -))) +), offset: whole) = #tr(( en: [Overview], diff --git a/typ/peripherals/a-first-attempt.typ b/typ/peripherals/a-first-attempt.typ index 8625e50c..8e8cc0b2 100644 --- a/typ/peripherals/a-first-attempt.typ +++ b/typ/peripherals/a-first-attempt.typ @@ -1,14 +1,15 @@ #import "../config.typ": * -#h1(offset: whole, tr(( - en: [A First Attempt], +#h1((en: [A First Attempt], de: [Ein erster Versuch in Rust], + ja: [最初の試み], zh: [Rust尝鲜], -))) +), offset: whole) = #tr(( en: [The Registers], de: [Die Register], + ja: [レジスタ], zh: [寄存器], )) @@ -31,12 +32,24 @@ de: [ #link(url_arm)[ARM-Referenzhandbuch]. Wir sehen, dass es vier Register gibt: ], +ja: [ + `SysTick` ペリフェラルを見ていきましょう。 `SysTick` + はCortex-Mプロセッサ・コアに搭載されているシンプルなタイマーです。 + 通常は、チップメーカーのデータシートや_テクニカルリファレンスマニュアル_でこれらのペリフェラルの情報を調べることができるのですが、この例においては全てのARM + Cortex-Mコアで共通のものですので、今回は#link("http://infocenter.arm.com/help/topic/com.arm.doc.dui0553a/Babieigh.html")[ARMリファレンスマニュアル]を見てみましょう。 + そこには4つのレジスタが載っています。 +], zh: [ 让我们看下 'SysTick' 外设 - 一个简单的计时器,它存在于每个Cortex-M处理器内核中。通常你能在芯片厂商的数据手册或者_技术参考手册_中看到它们,但是下面的例子对所有ARM Cortex-M核心都是通用的,让我们看下#link(url_arm)[ARM参考手册]。我们能看到这里有四个寄存器: ])) +#let _32bits = tr(( + en: [32 bits], + ja: [32ビット], +), default: [32 bits]) + #figure( kind: table, table( @@ -46,21 +59,25 @@ zh: [ tr(( en: [Offset], de: [Offset], + ja: [オフセット], zh: [Offset], )), tr(( en: [Name], de: [Name], + ja: [名前], zh: [Name], )), tr(( en: [Description], de: [Beschreibung], + ja: [説明], zh: [Description], )), tr(( en: [Width], de: [Breite], + ja: [幅], zh: [Width], )), ), @@ -68,36 +85,41 @@ zh: [ tr(( en: [Control and Status Register], de: [Steuerung und Status Register], + ja: [制御およびステータスレジスタ], zh: [控制和状态寄存器], )), - [32 bits], + _32bits, [0x04], [SYST_RVR], tr(( en: [Reload Value Register], de: [Wert-Register neu laden], + ja: [リロード値レジスタ], zh: [重装载值寄存器], )), - [32 bits], + _32bits, [0x08], [SYST_CVR], tr(( en: [Current Value Register], de: [Register für den aktuellen Wert], + ja: [現在値レジスタ], zh: [当前值寄存器], )), - [32 bits], + _32bits, [0x0C], [SYST_CALIB], tr(( en: [Calibration Value Register], de: [Kalibrierwert-Register], + ja: [キャリブレーション値レジスタ], zh: [校准值寄存器], )), - [32 bits], + _32bits, ) ) = #tr(( en: [The C Approach], de: [Der C-Ansatz], + ja: [Cアプローチ], zh: [C语言风格的方法(The C Approach)], )) @@ -110,6 +132,9 @@ de: [ In Rust können wir eine Sammlung von Registern auf genau dieselbe Weise darstellen wie in C -- mit einem `struct`. ], +ja: [ + Rustでも、`struct`を使ってCと同じ方法でレジスタの集合を正確に表現することができます。 +], zh: [ 在Rust中,我们可以像C语言一样,用一个 `struct` 表示一组寄存器。 ])) @@ -144,6 +169,13 @@ de: [ obigen Tabelle entsprechen. Aber diese „Struktur" allein nützt natürlich nichts -- wir brauchen eine Variable. ], +ja: [ + `#[repr(C)]`修飾子はRustコンパイラ対して、この構造体をCコンパイラと同じようにメモリにレイアウトするように指示します。 + これはとても重要なことです。なぜならRustでは、Cにおいては行われない構造体のフィールドの並び替えが許されているためです。 + コンパイラによって暗黙のうちに構造体のフィールドが並び替えられることにより、デバッグをするはめになることは想像できるでしょう! + この修飾子を置くことで、4つの32ビットの各フィールドは上記のテーブルに対応付けられます。 + ただしもちろん、この`struct`はそれ自体では何の役にも立ちません。次のように変数として使う必要があります。 +], zh: [ 告诉Rust编译器像C编译器一样去布局这个结构体。这非常重要,因为Rust允许结构体字段被重新排序,而C语言不允许。你可以想象下如果这些字段被编译器悄悄地重新排了序,在调试时会给我们带来多大的麻烦!有了这个限定符,我们就有了与上表对应的四个32位的字段。但当然,这个 `struct` 本身没什么用处 - 我们需要一个变量。 @@ -157,6 +189,7 @@ let time = unsafe { (*systick).cvr }; = #tr(( en: [Volatile Accesses], de: [Volatile-Zugriffe], + ja: [volatileアクセス], zh: [volatile访问(Volatile Accesses)], )) @@ -180,6 +213,13 @@ de: [ diese Struktur auf die Hardware zugreifen. + Vor allem aber funktioniert es eigentlich nicht … ], +ja: [ + 上記のやり方には、いくつか問題があります。 + + ペリフェラルにアクセスするためには毎回アンセーフを使わなくてはなりません。 + + どのレジスタが読み取り専用でどのレジスタが読み書き可能かを指定する方法がありません。 + + プログラム内のどのコードからでもこの構造体を通してハードウェアにアクセスできてしまいます。 + + 最も大事なことは、このコードは実際には動作しないということです… +], zh: [ 现在,上面的方法有一堆问题。 + 每次想要访问外设,不得不使用unsafe 。 @@ -205,6 +245,12 @@ de: [ wie vorgesehen ausgeführt wird. In Rust hingegen kennzeichnen wir die _Zugriffe_ als volatile, nicht die Variable. ], +ja: [ + ここで問題となるのは、コンパイラが賢いということです。 + 同じRAMに相次いで2回書き込むと、コンパイラはこれに気づき、最初の書き込みを完全にスキップします。 + Cでは、全ての読み書きが意図した通りに行われることを保証するために、`volatile`型修飾子を変数につけることができます。 + Rustでは、変数ではなく_アクセス_に対してvolatileをつけます。 +], zh: [ 现在的问题是编译器很聪明。如果你往RAM同个地方写两次,一个接着一个,编译器会注意到这个行为,且完全跳过第一个写入操作。在C语言中,我们能标记变量为`volatile`去确保每个读或写操作按所想的那样发生。在Rust中,我们将_访问_操作标记为易变的(volatile),而不是将变量标记为volatile。 ])) @@ -225,10 +271,14 @@ de: [ noch mehr `unsafe`-Code! Glücklicherweise gibt es ein Crate eines Drittanbieters, das hierbei helfen kann -- #ln_volatile. ], +ja: [ + これで4つの問題のうち1つを直せました。しかし、さらに`unsafe`なコードがあります! + 幸いなことに、これに対処できるサードパーティ製のクレート#ln_volatile;があります。 +], zh: [ 这样,我们已经修复了一个问题,但是现在我们有了更多的 `unsafe` 代码!幸运的是,有个第三方的crate可以帮助到我们 - - #link("https://crates.io/crates/volatile_register")[`volatile_register`] + #ln_volatile ])) ```rust @@ -268,6 +318,10 @@ de: [ diese Schreibvorgänge tatsächlich sicher sind; daher ist dies eine sinnvolle Standardeinstellung. ], +ja: [ + これで`read`と`write`メソッドを通してvolatileアクセスが自動的に行われるようになりました。 + 書き込みを実行するのはまだ`unsafe`です。しかし、公平を期するために言うと、ハードウェアは変更可能な状態の集まりであるため、それらへの書き込みが実際に安全なのかどうか、をコンパイラが知る方法はないのです。そのため、これは基本姿勢としては悪くないでしょう。 +], zh: [ 现在通过`read`和`write`方法,volatile accesses可以被自动执行。执行写操作仍然是 `unsafe` @@ -277,6 +331,7 @@ zh: [ = #tr(( en: [The Rusty Wrapper], de: [Der Rusty Wrapper], + ja: [Rustのラッパ], zh: [Rust风格的封装], )) @@ -296,6 +351,10 @@ de: [ kümmern müssen (vorausgesetzt, sie vertrauen darauf, dass wir alles richtig gemacht haben!). ], +ja: [ + ユーザが安全に呼び出せるように、この`struct`を高レイヤーのAPIでラップする必要があります。 + ドライバの作者として、アンセーフなコードが正しいことを手動で検証し、ユーザがそのドライバを使用する上で心配することがないように安全なAPIとして提供します。(ユーザは提供されたものが正しいと信頼しています!) +], zh: [ 我们需要把这个`struct`封装进一个更高抽象的API中,这个API对于用户来说,可以安全地调用。作为驱动的作者,我们亲手验证不安全的代码是否正确,然后为我们的用户提供一个safe的API,因此用户们不必担心它(让他们相信我们不会出错!)。 ])) @@ -307,6 +366,9 @@ en: [ de: [ Ein Beispiel hierfür wäre: ], +ja: [ + 一例を挙げます。 +], zh: [ 有可能可以这样写: ])) @@ -358,6 +420,9 @@ de: [ Das Problem bei diesem Ansatz ist nun, dass der folgende Code für den Compiler völlig zulässig ist: ], +ja: [ + このやり方の問題は、次のコードがコンパイラに完全に受け入れられることです。 +], zh: [ 现在,这种方法带来的问题是,下列的代码完全可以被编译器接受: ])) @@ -396,6 +461,10 @@ de: [ über mehrere Module, Treiber, Entwickler und Zeiträume hinweg erstreckt, schleichen sich derartige Fehler immer leichter ein. ], +ja: [ + `set_reload`関数に`&mut self`引数を渡すことで、_その_インスタンスの`SystemTimer`構造体に対する他の参照がないことを確認しますが、全く同じペリフェラルを指す2つ目の`SystemTimer`構造体をユーザが作ることは止められません! + このように書かれたコードは、作者がこれらの「重複した」ドライバのインスタンスを全て見つけるのに十分に熱心であれば動作するでしょうが、一度コードが複数のモジュール、ドライバ、開発者、そして日に渡って分散すると、この種の間違いがどんどん入り込みやすくなっていきます。 +], zh: [ 虽然 `set_reload` 函数的 `&mut self` 参数保证了没有引用到其它的`SystemTimer`结构体,但是不能阻止用户去创造第二个`SystemTimer`,其指向同个外设!如果作者足够努力的话,他能发现所有这些'重复的'驱动实例,那么按这种方式写的代码就可以工作,但是一旦代码被散播一段时间,散播给多个模块,驱动,开发者,它会越来越容易触发此类错误。 diff --git a/typ/peripherals/borrowck.typ b/typ/peripherals/borrowck.typ index 7eea5a6c..0337fb1b 100644 --- a/typ/peripherals/borrowck.typ +++ b/typ/peripherals/borrowck.typ @@ -1,10 +1,10 @@ #import "../config.typ": * -#h1(offset: whole, tr(( - en: [Mutable Global State], +#h1((en: [Mutable Global State], de: [Veränderbarer globaler Zustand], + ja: [ミュータブルでグローバルな状態], zh: [可变的全局状态], -))) +), offset: whole) #tr(( en: [ @@ -19,6 +19,10 @@ de: [ Hardware existiert unabhängig von den Strukturen des Codes, den wir schreiben, und kann jederzeit durch die reale Welt verändert werden. ], +ja: [ + 残念ながら、ハードウェアは基本的にミュータブルでグローバルな状態に他なりません。これはRustの開発者にとって非常に恐ろしいことです。 + ハードウェアは書かれたコードの構造とは独立して存在しており、現実の世界からいつでも変更される可能性があります。 +], zh: [ 不幸的是,硬件本质上是个可变的全局状态,Rust开发者可能会对此感到很害怕。因为硬件独立于我们所写的代码的结构,能被真实世界在任何时候改变。 ])) @@ -26,18 +30,19 @@ zh: [ = #tr(( en: [What should our rules be?], de: [Wie sollten unsere Regeln sein?], + ja: [何をルールとするべきか?], zh: [我们应该遵循什么规则?], )) #tr(( en: [ -How can we reliably interact with these peripherals? -+ Always use `volatile` methods to read or write to peripheral memory, - as it can change at any time -+ In software, we should be able to share any number of read-only - accesses to these peripherals -+ If some software should have read-write access to a peripheral, it - should hold the only reference to that peripheral + How can we reliably interact with these peripherals? + + Always use `volatile` methods to read or write to peripheral memory, + as it can change at any time + + In software, we should be able to share any number of read-only + accesses to these peripherals + + If some software should have read-write access to a peripheral, it + should hold the only reference to that peripheral ], de: [ Wie können wir zuverlässig mit diesen Peripheriegeräten interagieren? @@ -49,6 +54,12 @@ de: [ haben soll, sollte sie die einzige Referenz auf dieses Peripheriegerät besitzen. ], +ja: [ + どうすればこれらのペリフェラルと確実にやり取りできるのでしょう? + + いつ変化するかわからないペリフェラルメモリの読み書きには、常に`volatile`メソッドを使用してください + + ソフトウェアでは、これらのペリフェラルへの読み取り専用アクセスをいくつでも共有できるでしょう + + あるソフトウェアがあるペリフェラルに読み書きのアクセスをするならば、そのソフトウェアは、ペリフェラルへの唯一の参照を持つべきです +], zh: [ 我们如何才能做到可靠地与这些外设交互? + 总是使用 `volatile` 方法去读或者写外设存储器。因为它随时会改变。 @@ -59,6 +70,7 @@ zh: [ = #tr(( en: [The Borrow Checker], de: [Der Borrow-Checker], + ja: [借用チェッカ], zh: [借用检查器], )) @@ -71,6 +83,9 @@ de: [ Die letzten beiden dieser Regeln klingen verdächtig ähnlich wie das, was der Borrow Checker bereits tut! ], +ja: [ + さきほどのルールの最後の2つは、借用チェッカが行っていることに怪しいくらいよく似ています。 +], zh: [ 这些规则最后两个听起来与借用检查器在做的事情很像! ])) @@ -85,6 +100,9 @@ de: [ Peripheriegeräten weitergeben oder unveränderliche bzw. veränderliche Referenzen darauf anbieten? ], +ja: [ + ペリフェラルの所有権を譲渡したり、ペリフェラルへのイミュータブルまたはミュータブルな参照を提供したりできるのか、を想像してみてください。 +], zh: [ 思考一下,我们是否可以传递这些外设的所有权,或者提供对它们的可变或者不可变的引用? ])) @@ -103,6 +121,10 @@ de: [ Peripheriegeräts, aber wie können wir dies in der Struktur unseres Codes sichtbar machen? ], +ja: [ + それは可能です。ただし、借用チェッカが正しくペリフェラルの所有権や参照を扱うためには、各ペリフェラルが持つインスタンスはただ1つにする必要があります。そうすれば、Rustはこれを正しく扱えます。 + 幸いなことにハードウェアにおいて、任意のペリフェラルのインスタンスは1つだけしかありません。しかし、どうすればそれをコードの構造として明確にできるでしょうか? +], zh: [ 我们当然可以,但是对于借用检查器来说,每个外设只有一个实例的话,Rust才可以正确地处理这件事。幸运的是,在硬件中,任何给定的外设,只有一个实例,但是我们该如何将它暴露在代码的结构中呢? ])) diff --git a/typ/peripherals/index.typ b/typ/peripherals/index.typ index 7e3af35f..7efd7908 100644 --- a/typ/peripherals/index.typ +++ b/typ/peripherals/index.typ @@ -1,15 +1,16 @@ #import "../config.typ": * -#h1(tr(( - en: [Peripherals], +#h1((en: [Peripherals], de: [Peripheriegeräte], + ja: [ペリフェラル], zh: [外设], -))) +)) = #tr(( en: [What are Peripherals?], de: [Was sind Peripheriegeräte?], + ja: [ペリフェラルとは何か?], zh: [什么是外设?], )) @@ -31,6 +32,9 @@ de: [ Display oder eine Tastatur verwendet werden. Diese Komponenten werden zusammenfassend als Peripheriegeräte bezeichnet. ], +ja: [ + ほとんどのマイクロコントローラはCPUやRAM、フラッシュメモリ以外のものを持っています。マイクロコントローラはシリコン上に専用のセクションを持っており、そのセクションは、マイクロコントローラの外のシステムとやり取りしたり、センサーやモーターコントローラ、またはディスプレイもしくはキーボードのようなヒューマンインタフェースによって世界中の周囲環境と直接的または間接的にやり取りするために使用されます。それらのコンポーネントはまとめてペリフェラルと呼ばれています。 +], zh: [ 大多数微处理器不仅仅有一个CPU,RAM,或者Flash存储器 - 它们还包含被用来与微处理器的外部系统进行交互的硅片部分,通过传感器,电机控制器,或者人机接口比如一个显示器或者键盘直接和间接地与周遭世界交互。这些组件统称为外设。 @@ -54,6 +58,9 @@ de: [ verlagern, sodass die CPU ihre Zeit mit anderen wichtigen Dingen verbringen oder gar nichts tun kann, um Strom zu sparen. ], +ja: [ + これらのペリフェラルは有用です。なぜならば、開発者はペリフェラルに処理をオフロードすることが可能になるため、全ての処理をソフトウェアで行う必要がなくなります。デスクトップ開発者がグラフィック処理をビデオカードにオフロードするのと同じように、組込み開発者は一部のタスクをペリフェラルにオフロードして、CPUの時間を他の重要なことに使ったり、電力を節約するために何もしないようにすることができます。 +], zh: [ 这些外设很有用,因为它们允许一个开发者将处理工作交给它们来做,避免了必须在软件中处理每件事。就像一个桌面开发者如何将图形处理工作让给一个显卡那样,嵌入式开发者能将一些任务让给外设去做,让CPU可以把时间放在做其它更重要的事上,或者为了省电啥事也不做。 ])) @@ -79,6 +86,13 @@ de: [ - Einen ROM-Chip - Einen Ein-/Ausgabe-Controller ], +ja: [ + 1970年代か1980年代の旧式の家庭用コンピュータのメイン回路基板を見れば(実際に、旧式のデスクトップPCは今日の組込みシステムとさほど違いません)、次のものを目にするはずです。 + - プロセッサ + - RAMチップ + - ROMチップ + - I/Oコントローラ +], zh: [ 如果你看向来自1970s或者1980s的旧型号的家庭电脑的主板(其实,昨日的桌面PCs与今日的嵌入式系统没太大区别),你将看到: - 一个处理器 @@ -107,6 +121,9 @@ de: [ gleichen Prinzipien -- nur dass alles auf einem einzigen Stück Silizium untergebracht ist. ], +ja: [ + RAMチップ、ROMチップ、I/Oコントローラ(このシステムのペリフェラル)は「バス」として知られる一連の並列な配線を通してプロセッサに接続されているでしょう。アドレスバスは、プロセッサがバス上のどのデバイスと通信したいかを選択するアドレス情報を運び、データバスは、実際のデータを運びます。組込みマイクロコントローラにおいても、同じ原理が適用されます。それは全てが1つのシリコン片に詰め込まれているということです。 +], zh: [ RAM芯片,ROM芯片和I/O控制器(这个系统中的外设)会通过一系列并行的迹(traces)又被称为一个"总线"被加进处理器中。地址总线搬运地址信息,其用来选择处理器希望跟总线上哪个设备通信,还有一个用来搬运实际数据的数据总线。在我们的嵌入式微控制器中,应用了相同的概念 \- 只是所有的东西被打包到一片硅片上。 @@ -124,6 +141,9 @@ de: [ Mikrocontroller über eine Hardwareschnittstelle zugänglich gemacht, die einem Teil des Speichers zugeordnet ist. ], +ja: [ + しかしながら、VulkanやMetal、OpenGLなどのソフトウェアのAPIを通常持つグラフィックカードとは異なり、ペリフェラルはメモリチャンクにマッピングされたハードウェアインターフェースとしてマイクロコントローラに公開されています。 +], zh: [ 然而,不像显卡,显卡通常有像是Vulkan,Metal,或者OpenGL这样的一个软件API。外设暴露给微控制器的是一个硬件接口,其被映射到一块存储区域。 ])) @@ -131,6 +151,7 @@ zh: [ = #tr(( en: [Linear and Real Memory Space], de: [Linearer und realer Speicherraum], + ja: [線形な実メモリ空間], zh: [线性的物理存储空间], )) @@ -145,6 +166,9 @@ de: [ beliebige andere Adresse -- etwa `0x4000_0000` oder `0x0000_0000` -- ein völlig zulässiger Vorgang sein. ], +ja: [ + マイクロコントローラでは、`0x4000_0000`や`0x0000_0000`のような任意のアドレスにデータを書き込むことは、完全に正当な行為でしょう。 +], zh: [ 在一个微控制器上,随便往一些地址写一些数据,比如 `0x4000_0000` 或者 `0x0000_0000`,可能也是一个完全有效的动作。 @@ -171,6 +195,9 @@ de: [ verfügen in der Regel nicht über eine MMU; stattdessen verwendet die Software dort ausschließlich reale physische Adressen. ], +ja: [ + デスクトップシステムでは、メモリアクセスはMMU(メモリ管理ユニット)によって厳密に制御されています。このコンポーネントは2つの主な役割を持っています。メモリのセクションへのアクセス権限の強制(あるプロセスが別のプロセスのメモリを読み出したり変更したりできないようにする)、そして物理メモリのセグメントをソフトウェアで使用される仮想メモリ範囲に再マッピングすることです。マイクロコントローラは通常はMMUを持たず、代わりにソフトウェアで物理アドレスのみを使用します。 +], zh: [ 在一个桌面系统上,访问内存被MMU,或者内存管理单元紧紧地控制着。这个组件有两个主要责任: 对部分内存加入访问权限(防止一个进程读取或者修改另一个进程的内存);重映射物理内存的段到软件中使用的虚拟内存范围上。微控制器通常没有一个MMU,反而在软件中只使用真实的物理地址。 @@ -215,6 +242,9 @@ de: [ Mikrocontroller die Schnittstellen für Peripheriegeräte bestimmten Speicheradressen zugeordnet. Das Ergebnis sieht dann ungefähr so ​​aus: ], +ja: [ + 32ビットマイクロコントローラは`0x0000_0000`から`0xFFFF_FFFF`の線形な実アドレス空間を持ちますが、それらは大抵の場合、実際のメモリのためにはその範囲の数百キロバイトしか使用しません。これにより、かなりの量のアドレス空間が残ります。前の章では、RAMが`0x2000_0000`のアドレスに配置されていることについて話しました。もしもRAMが64KiBの長さなら(すなわち、最大アドレスが0xFFFF)、`0x2000_0000`から`0x2000_FFFF`がRAMのアドレスに対応します。`0x2000_1234`のアドレスにある変数に書き込むと、内部ではアドレスの上位部分(この例では0x2000)を検出し、アドレスの下位部分(この例では0x1234)に作用できるようにRAMをアクティブにします。Cortex-Mにおいては、フラッシュROMも`0x0000_0000`から`0x0007_FFFF`のアドレスにマッピングされています(512KiBのフラッシュROMが載っている場合)。これら2つの領域の間に残るスペースを全て無視するのではなく、代わりにマイクロコントローラの設計者は特定のメモリ配置にペリフェラルのインターフェースをマッピングしました。これは次のようなものになります。 +], zh: [ 虽然32位微控制器有一个从`0x0000_0000`到`0xFFFF_FFFF`的线性的物理地址空间,但是它们通常只使用几百KiB的实际内存。有相当大部分的地址空间保留着。在早期的章节中,我们说到RAM被放置在地址`0x2000_0000`处。如果我们的RAM是64KiB大小(i.e.~最大地址为0xFFFF),那么地址 `0x2000_0000`到`0x2000_FFFF`与我们的RAM有关。当我们写入一个位于地址`0x2000_1234`的变量时,内部发生的是,一些逻辑发现了地址的上部(这个例子里是0x2000),然后激活RAM,以便能操作地址的下部(这个例子里是0x1234)。在一个Cortex-M上,我们也也会把Flash @@ -230,6 +260,7 @@ zh: [ = #tr(( en: [Memory Mapped Peripherals], de: [Im Speicher abgebildete Peripheriegeräte], + ja: [メモリマップド・ペリフェラル], zh: [存储映射的外设], )) @@ -249,6 +280,9 @@ de: [ an eine bestimmte Speicheradresse. Das Serial-Port-Peripheriegerät würde dann übernehmen und die Daten automatisch versenden. ], +ja: [ + 一見すると、これらのペリフェラルとのやり取りは簡単です。正しいデータを正しいアドレスに書き込むだけです。例えば、32ビットワードをシリアルポート上で送信することは、32ビットワードを特定のメモリアドレスに書き込むことと同じくらい直接的になり得ます。シリアルポート・ペリフェラルは自動的にデータを引き受けて送信します。 +], zh: [ 乍一看,与这些外设交互很简单 - 将正确的数据写入正确的地址。比如,在一个串行端口上发送一个32位字,可以直接把那个32位字写入某个存储地址。串行端口外设然后能自动获取和发出数据。 @@ -273,6 +307,9 @@ de: [ „0x0200_0000" an dieselbe Adresse und der SPI-Port sendet Daten mit 125 Kilobit pro Sekunde. Diese Konfigurationsregister sehen in etwa so aus: ], +ja: [ + これらのペリフェラルの設定についても同じように動作します。ペリフェラルの設定をするための関数を呼ぶ代わりに、ハードウェアAPIとして機能するメモリチャンクが公開されます。`0x8000_0000`をSPI周波数の設定レジスタに書き込むと、SPIポートは8Mbpsでデータを送信するようになります。`0x0200_0000`を同じアドレスに書き込むと、SPIは125Kbpsでデータを送信するようになります。これらの設定レジスタをちょっとだけ見てみます。 +], zh: [ 这些外设的配置工作相似。不是调用一个函数去配置一个外设,而是暴露一块地址空间作为硬件API。向一个SPI频率控制寄存器写入 `0x8000_0000`,SPI端口将会按照每秒8MB的速度发送数据。向同个地址写入 @@ -293,6 +330,9 @@ de: [ unabhängig davon, welche Sprache verwendet wird -- sei es Assembler, C oder Rust. ], +ja: [ + アセンブリ言語やC言語、Rustなど、どの言語が使われようとも、このインターフェースがどのように作用するかはハードウェアによって定められています。 +], zh: [ 这个接口是关于如何与硬件交互的,其与被使用的语言无关,无论这个语言是汇编,C,或者Rust。 ])) diff --git a/typ/peripherals/singletons.typ b/typ/peripherals/singletons.typ index 6f87db7c..21a626a7 100644 --- a/typ/peripherals/singletons.typ +++ b/typ/peripherals/singletons.typ @@ -1,10 +1,10 @@ #import "../config.typ": * -#h1(offset: whole, tr(( - en: [Singletons], +#h1((en: [Singletons], de: [Singletons], + ja: [シングルトン], zh: [单例], -))) +), offset: whole) #quote(block: true)[ #tr(( @@ -16,6 +16,9 @@ de: [ In der Softwaretechnik ist das Singleton-Muster ein Entwurfsmuster, das die Instanziierung einer Klasse auf ein einziges Objekt beschränkt. ], +ja: [ + ソフトウェア工学において、シングルトン・パターンはクラスのインスタンス化を1つのオブジェクトに制限するデザインパターンです。 +], zh: [ 在软件工程中,单例模式是一个软件设计模式,其限制了一个类到一个对象的实例化。 ])) @@ -28,6 +31,9 @@ en: [ de: [ _Wikipedia: #link(url_singleton)[Singleton-Muster]_ ], +ja: [ + _Wikipedia: #link(url_singleton)[Singleton Pattern]_ +], zh: [ _Wikipedia: #link(url_singleton)[Singleton Pattern]_ ])) @@ -36,6 +42,7 @@ zh: [ = #tr(( en: [But why can't we just use global variable(s)?], de: [Aber warum können wir nicht einfach globale Variable(n) verwenden?], + ja: [なぜグローバル変数は使えないのか?], zh: [为什么不可以使用全局变量?], )) @@ -46,6 +53,9 @@ en: [ de: [ Wir könnten alles als „public static" definieren, etwa so: ], +ja: [ + このように、全てをパブリックかつスタティックにすることができます。 +], zh: [ 可以像这样,我们可以使每个东西都变成公共静态的(public static): ])) @@ -74,6 +84,11 @@ de: [ sichtbar, was bedeutet, dass der Borrow Checker nicht dabei helfen kann, die Referenzen und die Ownership dieser Variablen zu verfolgen. ], +ja: [ + しかし、これにはいくつか問題があります。 + これはミュータブルなグローバル変数であり、Rustにおいては、これらとやり取りするのは常にアンセーフです。 + これらの変数はプログラムの全体を通して見えることになり、つまりそれは借用チェッカがこれらの変数の参照や所有権を追跡するのに役立たなくなることを意味します。 +], zh: [ 但是这个带来了一些问题。它是一个可变的全局变量,在Rust,与这些变量交互总是unsafe的。这些变量在你所有的程序间也是可见的,意味着借用检查器不能帮你跟踪这些变量的引用和所有权。 ])) @@ -81,6 +96,7 @@ zh: [ = #tr(( en: [How do we do this in Rust?], de: [Wie machen wir das in Rust?], + ja: [Rustではどうするか?], zh: [在Rust中要怎么做?], )) @@ -96,6 +112,9 @@ de: [ -- in diesem Fall mit dem Namen `PERIPHERALS` -- zu erstellen, die für jedes unserer Peripheriegeräte ein `Option` enthält. ], +ja: [ + 単にペリフェラルをグローバル変数にする代わりに、各ペリフェラル毎に`Option`を含む`PERIPHERALS`と呼ばれるグローバル変数を作ることにします。 +], zh: [ 与其只是让我们的外设变成一个全局变量,我们不如创造一个结构体,在这个例子里其被叫做 `PERIPHERALS`,这个全局变量对于我们的每个外设,它都有一个与之对应的 @@ -127,6 +146,10 @@ de: [ Peripheriekomponente zu erhalten. Wenn wir versuchen, `take_serial()` mehr als einmal aufzurufen, wird unser Code eine Panic auslösen! ], +ja: [ + この構造体によって、ペリフェラルの唯一のインスタンスが取得できるようになります。 + もしも`take_serial()`を複数回呼び出そうとすれば、コードはパニックするでしょう。 +], zh: [ 这个结构体允许我们获得一个外设的实例。如果我们尝试调用`take_serial()`获得多个实例,我们的代码将会抛出运行时恐慌(panic)! ])) @@ -137,6 +160,7 @@ zh: [ // " + ts(( en: "This panics!", de: "Dies loest eine panic aus!", + ja: "これはパニックします!", zh: "这里造成运行时恐慌!", )) + " // let serial_2 = unsafe { PERIPHERALS.take_serial() }; @@ -154,6 +178,9 @@ de: [ wir -- sobald wir über die darin enthaltene `SerialPort`-Instanz verfügen -- weder `unsafe`-Code noch die `PERIPHERALS`-Struktur selbst. ], +ja: [ + この構造体とのやり取りは`unsafe`にはなりますが、一度この構造体に含まれる`SerialPort`を取得してしまえばもう`unsafe`や`PERIPHERALS`構造体を使う必要は全くありません。 +], zh: [ 虽然与这个结构体交互是`unsafe`,然而一旦我们获得了它包含的 `SerialPort`,我们将不再需要使用`unsafe`,或者`PERIPHERALS`结构体。 @@ -173,6 +200,10 @@ de: [ ermöglicht es uns jedoch, den Borrow-Checker im weiteren Verlauf des Programms voll zu nutzen. ], +ja: [ + この方法では、オプション型の中に`SerialPort`構造体をラップし、`take_serial()`を一度コールする必要があるため、実行時に小さなオーバーヘッドとなります。 + しかし、この少々のコストを前払いすることで、残りのプログラムで借用チェッカを利用できるようになるのです。 +], zh: [ 这个带来了少量的运行时开销,因为我们必须打包 `SerialPort` 结构体进一个option中,且我们将需要调用一次 @@ -183,6 +214,7 @@ zh: [ = #tr(( en: [Existing library support], de: [Vorhandene Bibliotheksunterstützung], + ja: [既存のライブラリによるサポート], zh: [已存在的库支持], )) @@ -197,6 +229,10 @@ de: [ dies für Ihren Code nicht erforderlich; das `cortex_m`-Crate enthält ein Makro namens `singleton!()`, das diese Aufgabe für Sie übernimmt. ], +ja: [ + 上記のコードでは`Peripherals`構造体を作りましたが、あなたのコードでも同じようにする必要はありません。 + `cortex_m`クレートはこれと同様のことをしてくれる`singleton!()`と呼ばれるマクロを含んでいます。 +], zh: [ 虽然我们在上面生成了我们自己的 `Peripherals` 结构体,但这并不是必须的。`cortex_m` crate 包含一个被叫做 `singleton!()` @@ -210,6 +246,7 @@ fn main() { // " + ts(( en: "OK if `main` is executed only once", de: "In Ordnung, wenn `main` nur einmal ausgefuehrt wird.", + ja: "`main`が一度だけしか実行されなければOKです", zh: "OK 如果 `main` 只被执行一次", )) + " let x: &'static mut bool = @@ -221,7 +258,6 @@ fn main() { #tr(( en: link(url_sinmacro)[cortex_m docs], de: link(url_sinmacro)[cortex_m Dokumente], - zh: link(url_sinmacro)[cortex_m docs], )) #let ln_rtic = link("https://github.com/rtic-rs/cortex-m-rtic")[`cortex-m-rtic`] @@ -274,6 +310,7 @@ const APP: () = { = #tr(( en: [But why?], de: [Aber warum?], + ja: [しかしなぜ?], zh: [为什么?], )) @@ -286,6 +323,9 @@ de: [ Aber wie bewirken diese Singletons einen spürbaren Unterschied in der Funktionsweise unseres Rust-Codes? ], +ja: [ + しかし、これらのシングルトンがRustのコードの動作にどのような顕著な違いをもたらすのでしょうか? +], zh: [ 但是这些单例模式是如何使我们的Rust代码在工作方式上产生很大不同的? ])) @@ -298,6 +338,7 @@ zh: [ &self // " + ts(( en: "<------ This is really, really important", de: "<------ Das ist wirklich, wirklich wichtig.", + ja: "<------ これは本当に、本当に重要です。", zh: "<------ 这个真的真的很重要", )) + " ) -> u32 { @@ -323,6 +364,11 @@ de: [ - Um die Methode `read_speed()` aufzurufen, benötigen wir den Besitz einer `SerialPort`-Struktur oder eine Referenz darauf. ], +ja: [ + ここには2つの重要な要素があります。 + - シングルトンを使用しているため、`SerialPort`構造体の取得手段や場所は1つだけになります。 + - `read_speed()`メソッドを呼ぶためには、`SerialPort`構造体の所有権もしくは参照を持つ必要があります。 +], zh: [ 这里有两个重要因素: - 因为我们正在使用一个单例模式,所以我们只有一种方法或者地方去获得一个 @@ -345,6 +391,9 @@ de: [ Zeitpunkt mehrere veränderbare Referenzen auf dieselbe Hardware existieren! ], +ja: [ + これらの2つの要素をまとめると、借用チェッカを適切に満たしている場合のみハードウェアにアクセスできることを意味します。つまり、同じハードウェアに対して複数のミュータブルな参照を持つことはありません。 +], zh: [ 这两个因素放在一起意味着,只有当我们满足了借用检查器的条件时,我们才有可能访问硬件,也意味着在任何时候不可能存在多个对同一个硬件的可变引用(&mut)! ])) @@ -354,6 +403,7 @@ zh: [ // " + ts(( en: "missing reference to `self`! Won't work.", de: "Verweis auf `self` fehlt! Das wird nicht funktionieren.", + ja: "`self`への参照が見つかりません。これはうまく動きません。", zh: "缺少对`self`的引用!将不会工作。", )) + " // SerialPort::read_speed(); @@ -363,6 +413,7 @@ zh: [ // " + ts(( en: "you can only read what you have access to", de: "Sie koennen nur lesen, worauf Sie Zugriff haben", + ja: "アクセスできるものだけ読み出すことができます。", zh: "你只能读取你有权访问的内容", )) + " let _ = serial_1.read_speed(); @@ -372,6 +423,7 @@ zh: [ = #tr(( en: [Treat your hardware like data], de: [Behandle deine Hardware wie Daten], + ja: [ハードウェアをデータのように扱う], zh: [像对待数据一样对待硬件], )) @@ -386,6 +438,9 @@ de: [ lässt sich erkennen, ob eine Funktion oder Methode potenziell den Zustand der Hardware ändern könnte. Zum Beispiel: ], +ja: [ + 加えて、いくつかの参照はミュータブルで、またいくつかはイミュータブルなため、関数またはメソッドがハードウェアの状態を変更できるかどうかを確認することが可能になります。 +], zh: [ 另外,因为一些引用是可变的,一些是不可变的,就可以知道一个函数或者方法是否有能力修改硬件的状态。比如, ])) @@ -397,6 +452,9 @@ en: [ de: [ Dies darf Hardware-Einstellungen ändern: ], +ja: [ + この関数はハードウェアの設定を変更できます。 +], zh: [ 这个函数可以改变硬件的配置: ])) @@ -417,6 +475,9 @@ en: [ de: [ Das nicht: ], +ja: [ + このメソッドは変更できません。 +], zh: [ 这个不行: ])) @@ -443,6 +504,12 @@ de: [ Bare-Metal-Systeme jedoch als eine einzige Anwendung kompiliert wird, stellt dies üblicherweise keine Einschränkung dar. ], +ja: [ + + これにより、実行時ではなく*コンパイル時に*コードがハードウェアを変更するかどうかを強制できます。 + 注意点としては、通常この強制はひとつのアプリケーション内でのみ機能します。ベアメタルシステムにおいては、ソフトウェアはひとつのアプリケーションにコンパイルされるため、これは一般的には制約にはなりません。 + +], zh: [ 这允许我们在*编译时*而不是运行时强制代码是否应该或者不应该对硬件进行修改。要注意,这通常在只有一个应用的情况下起作用,但是对于裸机系统来说,我们的软件将被编译进一个单一应用中,因此这通常不是一个限制。 ])) diff --git a/typ/portability/index.typ b/typ/portability/index.typ index 5af0ca83..061365d5 100644 --- a/typ/portability/index.typ +++ b/typ/portability/index.typ @@ -1,10 +1,10 @@ #import "../config.typ": * -#h1(tr(( - en: [Portability], +#h1((en: [Portability], de: [Portabilität], + ja: [移植性], zh: [可移植性], -))) +)) #tr(( @@ -21,6 +21,10 @@ de: [ und dementsprechend variieren auch die Interaktionsmöglichkeiten mit den Peripheriegeräten. ], +ja: [ + 組込み環境においては、移植性は非常に重要なトピックです。1つのメーカから、それぞれのベンダや各プロダクトファミリが、異なるペリフェラルや機能を提供します、 + 異なるペリフェラルでは、ペリフェラルとやり取りする方法も異なります。 +], zh: [ 在嵌入式环境中,可移植性是一个非常重要的主题: 每个供应商甚至同个制造商的不同系列间,都提供了不同的外设和功能。同样地,与外设交互的方式也将会不一样。 @@ -35,6 +39,9 @@ de: [ Eine gängige Methode, diese Unterschiede auszugleichen, ist die sogenannte Hardware-Abstraktionsschicht (HAL). ], +ja: [ + このような違いを吸収する一般的な方法は、ハードウェア抽象化レイヤまたは*HAL*と呼ばれるレイヤを導入することです。 +], zh: [ 通过一个被叫做硬件抽象层或者*HAL*的层去均等化这种差异是一种常见的方法。 ])) @@ -51,6 +58,9 @@ de: [ bestimmte plattformspezifische Details emulieren und Programmen so direkten Zugriff auf die Hardwareressourcen ermöglichen. ], +ja: [ + ハードウェア抽象化は、プラットフォーム固有の細部をエミュレーションして、プログラムにハードウェアリソースへ直接アクセスする方法を提供する、ソフトウェアの一連のルーチンです。 +], zh: [ 在软件中硬件抽象是一组函数,其模仿了一些平台特定的细节,让程序可以直接访问硬件资源。 ])) @@ -66,6 +76,9 @@ de: [ Hochleistungsanwendungen zu schreiben, indem sie standardisierte Betriebssystemaufrufe für den Hardwarezugriff bereitstellen. ], +ja: [ + それらのルーチンがハードウェアへのオペレーティングシステム(OS)コールを提供することで、プログラマは、デイバスに依存せず、高性能なアプリケーションを書くことができます。 +], zh: [ 通过向硬件提供标准的操作系统(OS)调用,它可以让程序员编写独立于设备的高性能应用。 ])) @@ -74,6 +87,7 @@ zh: [ #tr(( en: [_Wikipedia: #link(url_hal)[Hardware Abstraction Layer]_], de: [_Wikipedia: #link(url_hal)[Hardware Abstraction Layer]_], + ja: [_Wikipedia: #link(url_hal)[ハードウェア抽象化レイヤ]_], zh: [_Wikipedia: #link(url_hal)[Hardware Abstraction Layer]_], )) ] @@ -97,6 +111,11 @@ de: [ wahrscheinlich nicht der effizienteste Weg, um Portabilität zu gewährleisten. ], +ja: [ + 組込みシステムは、通常オペレーティングシステムを使わず、ユーザがインストールできるソフトウェアもありません。 + 全体として1つにコンパイルされたファームウェアイメージであり、様々な制約を持つ、という点が特殊です。 + そのため、Wikipediaで定義されている従来のアプローチでもうまく機能する可能性はありますが、移植性を確保するための最も有効なアプローチではない可能性があります。 +], zh: [ 在这方面,嵌入式系统有点特别,因为通常没有操作系统和用户可安装的软件,而只有固件镜像,其作为一个整体被编译且伴着许多约束。因此虽然维基百科定义的传统方法可能有用,但是它不是确保可移植性最有效的方法。 ])) @@ -109,6 +128,9 @@ en: [ de: [ Wie gehen wir dabei in Rust vor? Hier kommt *#ln_hal* ins Spiel… ], +ja: [ + Rustではどうするのでしょうか?*embedded-hal*を見ていきましょう。 +], zh: [ 在Rust中我们要怎么实现这个目标呢?让我们进入*embedded-hal*… ])) @@ -116,6 +138,7 @@ zh: [ == #tr(( en: [What is #ln_hal?], de: [Was ist #ln_hal?], + ja: [embedded-halとは?], zh: [什么是embedded-hal?], )) @@ -140,6 +163,10 @@ de: [ werden kann, ist garantiert, dass die im Merkmal spezifizierten Methoden verfügbar sind). ], +ja: [ + 一言で言えば、*HAL実装*、*ドライバ*、*アプリケーションまたはファームウェア*間の実装規約を定義するトレイトの集まりのことです。 + それらの規約は、機能(ある型にトレイトが実装されていれば、*HAL実装*はそのトレイトの機能を提供します)とメソッド(あるトレイトを実装している型のオブジェクトを作成できれば、そのトレイトに含まれるメソッドが利用可能であることが保証されます)を含んでいます。 +], zh: [ 简而言之,它是一组traits,其定义了*HAL implementations*,*驱动*,*应用(或者固件)* @@ -155,6 +182,9 @@ en: [ de: [ Ein typischer Schichtaufbau könnte wie folgt aussehen: ], +ja: [ + 典型的なレイヤ構造は、次のようになります。 +], zh: [ 典型的分层可能如下所示: ])) @@ -180,6 +210,15 @@ de: [ - Timers/Countdowns - Analog-Digital-Umsetzung ], +ja: [ + *embedded-hal*で定義されているいくつかのトレイトを示します。 + - GPIO(入出力ピン) + - シリアル通信 + - I2C + - SPI + - タイマ/カウントダウン + - アナログデジタル変換 +], zh: [ 一些在*embedded-hal*中被定义的traits: - GPIO (input and output pins) @@ -223,6 +262,14 @@ de: [ Vorteile, wie etwa ein geringerer Aufwand durch „Trial-and-Error" dank wohldefinierter und sofort einsatzbereiter APIs. ], +ja: [ + *embedded-hal*トレイトと、それらを実装して使用するクレートを持つ主な理由は、複雑さを抑えることです。 + アプリケーションがハードウェアのペリフェラルを使うコードだけでなく、追加するハードウェアコンポーネントのドライバを実装しなければならない場合を考えると、再利用性は非常に制限されます。 + 数学的に表現すると、*M*がペリフェラルHAL実装の数で、*N*がドライバの数とするならば、全てのアプリケーションで車輪の再発明を行うことになります。 + その結果、*M\*N*の実装が必要になります。 + 一方、*embedded-hal*トレイトで提供されるAPIを使うことで、実装の複雑さは*M+N*になるでしょう。 + もちろん、明確に定義されたすぐに利用可能なAPIによって試行錯誤を減らすなど、他にも利点があります。 +], zh: [ 使用*embedded-hal* traits和依赖*embedded-hal*的crates的主要原因是为了控制复杂性。如果发现一个应用可能必须要实现对硬件外设的使用,以及需要实现应用程序和其它硬件组件间潜在的驱动,那么其应该很容易被看作是可复用性有限的。用数学语言来说就是,如果*M*是外设HAL @@ -234,6 +281,7 @@ zh: [ == #tr(( en: [Users of the #ln_hal], de: [Nutzer von #ln_hal], + ja: [embedded-halのユーザ], zh: [embedded-hal的用户], )) @@ -244,6 +292,9 @@ en: [ de: [ Wie bereits erwähnt, gibt es drei Hauptnutzer einer HAL: ], +ja: [ + 上記のように、HALには主に3つのユーザがいます。 +], zh: [ 像上面所说的,HAL有三个主要用户: ])) @@ -251,6 +302,7 @@ zh: [ === #tr(( en: [HAL implementation], de: [HAL-Implementierung], + ja: [HAL実装], zh: [HAL implementation], )) @@ -276,6 +328,12 @@ de: [ - eine oder mehrere `trait`-Implementierungen (`impl`) von *#ln_hal*-Traits für diesen Typ ], +ja: [ + HAL実装は、ハードウェアとHALトレイトのユーザとの間のインタフェースを実装します。典型的な実装では、3つの部分から構成されます。 + - 1つ以上のハードウェア固有の型 + - そのような型のオブジェクトを作成し、初期化する関数。多くの場合、様々な設定オプションを提供しています(速度、動作モード、使用ピンなど) + - 1つ以上のその型に対する*embedded-hal*の`trait` `impl` +], zh: [ HAL implentation提供硬件和HAL traits的用户之间的接口。典型的实现由三部分组成: @@ -301,6 +359,13 @@ de: [ - Über einen Adapter, z. B. ein Mock von Typen für Unit-Tests - Via-Treiber für Hardware-Adapter, z. B. I2C-Multiplexer oder GPIO-Expander ], +ja: [ + このような*HAL実装*は、様々な種類があります。 + - 低レベルのハードウェアアクセスによるもの、例えばレジスタ + - オペレーティングシステムによるもの、例えばLinuxの`sysfs`の使用 + - アダプタによるもの、例えばユニットテストのための型のモック + - ハードウェアアダプタ用のドライバによるもの、例えばI2CマルチプレクサやGPIOエキスパンダ +], zh: [ 这样的一个 *HAL implementation* 可以有多个方法来实现: - 通过低级硬件访问,比如通过寄存器。 @@ -312,6 +377,7 @@ zh: [ === #tr(( en: [Driver], de: [Treiber], + ja: [ドライバ], zh: [驱动], )) @@ -333,6 +399,10 @@ de: [ Lichtsensoren), Anzeigegeräte (LED-Arrays, LCD-Displays) und Aktoren (Motoren, Sender). ], +ja: [ + ドライバは、embedded-halトレイトを実装しているペリフェラルに接続されている、内部または外部コンポーネントに対するカスタム機能を実装します。 + そのようなドライバの典型的な例は、様々なセンサ(温度、磁気、加速度、輝度)、ディスプレイデバイス(LEDアレイ、LCDディスプレイ)や、アクチュエータ(モータ、トランスミッタ)を含みます。 +], zh: [ 驱动为一个外部或者内部组件实现了一组自定义的功能,被连接到一个实现了embedded-hal traits的外设上。这种驱动的典型的例子包括多种传感器(温度计,磁力计,加速度计,光照计),显示设备(LED阵列,LCD显示屏)和执行器(电机,发送器)。 @@ -353,6 +423,10 @@ de: [ stellt eine eigene Typinstanz mit einem benutzerdefinierten Satz von Methoden bereit, die die Interaktion mit dem angesteuerten Gerät ], +ja: [ + ドライバは、embedded-halの特定のトレイトを実装する型のインスタンスと共に初期化する必要があります。 + そのトレイトは、トレイト境界により保証され、駆動するデバイスとやり取りできるインスタンスをカスタムメソッドと共に提供します。 +], zh: [ 必须使用实现了embedded-hal的某个`trait`的类型的实例来初始化驱动,这是通过trait bound来确保的,驱动也提供了它自己的类型实例,这个实例具有一组自定义的方法,这些方法允许与被驱动的设备交互。 @@ -362,6 +436,7 @@ zh: [ === #tr(( en: [Application], de: [Anwendung], + ja: [アプリケーション], zh: [应用], )) @@ -394,6 +469,13 @@ de: [ mit unterschiedlichen Fähigkeiten zur Verfügung stehen oder Konflikte zwischen verschiedenen Peripherieeinheiten auftreten). ], +ja: [ + アプリケーションは、様々な部品を結合し、必要な機能が確実に実現できるようにします。 + 別システムに移植する際、アプリケーションは最も適合作業が求められる部分です。アプリケーションは、HAL実装を通じて、実際のハードウェアを初期化する必要があるからです。 + 異なるハードウェアの初期化は、極端に異なる場合があります。 + ユーザの選択は、多くの場合、大きな影響があります。 + コンポーネントが別の端子に物理的に接続されたり、ハードウェアバスが機器構成を満たすために外部ハードウェアを必要とする場合があったり、内部ペリフェラルを使えるようにするための異なるトレードオフがあったりします。例えば、異なる機能を持つ複数のタイマが利用可能であること、や、ペリフェラルが他のペリフェラルと競合すること、です。 +], zh: [ 应用把多个部分结合在一起并确保需要的功能被实现。当在不同的系统间移植时,这部分的适配是花费最多精力的地方,因为应用需要通过HAL implementation正确地初始化真实的硬件,而且不同硬件的初始化也不相同,甚至有时候差别非常大。用户的选择也在其中扮演了非常重大的角色,因为组件能被物理连接到不同的端口,硬件总线有时候需要外部硬件去匹配配置,或者用户在内部外设的使用上有不同的考量。 diff --git a/typ/start/exceptions.typ b/typ/start/exceptions.typ index b59eb535..b834be37 100644 --- a/typ/start/exceptions.typ +++ b/typ/start/exceptions.typ @@ -1,11 +1,10 @@ #import "../config.typ": * -#h1(offset: whole, tr(( - en: [Exceptions], +#h1((en: [Exceptions], de: [Ausnahmen (Exceptions)], ja: [例外], zh: [异常], -))) +), offset: whole) #tr(( diff --git a/typ/start/hardware.typ b/typ/start/hardware.typ index 81a2fe53..92216d3c 100644 --- a/typ/start/hardware.typ +++ b/typ/start/hardware.typ @@ -1,11 +1,10 @@ #import "../config.typ": * -#h1(tr(( - en: [Hardware], +#h1((en: [Hardware], de: [Hardware], ja: [ハードウェア], zh: [硬件], -))) +)) #tr(( @@ -301,7 +300,7 @@ zh: [ fn main() -> ! { hprintln!(\"Hello, world!\").unwrap(); - // " + ( + // " + ts(( en: "exit QEMU // NOTE do not run this on hardware; it can corrupt OpenOCD state // debug::exit(debug::EXIT_SUCCESS);", @@ -314,7 +313,7 @@ fn main() -> ! { // debug::exit(debug::EXIT_SUCCESS);", zh: "退出 QEMU // 注意 不要在硬件上运行这个;它会打破OpenOCD的状态", - ).at(lang, default: todos) + " + )) + " loop {} } diff --git a/typ/start/index.typ b/typ/start/index.typ index 15416de0..a6e039e0 100644 --- a/typ/start/index.typ +++ b/typ/start/index.typ @@ -1,11 +1,10 @@ #import "../config.typ": * -#h1(tr(( - en: [Getting Started], +#h1((en: [Getting Started], de: [Erste Schritte], ja: [入門], zh: [开始], -))) +)) #let ln_f3 = link("http://www.st.com/en/evaluation-tools/stm32f3discovery.html")[STM32F3DISCOVERY] diff --git a/typ/start/interrupts.typ b/typ/start/interrupts.typ index f9ff5563..9b90475a 100644 --- a/typ/start/interrupts.typ +++ b/typ/start/interrupts.typ @@ -1,11 +1,10 @@ #import "../config.typ": * -#h1(offset: whole, tr(( - en: [Interrupts], +#h1((en: [Interrupts], de: [Interrupts], ja: [割り込み], zh: [中断], -))) +), offset: whole) #tr(( en: [ diff --git a/typ/start/panicking.typ b/typ/start/panicking.typ index 5270f3af..f0c31b78 100644 --- a/typ/start/panicking.typ +++ b/typ/start/panicking.typ @@ -1,11 +1,10 @@ #import "../config.typ": * -#h1(offset: whole, tr(( - en: [Panicking], +#h1((en: [Panicking], de: [In Panik geraten], ja: [パニック], zh: [运行时恐慌(Panicking)], -))) +), offset: whole) #tr(( @@ -270,6 +269,7 @@ zh: [ = #tr(( en: [An example], de: [Ein Beispiel], + ja: [例], zh: [一个例子], )) @@ -282,6 +282,9 @@ de: [ Hier ist ein Beispiel, das versucht, auf ein Array an einer Position zuzugreifen, die über dessen Länge hinausgeht. Der Vorgang führt zu einer Panic. ], +ja: [ + 配列の長さを超えてアクセスしようとする例を示します。この操作はパニックを引き起こします。 +], zh: [ 这里有一个尝试越界访问数组的例子。操作的结果导致了一个运行时恐慌(panic)。 ])) @@ -301,6 +304,7 @@ fn main() -> ! { let _y = xs[i]; // " + ts(( en: "out of bounds access", de: "Zugriff ausserhalb der zulaessigen Grenzen", + ja: "範囲外アクセス", zh: "out of bounds access" )) + " @@ -317,6 +321,10 @@ de: [ Für dieses Beispiel wurde das Verhalten `panic-semihosting` gewählt, das die Panic-Meldung mittels Semihosting auf der Host-Konsole ausgibt. ], +ja: [ + この例では、`panic-semihosting`の挙動を選択しており、パニックメッセージは、 + セミホスティングを使ってホストコンソールに出力されます。 +], zh: [ 这个例子选择了`panic-semihosting`行为,运行时恐慌的信息会被打印至使用了半主机模式的主机控制台上。 ])) @@ -336,6 +344,9 @@ de: [ Sie können versuchen, das Verhalten auf `panic-halt` zu ändern, und bestätigen, dass in diesem Fall keine Meldung ausgegeben wird. ], +ja: [ + 挙動を`panic-halt`に変更し、その場合にメッセージが出力されないことを確認することができます。 +], zh: [ 你可以尝试将行为改成`panic-halt`,确保在这个案例里没有信息被打印。 ])) diff --git a/typ/start/qemu.typ b/typ/start/qemu.typ index e2fc5c94..cfcd3c26 100644 --- a/typ/start/qemu.typ +++ b/typ/start/qemu.typ @@ -1,7 +1,7 @@ #import "../config.typ": * -#h1(offset: whole, - [QEMU]) +#h1([QEMU], + offset: whole) // TODO: check @@ -45,11 +45,11 @@ (Stand 2026). ] -#let lm3s6965 = link("http://www.ti.com/product/LM3S6965")[LM3S6965] +#let ln_lm3s6965 = link("http://www.ti.com/product/LM3S6965")[LM3S6965] #let url_qemu = "https://wiki.qemu.org/Documentation/Platforms/ARM#Supported_in_qemu-system-arm" #tr(( en: [ - We'll start writing a program for the #lm3s6965, a Cortex-M3 + We'll start writing a program for the #ln_lm3s6965, a Cortex-M3 microcontroller. We have chosen this as our initial target because it #link(url_qemu)[can be emulated] using QEMU so you don't need to fiddle with hardware in this section and @@ -57,7 +57,7 @@ en: [ ], de: [ Wir beginnen mit der Entwicklung eines Programms für den - #lm3s6965, einen ARM + #ln_lm3s6965, einen ARM Cortex-M3-Mikrocontroller. Wir haben diesen als unser erstes Ziel ausgewählt, da er mit QEMU #link(url_qemu)[emuliert werden kann], @@ -65,8 +65,13 @@ de: [ müssen und wir uns auf die Werkzeuge und den Entwicklungsprozess konzentrieren können. ], +ja: [ + Cortex-M3マイクロコントローラの#ln_lm3s6965;用にプログラムを書くところから始めましょう。 + このLM3S6965を最初のターゲットとして選んだ理由は、QEMUを使ってエミュレーションできるからです。 + このセクションでは、ハードウェアをいじる必要がなく、ツールと開発プロセスに集中できます。 +], zh: [ - 我们将开始为#lm3s6965;编写程序,一个Cortex-M3微控制器。因为它能使用#link("https://wiki.qemu.org/Documentation/Platforms/ARM#Supported_in_qemu-system-arm")[QEMU仿真],所以我们选择它作为我们的第一个目标,本节中,不需要使用硬件,我们注意力可以集中在工具和开发过程上。 + 我们将开始为#ln_lm3s6965;编写程序,一个Cortex-M3微控制器。因为它能使用#link("https://wiki.qemu.org/Documentation/Platforms/ARM#Supported_in_qemu-system-arm")[QEMU仿真],所以我们选择它作为我们的第一个目标,本节中,不需要使用硬件,我们注意力可以集中在工具和开发过程上。 ])) #tr(( @@ -91,6 +96,7 @@ zh: [ = #tr(( en: [Creating a non standard Rust program], de: [Erstellen eines nicht standardmäßigen Rust-Programms], + ja: [標準ライブラリを使わないRustプログラム], zh: [生成一个非标准的 Rust program], )) @@ -294,7 +300,7 @@ fn main() -> ! { // " + ts(( en: "your code goes here", de: "Hier kommt Ihr Code hin.", - zh: "your code goes here", + ja: "あなたのコードはここに書きます", )) + " } } @@ -1086,6 +1092,7 @@ head -n3 .cargo/config.toml # " + ts(( en: "uncomment this to make `cargo run` execute programs on QEMU", de: "Entferne den Kommentar hier, damit `cargo run` Programme auf QEMU ausfuehrt", + ja: "`cargo run`で、プログラムをQEMUで実行するため、コメントアウトを外して下さい。", )) + " runner = \"qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel\" ") @@ -1101,6 +1108,10 @@ de: [ Standard-Kompilierungsziel ist. Nun kompiliert `cargo run` das Programm und führt es auf QEMU aus: ], +ja: [ + このランナーは、デフォルトのコンパイルターゲットである`thumbv7m-none-eabi`のみに適用されます。 + これで、`cargo run`はプログラムをコンパイルしてQEMUで実行します。 +], zh: [ 这个运行器只会应用于 `thumbv7m-none-eabi` 目标,它是我们的默认编译目标。现在 `cargo run` @@ -1123,6 +1134,7 @@ Hello, world! = #tr(( en: [Debugging], de: [Fehlerbehebung (Debugging)], + ja: [デバッグ], zh: [调试], )) @@ -1134,6 +1146,9 @@ de: [ Das Debuggen ist für die Embedded-Entwicklung von entscheidender Bedeutung. Schauen wir uns einmal an, wie es funktioniert. ], +ja: [ + デバッグは組込み開発にとって非常に重要です。どのように行うのか、見てみましょう。 +], zh: [ 对于嵌入式开发来说,调试非常重要。让我们来看下如何调试它。 ])) @@ -1149,6 +1164,10 @@ de: [ Programm, das wir debuggen möchten, nicht auf dem Rechner läuft, auf dem das Debugger-Programm (GDB oder LLDB) ausgeführt wird. ], +ja: [ + 組込みデバイスのデバッグは、_リモート_デバッグを伴います。デバッグしたいプログラムは、 + デバッガプログラム(GDBまたはLLDB)を実行しているマシン上で実行されないためです。 +], zh: [ 因为我们想要调试的程序所运行的机器上并没有运行一个调试器程序(GDB或者LLDB),所以调试一个嵌入式设备就涉及到了 _远程_ 调试 @@ -1165,6 +1184,10 @@ de: [ QEMU-Umgebung ist der Client ein GDB- (oder LLDB-)Prozess und der Server der QEMU-Prozess, auf dem auch das eingebettete Programm läuft. ], +ja: [ + リモートデバッグは、クライアントとサーバからなります。QEMUのセットアップで、 + クライアントはGDB(またはLLDB)プロセスとなり、サーバは組込みプログラムを実行しているQEMUプロセスとなります。 +], zh: [ 远程调试涉及一个客户端和一个服务器。在QEMU的情况中,客户端将是一个GDB(或者LLDM)进程且服务器将会是运行着嵌入式程序的QEMU进程。 @@ -1177,6 +1200,9 @@ en: [ de: [ In diesem Abschnitt verwenden wir das bereits kompilierte Beispiel `hello`. ], +ja: [ + このセクションでは、コンパイル済みの`hello`の例を使用します。 +], zh: [ 在这部分,我们要使用我们已经编译的 `hello` 示例。 ])) @@ -1188,6 +1214,9 @@ en: [ de: [ Der erste Schritt beim Debuggen besteht darin, QEMU im Debugging-Modus zu starten: ], +ja: [ + 最初のデバッグステップは、QEMUをデバッグモードで起動することです。 +], zh: [ 调试的第一步是在调试模式中启动QEMU: ])) @@ -1223,6 +1252,13 @@ de: [ Funktion `main` erreicht, bevor wir die Gelegenheit gehabt hätten, den Debugger zu starten! ], +ja: [ + このコマンドは、コンソールに何も表示せず、端末をブロックします。 + ここでは2つの追加フラグを渡しています。 + - `-gdb tcp::3333`。QEMUがTCPポート3333番で、GDBコネクションを待つようにします。 + - `-S`。QEMUが、起動時に、マシンをフリーズします。このフラグがないと、 + デバッガを起動する前に、プログラムがmain関数の終わりに到達してしまいます。 +], zh: [ 这个命令将不打印任何东西到调试台上,且将会阻塞住终端。此刻我们还传递了两个额外的标志。 - `-gdb tcp::3333`。这告诉QEMU在3333的TCP端口上等待一个GDB连接。 @@ -1238,6 +1274,9 @@ de: [ Als Nächstes starten wir GDB in einem anderen Terminal und weisen es an, die Debug-Symbole des Beispiels zu laden: ], +ja: [ + 次に別の端末でGDBを起動し、`hello`の例のデバッグシンボルをロードします。 +], zh: [ 接下来我们在另一个终端启动GDB,且告诉它去加载示例的调试符号。 ])) @@ -1434,6 +1473,10 @@ de: [ Wir sind nun fast bei dem Code angelangt, der „Hello, world!" ausgibt. Machen wir weiter mit dem Befehl `next`. ], +ja: [ + 「Hello, world!」を表示するコードに近づいてきました。 + `next`コマンドを使って、先へ進みましょう。 +], zh: [ 我们现在靠近打印"Hello, world!"的代码。让我们使用`next`命令继续前进。 ])) @@ -1455,6 +1498,10 @@ de: [ An dieser Stelle sollte auf dem Terminal, auf dem `qemu-system-arm` läuft, „Hello, world!" angezeigt werden. ], +ja: [ + この時点で、`qemu-system-arm`を実行している端末に「Hello, + world」が表示されるはずです。 +], zh: [ 在这里,你应该看到 "Hello, world!" 被打印到正在运行 `qemu-system-arm` 的终端上。 ])) @@ -1471,6 +1518,9 @@ en: [ de: [ Ein erneuter Aufruf von `next` beendet den QEMU-Prozess. ], +ja: [ + もう1度`next`を実行すると、QEMUプロセスが終了します。 +], zh: [ 再次调用`next`将会终止QEMU进程。 ])) @@ -1490,6 +1540,9 @@ en: [ de: [ Sie können die GDB-Sitzung nun beenden. ], +ja: [ + これでGDBセッションを終了できます。 +], zh: [ 你现在能退出GDB的会话了。 ])) diff --git a/typ/start/registers.typ b/typ/start/registers.typ index ffd59c88..1dad1b72 100644 --- a/typ/start/registers.typ +++ b/typ/start/registers.typ @@ -1,10 +1,10 @@ #import "../config.typ": * -#h1(offset: whole, tr(( - en: [Memory Mapped Registers], +#h1((en: [Memory Mapped Registers], de: [Im Speicher abgebildete Register], + ja: [メモリマップドレジスタ], zh: [存储映射的寄存器(Memory-Mapped Registers)], -))) +), offset: whole, ) #tr(( @@ -26,6 +26,11 @@ de: [ Welt der Peripherieeinheiten und deren „im Speicher abgebildeten Registern" (memory-mapped registers) befassen. ], +ja: [ + 組込みシステムでは、通常のRustコードを実行し、データをRAM内で移動させるだけではたいしたことはできません。 + LEDの点滅やボタンの押下検出、もしくは、バス上のオフチップペリフェラルとの通信など、 + システムが情報を入出力するには、ペリフェラルとその「メモリマップドレジスタ」の世界に足を踏み入れる必要があります。 +], zh: [ 嵌入式系统想要继续执行下去,只有通过执行常规的Rust代码并在RAM间移动数据才行。如果我们想要获取或者发出信息(点亮一个LED,发现一个按钮按下或者在总线上与芯片外设通信),我们不得不深入了解外设和它们的"存储映射的寄存器"。 ])) @@ -70,6 +75,23 @@ en: [ developer kit or board you are using, such as #ln_f3 for the STM32F3DISCOVERY board. ], +ja: [ + マイクロコントローラのペリフェラルにアクセスするためのコードが、次のいずれかのレベルで、既に書かれています。 + - マイクロアーキテクチャクレート。この種のクレートは、マイクロコントローラに搭載されているプロセッサコアで共通となる便利なルーチンを扱っています。 + また、特定のプロセッサコアを使用する全てのマイクロコントローラに共通のペリフェラルも取り扱います。 + 例えば、#ln_cortex;クレートは、割り込みの有効化と無効化を行う関数を提供しています。これは全てのCortex-Mベースマイクロコントローラで同じものです。 + #ln_cortex;クレートは、「SysTick」ペリフェラルへのアクセスも提供しています。このペリフェラルは、全てのCortex-Mベースマイクロコントローラに搭載されています。 + - ペリフェラルアクセスクレート(PAC)。この種のクレートは、薄いラッパーです。特定の型番のマイクロコントローラに対して定義されている、 + 様々なメモリマップドレジスタのラッパーを提供します。例えば、テキサスインスツルメンツのTiva-C + TM4C123シリーズ向けの#ln_tm4c123x;クレートや、 + STマイクロのSTM32F30xシリーズ向けの#ln_f30x;クレートです。マイクロコントローラのテクニカルリファレンスマニュアルに記載されている各ペリフェラルの操作手順に従って、 + レジスタと直接やり取りします。 + - HALクレート。これらのクレートは、特定のプロセッサに対して、よりユーザフレンドリなAPIを提供しています。#ln_hal;で定義されている共通のトレイトを使って実装されていることが多いです。 + 例えば、このクレートは、`Serial`構造体を提供しているでしょう。そのコンストラクタは、適切なGPIOピンの一式とボーレートを引数に取ります。そして、データを送信するための`write_byte`関数一式を提供します。 + #ln_hal;に関する詳細は、#link()[移植性]の章を参照して下さい。 + - ボードクレート。これらのクレートは、HALクレートのさらに一歩先を進んでいます。これらは、STM32F3DISCOVERYボード向けの#ln_f3;のように、 + 特定の開発キットやボード向けに、様々なペリフェラルとGPIOピンを事前に設定してあります。 +], de: [ Möglicherweise stellen Sie fest, dass der für den Zugriff auf die Peripherie Ihres Mikrocontrollers erforderliche Code bereits auf einer @@ -212,13 +234,17 @@ de: [ Cortex-M-Basis gemeinsam ist. Im #ln_cortex;-Crate finden wir eine recht hardwarenahe API, die sich folgendermaßen verwenden lässt: ], +ja: [ + 全てのCortex-Mマイクロコントローラで共通のSysTickペリフェラルから見ていきましょう。 + #ln_cortex;クレートにはかなり低レベルなAPIがあり、次のように使うことができます。 +], zh: [ 让我们看一下SysTick外设,SysTick外设存在于所有的Cortex-M微控制器中。我们能在#ln_cortex crate中找到一个相当底层的API,我们能像这样使用它: ])) -```rust -#![no_std] +#raw(block: true, lang: "rust", +"#![no_std] #![no_main] use cortex_m::peripheral::{syst, Peripherals}; use cortex_m_rt::entry; @@ -233,12 +259,15 @@ fn main() -> ! { systick.clear_current(); systick.enable_counter(); while !systick.has_wrapped() { - // Loop + // " + ts(( + en: "Loop", + ja: "ループ", + )) + " } loop {} } -``` +") #tr(( en: [ @@ -264,6 +293,12 @@ de: [ existiert. Weitere Informationen dazu finden Sie im Abschnitt "#link()[Peripherien]". ], +ja: [ + `SYST`構造体の関数は、ARMテクニカルリファレンスマニュアルにおいて、このペリフェラルに定義されている機能と非常によく似ています。 + 「Xミリ秒遅延」といった具合のAPIはありません。`while`ループを使って愚直に実装する必要があります。`Peripherals::take()`を呼び出すまでは、 + `SYST`構造体にアクセスできないことに注意して下さい。これは、プログラム全体で唯一の`SYST`構造体が存在することを保証する特別な手順です。 + 詳しくは、#link()[ペリフェラル]セクションをご覧下さい。 +], zh: [ `SYST`结构体上的功能,相当接近ARM技术手册为这个外设定义的功能。在这个API中没有关于 '延迟X毫秒' 的功能 - 我们不得不通过使用一个 `while` @@ -274,6 +309,7 @@ zh: [ = #tr(( en: [Using a Peripheral Access Crate (PAC)], de: [Verwendung eines Peripheral Access Crate (PAC)], + ja: [ペリフェラルアクセスクレート(PAC)の使用], zh: [使用一个外设访问Crate (PAC)], )) @@ -297,6 +333,13 @@ de: [ Flash-Speicher. Um diesen Chip nutzen zu können, binden wir das #ln_tm4c123x;-Crate ein. ], +ja: [ + 全てのCortex−Mに搭載されている基本的なペリフェラルのみに限定するのであれば、組込みソフトウェア開発はあまり進まないでしょう。 + どこかの時点で、使用している特定のマイクロコントローラ固有のコードを書く必要があります。今回の例では、テキサスインスツルメンツのTM4C123があるとしましょう。 + TM4C123はミドルレンジのマイクロコントローラで、80MHzのCortex-M4と256 + KiBのフラッシュメモリが搭載されています。 + このチップを利用するために、#ln_tm4c123x;クレートを取得します。 +], zh: [ 如果我们把自己只局限于每个Cortex-M拥有的基本外设,那我们的嵌入式软件开发将不会走得太远。我们准备需要写一些特定于我们正在使用的微控制器的代码。在这个例子里,让我们假设我们有一个TI的TM4C123 - 一个有256KiB @@ -304,11 +347,14 @@ zh: [ crate去使用这个芯片。 ])) -```rust -#![no_std] +#raw(block: true, lang: "rust", +"#![no_std] #![no_main] -use panic_halt as _; // panic handler +use panic_halt as _; // " + ts(( + en: "panic handler", + ja: "パニックハンドラ", + )) + " use cortex_m_rt::entry; use tm4c123x; @@ -320,15 +366,21 @@ pub fn init() -> (Delay, Leds) { let pwm = p.PWM0; pwm.ctl.write(|w| w.globalsync0().clear_bit()); - // Mode = 1 => Count up/down mode + // " + ts(( + en: "Mode = 1 => Count up/down mode", + ja: "モード1は カウントアップ/ダウンモード", + )) + " pwm._2_ctl.write(|w| w.enable().set_bit().mode().set_bit()); pwm._2_gena.write(|w| w.actcmpau().zero().actcmpad().one()); - // 528 cycles (264 up and down) = 4 loops per video line (2112 cycles) + // " + ts(( + en: "528 cycles (264 up and down) = 4 loops per video line (2112 cycles)", + ja: "528サイクル(264カウントアップとカウントダウン)は、ビデオラインごとに4ループ(2112サイクル)", + )) + " pwm._2_load.write(|w| unsafe { w.load().bits(263) }); pwm._2_cmpa.write(|w| unsafe { w.compa().bits(64) }); pwm.enable.write(|w| w.pwm4en().set_bit()); } -``` +") #let ln_svd2rust = link("https://crates.io/crates/svd2rust")[svd2rust] #tr(( @@ -366,6 +418,14 @@ de: [ Dies lässt sich im obigen Beispiel beim Setzen der Unterfelder `load` und `compa` mittels der Funktion `bits()` beobachten. ], +ja: [ + 先ほど`SYST`にアクセスした時と全く同じ方法で、`PWM0`ペリフェラルにアクセスします。違う点は、`tm4c123x::Peripherals::take()`を呼ぶことです。 + このクレートは、#link("https://crates.io/crates/svd2rust")[svd2rust]を使って自動生成されたものです。レジスタフィールドのアクセス関数は、数値の引数ではなく、クロージャを取ります。 + このコードは量が多いように見えますが、Rustコンパイラは一連のチェックを実行し、手書きのアセンブラに近いマシンコードを生成します。 + 自動生成されたコードが、特定のアクセサ関数への全引数が有効であることを判断できない場合、その関数は`unsafe`とマークされます。 + 例えば、SVDがレジスタを32ビットと定義しているが、それらの32ビット値の一部が特別な意味を持つかどうか、記述していない場合です。 + 上記の例では、`bits()`関数を使って`load`と`compa`サブフィールドを設定する時に、`unsafe`をマークしています。 +], zh: [ 我们访问 `PWM0` 外设的方法和我们之前访问 `SYST` 的方法一样,除了我们调用的是 `tm4c123x::Peripherals::take()` @@ -376,6 +436,7 @@ zh: [ == #tr(( en: [Reading], de: [Lesen], + ja: [読み込み], zh: [Reading], )) @@ -398,6 +459,10 @@ de: [ verfügbaren Funktionen finden Sie in der #link(url_struct_r)[tm4c123x-Dokumentation]. ], +ja: [ + `read()`関数は、メーカーのSVDファイルで定義されている通り、レジスタ内の様々なサブフィールドに対して、読み込み専用のアクセスオブジェクトを返します。 + 特定チップ上にある、特定ペリフェラルの、特定レジスタに対して、固有の返り値`R`型があり、このR型で使える全ての関数は、#link(url_struct_r)[tm4c123xドキュメント]で見ることができます。 +], zh: [ `read()` 函数返回一个对象,这个对象提供了对这个寄存器中不同子域的只读访问,由厂商提供的这个芯片的SVD文件定义。在 @@ -411,6 +476,7 @@ zh: [ // " + ts(( en: "Do a thing", de: "Tu etwas", + ja: "処理をする", )) + " } ") @@ -418,6 +484,7 @@ zh: [ == #tr(( en: [Writing], de: [Schreiben], + ja: [書き込み], zh: [Writing], )) @@ -447,6 +514,12 @@ de: [ automatisch auf einen Standardwert gesetzt werden; bereits vorhandene Inhalte des Registers gehen dabei verloren. ], +ja: [ + `write()`関数は、単一引数のクロージャを取ります。通常は、この引数を`w`と呼びます。 + この引数は、チップメーカーがSVDファイルで定義している通り、様々なレジスタのサブフィールドへの読み書きアクセスを許可します。 + 特定チップ上にある、特定ペリフェラルの、特定レジスタに対して、`w`型で使える全ての関数も、#link(url_struct_w)[tm4c123xドキュメント]で見ることができます。 + 設定していない全てのサブフィールドは、デフォルト値に設定されます。レジスタの既存の内容は失われます。 +], zh: [ `write()`函数使用一个只有一个参数的闭包。通常我们把这个参数叫做 `w`。然后这个参数提供对这个寄存器中不同的子域的读写访问,由厂商关于这个芯片的SVD文件提供。再一次,在 @@ -463,6 +536,7 @@ pwm.ctl.write(|w| w.globalsync0().clear_bit()); == #tr(( en: [Modifying], de: [Ändern], + ja: [修正], zh: [Modifying], )) @@ -485,6 +559,11 @@ de: [ betrachten, während das Argument `w` genutzt werden kann, um den Registerinhalt zu ändern. ], +ja: [ + レジスタの特定のサブフィールドだけを変更して、残りのサブフィールドは変更したくない場合、`modify`関数を使えます。この関数は2引数のクロージャを取ります。 + 1つは読み込み用で、もう1つは書き込み用です。通常、これらの引数をそれぞれ、`r`と`w`と呼びます。 + `r`引数は、レジスタの現在の内容を調べるために使用されます。そして、`w`引数は、レジスタの内容を修正するために使用されます。 +], zh: [ 如果我们希望只改变这个寄存器中某个特定的子域而让其它子域不变,我们能使用`modify`函数。这个函数使用一个具有两个参数的闭包 - 一个用来读取,一个用来写入。通常我们分别称它们为 `r` 和 `w` 。 `r` @@ -508,6 +587,10 @@ de: [ Wert anschließend wieder zurückschreiben. Dies birgt ein erhebliches Fehlerpotenzial: ], +ja: [ + `modify`関数は、クロージャの本領を発揮します。C言語では、一時変数に読み込み、正しいビットを修正してから、その値を書き戻す必要があります。 + これは、エラーが発生するかなりの余地があることを示しています。 +], zh: [ `modify` 函数在这里真正展示了闭包的能量。在C中,我们经常需要读取一些临时值,修改成正确的比特,然后再把值写回。这意味着出现错误的范围非常大。 @@ -521,6 +604,7 @@ uint32_t temp2 = pwm0.enable.read(); temp2 |= PWM0_ENABLE_PWM4EN; pwm0.enable.write(temp); // " + ts(( en: "Uh oh! Wrong variable!", + ja: "ああ!間違った変数です!", zh: "哦 不! 错误的变量!", )) + " ") @@ -528,6 +612,7 @@ pwm0.enable.write(temp); // " + ts(( = #tr(( en: [Using a HAL crate], de: [Verwendung eines HAL-Crates], + ja: [HALクレートの使用], zh: [使用一个HAL crate], )) @@ -571,6 +656,18 @@ de: [ passenden Modus für alternative Funktionen), bevor er ihn an die Peripherieeinheit übergibt. Und das alles ohne Laufzeitkosten! ], +ja: [ + あるチップ用のHALクレートは、典型的には、PACによって公開されている生の構造体に対して、カスタムトレイトを実装することで機能しています。 + 大抵、このトレイトは、単独のペリフェラルには`constrain()`関数を定義し、複数ピンを利用するGPIOポートのようなものには`split()`関数を定義します。 + この関数は、下層の生のペリフェラル構造体オブジェクトを消費し、より高レベルなAPIを備える新しいオブジェクトを返します。 + このAPIは、シリアルポートの`new`関数が、`Clock`構造体オブジェクトの借用を必要とするようなことをするかもしれません。Clock構造体オブジェクトは、 + PLLと全てのクロック周波数とを設定する関数呼び出しによってのみ、生成することが可能です。この方法では、最初にクロックレートを設定しないでシリアルポートオブジェクトを作成したり、 + シリアルポートオブジェクトがボーレートをクロック数に誤って変換するようなことは、静的に起こり得ません。 + 一部のクレートでは、各GPIOが取り得る状態のための特別なトレイトを定義することさえあります。このトレイトは、ペリフェラルにピンを渡す前に、 + ユーザがピンを正しい状態(例えば、適切なAlternate + Functionモードを選択することによって)にすることを求めます。 + これらは全て、ランタイムのコストを必要としません。 +], zh: [ 一个芯片的HAL crate是通过为PAC暴露的基础结构体们实现一个自定义Trait来发挥作用的。经常这个trait将会为某个外设定义一个被称作 @@ -591,6 +688,9 @@ en: [ de: [ Schauen wir uns ein Beispiel an: ], +ja: [ + 例を見てみましょう。 +], zh: [ 让我们看一个例子: ])) @@ -616,12 +716,14 @@ fn main() -> ! { en: "Wrap up the SYSCTL struct into an object with a higher-layer API", de: "Kapseln Sie die SYSCTL-Struktur in einem Objekt mit einer API auf // hoeherer Ebene.", + ja: "SYSCTL構造体をより高レイヤなAPIオブジェクトでラップします", zh: "将SYSCTL结构体封装成一个有更高抽象API的对象", )) + " let mut sc = p.SYSCTL.constrain(); // " + ts(( en: "Pick our oscillation settings", de: "Waehlen Sie unsere Oszillationseinstellungen aus.", + ja: "オシレータの設定値を選択します", zh: "选择我们的晶振配置", )) + " sc.clock_setup.oscillator = sysctl::Oscillator::Main( @@ -631,6 +733,7 @@ fn main() -> ! { // " + ts(( en: "Configure the PLL with those settings", de: "Konfigurieren Sie die PLL mit diesen Einstellungen.", + ja: "PLLをそれらの設定値で設定します", zh: "设置PLL", )) + " let clocks = sc.clock_setup.freeze(); @@ -643,6 +746,9 @@ fn main() -> ! { // hoeheren Ebene. // Beachten Sie, dass es `sc.power_control` nutzen muss, um die // Stromversorgung der GPIO-Peripherie automatisch zu aktivieren.", + ja: "GPIO_PORTA構造体をより高レイヤなAPIオブジェクトでラップします。 + // GPIOペリフェラルに自動的に電源を入れるために、 + // `sc.power_control`の借用が必要なことに留意して下さい。", zh: "把GPIO_PORTA结构体封装成一个有更高抽象API的对象 // 注意它需要借用 `sc.power_control` 因此它能自动开启GPIO外设。", )) + " @@ -651,6 +757,7 @@ fn main() -> ! { // " + ts(( en: "Activate the UART.", de: "Aktivieren Sie den UART.", + ja: "UARTを起動します。", zh: "激活UART", )) + " let uart = Serial::uart0( @@ -658,6 +765,7 @@ fn main() -> ! { // " + ts(( en: "The transmit pin", de: "Der Sende-Pin", + ja: "送信ピン", zh: "激活UART", )) + " porta @@ -666,6 +774,7 @@ fn main() -> ! { // " + ts(( en: "The receive pin", de: "Der Empfangs-Pin", + ja: "受信ピン", zh: "接收管脚" )) + " porta @@ -674,6 +783,7 @@ fn main() -> ! { // " + ts(( en: "No RTS or CTS required", de: "Kein RTS oder CTS erforderlich", + ja: "RTSとCTSは必要としません", zh: "不需要RTS或者CTS" )) + " (), @@ -681,12 +791,14 @@ fn main() -> ! { // " + ts(( en: "The baud rate", de: "Die Baudrate", + ja: "ボーレート", zh: "波特率", )) + " 115200_u32.bps(), // " + ts(( en: "Output handling", de: "Ausgabeverarbeitung", + ja: "出力制御", zh: "输出处理", )) + " NewlineMode::SwapLFtoCRLF, @@ -694,12 +806,14 @@ fn main() -> ! { en: "We need the clock rates to calculate the baud rate divisors", de: "Wir benoetigen die Taktfrequenzen, um die Baudraten-Teiler zu // berechnen.", + ja: "ボーレートの除数を計算するためにクロックレートが必要です", zh: "我们需要时钟频率去计算波特率除法器(divisors)", )) + " &clocks, // " + ts(( en: "We need this to power up the UART peripheral", de: "Wir benoetigen dies, um die UART-Peripherie zu aktivieren.", + ja: "UARTペリフェラルの電源を入れるために必要です", zh: "我们需要这个去启动UART外设", )) + " &sc.power_control, diff --git a/typ/start/semihosting.typ b/typ/start/semihosting.typ index 0f63e231..90beb2e2 100644 --- a/typ/start/semihosting.typ +++ b/typ/start/semihosting.typ @@ -1,10 +1,10 @@ #import "../config.typ": * -#h1(offset: whole, tr(( - en: [Semihosting], +#h1((en: [Semihosting], de: [Semihosting], + ja: [セミホスティング], zh: [半主机模式], -))) +), offset: whole) #tr(( en: [ @@ -26,6 +26,11 @@ de: [ Hardware-Debugger (z. B. ST-Link) kann jeder Schreibvorgang mehrere Millisekunden in Anspruch nehmen. ], +ja: [ + セミホスティングは、組込みデバイスがホスト上でI/Oを行う仕組みです。主に、ホストのコンソールにログ出力するために使われます。 + セミホスティングには、デバッグセッションが必要ですが、他には何も必要としません(追加の配線は不要です)。そのため、非常に便利です。 + 欠点は、非常に低速であることです。ハードウェアデバッガ(例えば、ST-Link)によっては、書き込み操作が数ミリ秒かかります。 +], zh: [ 半主机模式是一种可以让嵌入式设备在主机上进行I/O操作的的机制,主要被用来记录信息到主机控制台上。半主机模式需要一个debug会话,除此之外几乎没有其它要求了,因此它非常易于使用。缺点是它非常慢:每个写操作需要几毫秒的时间,其取决于你的硬件调试器(e.g.~ST-LINK)。 ])) @@ -42,6 +47,10 @@ de: [ stellt eine API für Semihosting-Operationen auf Cortex-M-Geräten bereit. Das folgende Programm ist die Semihosting-Version von „Hello, world!": ], +ja: [ + #ln_sh;クレートは、Cortex-Mデバイス上でセミホスティング操作をするためのAPIを提供します。 + 下のプログラムは、セミホスティングバージョンの「Hello, world!」です。 +], zh: [ #ln_sh crate 提供了一个API去在Cortex-M设备上执行半主机操作。下面的程序是"Hello, world!"的半主机版本。 @@ -73,6 +82,10 @@ de: [ Wenn Sie dieses Programm auf der Hardware ausführen, sehen Sie die Meldung „Hello, world!" in den OpenOCD-Protokollen. ], +ja: [ + このプログラムをハードウェア上で実行すると、OpenOCDのログに、「Hello + world!」のメッセージが表示されます。 +], zh: [ 如果你在硬件上运行这个程序,你将会在OpenOCD的logs中看到"Hello, world!"信息。 @@ -92,6 +105,9 @@ en: [ de: [ Sie müssen zunächst Semihosting in OpenOCD über GDB aktivieren: ], +ja: [ + 最初に、GDBからOpenOCDのセミホスティングを有効化する必要があります。 +], zh: [ 你首先需要从GDB使能OpenOCD中的半主机模式。 ])) @@ -117,6 +133,11 @@ de: [ zu aktivieren; diese Optionen sind bereits in der Datei `.cargo/config.toml` der Vorlage enthalten. ], +ja: [ + QEMUはセミホスティング操作を理解しているため、上のプログラムは、デバッグセッションを開始していない`qemu-system-arm`でも動作します。 + セミホスティングサポートを有効化するため、QEMUに`-semihosting-config`フラグを渡す必要があることに注意して下さい。 + これらのフラグは、テンプレートの`.cargo/config`ファイルに既に含まれています。 +], zh: [ QEMU理解半主机操作,因此上面的程序不需要启动一个debug会话,也能在`qemu-system-arm`中工作。注意你需要传递`-semihosting-config`标志给QEMU去使能支持半主机模式;这些标识已经被包括在模板的`.cargo/config.toml`文件中了。 ])) @@ -125,6 +146,7 @@ zh: [ "$ # " + ts(( en: "this program will block the terminal", de: "Dieses Programm wird das Terminal blockieren.", + ja: "このプログラムは端末をブロックします", )) + " $ cargo run Running `qemu-system-arm (..) @@ -145,6 +167,11 @@ de: [ OpenOCD-Sitzung beschädigen, sodass Sie keine weiteren Programme mehr debuggen können, bis Sie die Sitzung neu starten. ], +ja: [ + `exit`セミホスティング操作もあり、QEMUプロセスを終了するために使われます。 + 重要:ハードウェア上で`debug::exit`を*使用しない*で下さい。この関数は、OpenOCDセッションを破壊する可能性があり、 + OpenOCDを再起動しない限り、それ以上のプログラムのデバッグができなくなります。 +], zh: [ `exit`半主机操作也能被用于终止QEMU进程。重要:*不要*在硬件上使用`debug::exit`;这个函数会关闭你的OpenOCD对话,这样你就不能执行其它的程序调试操作了,除了重启它。 ])) @@ -192,6 +219,11 @@ de: [ schreiben, die erfolgreich durchlaufen und unter QEMU ausgeführt werden können. ], +ja: [ + 最後のヒント:パニック時の挙動を、`exit(EXIT_FAILURE)`に設定することができます。 + これで、QEMU上で実行できる`no_std`ランパステストを書くことができます。 + +], zh: [ 最后一个提示:你可以将运行时恐慌(panicking)的行为设置成 `exit(EXIT_FAILURE)`。这会允许你编写可以在QEMU上运行通过的 `no_std` @@ -209,6 +241,10 @@ de: [ an; ist dieses aktiviert, wird `exit(EXIT_FAILURE)` aufgerufen, nachdem die Panic-Meldung auf dem `stderr` des Hosts ausgegeben wurde. ], +ja: [ + 利便性のために、`panic-semihosting`クレートは、「exit」フィーチャを持っています。 + このフィーチャが有効化されていると、ホストの標準エラーにパニックメッセージをログ出力した後、`exit(EXIT_FAILURE)`を呼び出します。 +], zh: [ 为了方便,`panic-semihosting` crate有一个 "exit" 特性。当它使能的时候,在主机stderr上打印恐慌(painc)信息后会调用 diff --git a/typ/static-guarantees/design-contracts.typ b/typ/static-guarantees/design-contracts.typ index 7bc2676a..7c4a7270 100644 --- a/typ/static-guarantees/design-contracts.typ +++ b/typ/static-guarantees/design-contracts.typ @@ -1,10 +1,10 @@ #import "../config.typ": * -#h1(offset: whole, tr(( - en: [Design Contracts], +#h1((en: [Design Contracts], de: [Design-Verträge], + ja: [設計契約], zh: [设计约定(design contracts)], -))) +), offset: whole) #tr(( en: [ @@ -17,6 +17,9 @@ de: [ _keine_ Design-Verträge (Design Contracts) durchsetzte. Werfen wir noch einmal einen Blick auf unser fiktives GPIO-Konfigurationsregister: ], +ja: [ + 前回、設計契約を強制_しない_インタフェースを書きました。架空のGPIO設定レジスタをもう一度見てみましょう。 +], zh: [ 在我们的上个章节中,我们写了一个接口,但没有强制遵守设计约定。让我们再看下我们假想的GPIO配置寄存器: ])) @@ -35,6 +38,10 @@ de: [ Laufzeit durchsetzen würden, könnten wir stattdessen Code schreiben, der wie folgt aussieht: ], +ja: [ + ハードウェアを使う前に状態をチェックし、実行時に設計契約を強制すると、コードは次のように書くことができます。 + +], zh: [ 如果在使用底层硬件之前检查硬件的状态,在运行时强制用户遵守设计约定,代码可能像这一样: ])) @@ -43,12 +50,14 @@ zh: [ "/// " + ts(( en: "GPIO interface", de: "GPIO-Schnittstelle", + ja: "GPIOインタフェース", zh: "GPIO接口", )) + " struct GpioConfig { /// " + ts(( en: "GPIO Configuration structure generated by svd2rust", de: "Von svd2rust generierte GPIO-Konfigurationsstruktur", + ja: "svd2rustによって生成されたGPIO設定構造体", zh: "由svd2rust生成的GPIO配制结构体" )) + " periph: GPIO_CONFIG, @@ -66,6 +75,7 @@ impl GpioConfig { // " + ts(( en: "Must be enabled to set direction", de: "Muss aktiviert sein, um die Richtung festzulegen.", + ja: "方向を設定するには、有効化されてなければなりません", zh: "必须被使能配置方向" )) + " return Err(()); @@ -83,6 +93,7 @@ impl GpioConfig { // " + ts(( en: "Must be enabled to set input mode", de: "Muss aktiviert sein, um den Eingabemodus festzulegen.", + ja: "入力モードを設定するには、有効化されてなければなりません", zh: "必须被使能配置输入模式", )) + " return Err(()); @@ -92,6 +103,7 @@ impl GpioConfig { // " + ts(( en: "Direction must be input", de: "Die Richtung muss input sein.", + ja: "方向は入力でなければなりません", zh: "方向必须被设置成输入", )) + " return Err(()); @@ -109,6 +121,7 @@ impl GpioConfig { // " + ts(( en: "Must be enabled to set output status", de: "Muss aktiviert sein, um den Ausgangsstatus festzulegen.", + ja: "方向は入力でなければなりません", zh: "设置输出状态必须被使能", )) + " return Err(()); @@ -118,6 +131,7 @@ impl GpioConfig { // " + ts(( en: "Direction must be output", de: "Die Richtung muss output sein.", + ja: "方向は出力でなければなりません", zh: "方向必须是输出", )) + " return Err(()); @@ -135,6 +149,7 @@ impl GpioConfig { // " + ts(( en: "Must be enabled to get status", de: "Muss aktiviert sein, um den Status abzurufen.", + ja: "状態を取得するには、有効化されてなければなりません", zh: "获取状态必须被使能", )) + " return Err(()); @@ -144,6 +159,7 @@ impl GpioConfig { // " + ts(( en: "Direction must be input", de: "Die Richtung muss input sein.", + ja: "方向は入力でなければなりません", zh: "方向必须是输入", )) + " return Err(()); @@ -166,6 +182,9 @@ de: [ Ressourcen verschwendet --, und der Code ist für Entwickler deutlich weniger angenehm zu handhaben. ], +ja: [ + ハードウェアの制約を強制する必要があるため、時間とリソースを浪費する多くの実行時チェックを行うこととなり、開発者にとってこのコードは好ましくないです。 +], zh: [ 因为需要强制遵守硬件上的限制,所以最后做了很多运行时检查,它浪费了我们很多时间和资源,对于开发者来说,这个代码用起来就没那么愉快了。 ])) @@ -173,6 +192,7 @@ zh: [ = #tr(( en: [Type States], de: [Typzustände], + ja: [型状態], zh: [类型状态(Type states)], )) @@ -186,6 +206,9 @@ de: [ die Regeln für Zustandsübergänge durchzusetzen? Betrachten wir dieses Beispiel: ], +ja: [ + しかし、代わりに、状態遷移の規則を強制するために、Rustの型システムを使うとどうなるでしょうか?この例を見て下さい。 +], zh: [ 但是,如果我们让Rust的类型系统去强制遵守状态转换的规则会怎样?看下这个例子: ])) @@ -194,12 +217,14 @@ zh: [ "/// " + ts(( en: "GPIO interface", de: "GPIO-Schnittstelle", + ja: "GPIOインタフェース", zh: "GPIO接口", )) + " struct GpioConfig { /// " + ts(( en: "GPIO Configuration structure generated by svd2rust", de: "Von svd2rust generierte GPIO-Konfigurationsstruktur", + ja: "svd2rustによって生成されたGPIO設定構造体", zh: "由svd2rust产生的GPIO配置结构体", )) + " periph: GPIO_CONFIG, @@ -211,6 +236,7 @@ struct GpioConfig { // " + ts(( en: "Type states for MODE in GpioConfig", de: "Typzustaende fuer MODE in GpioConfig", + ja: "GpioConfigのMODEのための型状態", zh: "GpioConfig中MODE的类型状态", )) + " struct Disabled; @@ -225,6 +251,7 @@ struct DontCare; /// " + ts(( en: "These functions may be used on any GPIO Pin", de: "Diese Funktionen koennen an jedem GPIO-Pin verwendet werden.", + ja: "これらの関数はどのGPIOピンにも使えます", zh: "这些函数可能被用于所有的GPIO管脚", )) + " impl GpioConfig { @@ -270,6 +297,7 @@ impl GpioConfig { /// " + ts(( en: "This function may be used on an Output Pin", de: "Diese Funktion kann fuer einen Ausgangspin verwendet werden.", + ja: "この関数はOutputピンに使用できます", zh: "这个函数可能被用于一个输出管脚", )) + " impl GpioConfig { @@ -281,6 +309,7 @@ impl GpioConfig { /// " + ts(( en: "These methods may be used on any enabled input GPIO", de: "Diese Methoden koennen auf jedem aktivierten Eingangs-GPIO verwendet werden.", + ja: "これらのメソッドは、有効化された入力GPIOに使えます", zh: "这些方法可能被用于任意一个使能的输入GPIO", )) + " impl GpioConfig { @@ -327,6 +356,9 @@ en: [ de: [ Schauen wir uns nun an, wie der Code aussehen würde, der dies verwendet: ], +ja: [ + それでは、これを使うコードがどのようになるか、見てみましょう。 +], zh: [ 现在让我们看下代码如何用这个API: ])) @@ -336,6 +368,7 @@ zh: [ * " + ts(( en: "Example 1: Unconfigured to High-Z input", de: "Beispiel 1: Nicht konfiguriert als High-Z-Eingang", + ja: "例1:未設定から高抵抗入力", zh: "案例 1: 从未配置到高阻输入", )) + " */ @@ -344,6 +377,7 @@ let pin: GpioConfig = get_gpio(); // " + ts(( en: "Can't do this, pin isn't enabled!", de: "Das ist nicht moeglich, die PIN ist nicht aktiviert!", + ja: "これはできません、ピンが有効になっていません!", zh: "不能这么做,pin没有被使能", )) + " // pin.into_input_pull_down(); @@ -352,6 +386,7 @@ let pin: GpioConfig = get_gpio(); en: "Now turn the pin from unconfigured to a high-z input", de: "Schalten Sie den Pin nun von „unkonfiguriert“ auf einen hochohmigen Eingang // (High-Z-Eingang) um.", + ja: "今度は、未設定から高抵抗入力に変えます", zh: "现在把管脚从未配置变为高阻输入", )) + " let input_pin = pin.into_enabled_input(); @@ -359,6 +394,7 @@ let input_pin = pin.into_enabled_input(); // " + ts(( en: "Read from the pin", de: "Vom Pin lesen", + ja: "ピンから値を読みます", zh: "从管脚读取", )) + " let pin_state = input_pin.bit_is_set(); @@ -367,6 +403,7 @@ let pin_state = input_pin.bit_is_set(); en: "Can't do this, input pins don't have this interface!", de: "Das ist nicht moeglich; Eingangs-Pins verfuegen nicht über diese // Schnittstelle!", + ja: "これはできません、入力ピンはこのインタフェースを持っていません!", zh: "不能这么做,输入管脚没有这个接口", )) + " // input_pin.set_bit(true); @@ -375,6 +412,7 @@ let pin_state = input_pin.bit_is_set(); * " + ts(( en: "Example 2: High-Z input to Pulled Low input", de: "Beispiel 2: Hochohmiger Eingang (High-Z) an auf Low-Pegel gezogenen Eingang", + ja: "例2:高抵抗入力からプルダウン入力", zh: "案例 2: 高阻输入到下拉输入", )) + " */ @@ -385,6 +423,7 @@ let pin_state = pulled_low.bit_is_set(); * " + ts(( en: "Example 3: Pulled Low input to Output, set high", de: "Beispiel 3: Eingang auf Low gezogen, Ausgang auf High gesetzt", + ja: "例3:プルダウン入力から出力、ハイを設定", zh: "案例 3: 下拉输入到输出, 拉高", )) + " */ @@ -394,6 +433,7 @@ output_pin.set_bit(true); // " + ts(( en: "Can't do this, output pins don't have this interface!", de: "Das geht nicht, Ausgangspins verfuegen nicht über diese Schnittstelle!", + ja: "これはできません、出力ピンはこのインタフェースを持っていません!", zh: "不能这么做,输出管脚没有这个接口", )) + " // output_pin.into_input_pull_down(); @@ -411,6 +451,10 @@ de: [ besser, als den Zustand als `enum` innerhalb unserer `GpioConfig`-Struktur zu speichern? ], +ja: [ + これは間違いなく、ピンの状態を保存するのに便利な方法ですが、なぜこのようにするのでしょうか? + なぜ、`GpioConfig`構造体の中で、状態を`enum`として保存するより良い方法なのでしょうか? +], zh: [ 这绝对是存储管脚状态的便捷方法,但是为什么这么做?为什么这比把状态当成一个`enum`存在我们的`GpioConfig`结构体中更好? ])) @@ -418,6 +462,7 @@ zh: [ = #tr(( en: [Compile Time Functional Safety], de: [Funktionale Sicherheit zur Kompilierzeit], + ja: [コンパイル時の機能の安全性], zh: [编译时功能安全(Functional Safety)], )) @@ -439,6 +484,11 @@ de: [ Dadurch entfällt der Laufzeitaufwand für die Überprüfung des aktuellen Zustands vor der Ausführung einer Funktion. ], +ja: [ + コンパイル時に、設計契約を完全に強制しているため、実行時コストはかかりません。入力方向のピンに対して、出力モードを設定することは不可能です。 + 代わりに、そのピンを出力ピンに変換してから、出力モードを設定することで、状態を辿る必要があります。 + このおかげで、関数実行前に現在の状態をチェックすることによる実行時ペナルティは、ありません。 +], zh: [ 因为我们在编译时完全强制遵守设计约定,这造成了没有运行时开销。当管脚处于输入模式时时,是不可能设置输出模式的。必须先把它设置成一个输出管脚,然后再设置输出模式。因为在执行一个函数前会检查现在的状态,因此没有运行时消耗。 ])) @@ -455,6 +505,10 @@ de: [ der Nutzer einen unzulässigen Zustandsübergang, lässt sich der Code nicht kompilieren! ], +ja: [ + 型システムによってこれらの状態が強制されるため、このインタフェースの利用者によるエラーの余地はもはや残っていません。 + もし利用者が不正な状態遷移をしようとすると、そのコードはコンパイルできません! +], zh: [ 也因为这些状态被类型系统强制遵守,因此没有为这个接口的使用者留太多的犯错余地。如果它们尝试执行一个非法的状态转换,代码将不会编译成功! ])) diff --git a/typ/static-guarantees/gpio-table.typ b/typ/static-guarantees/gpio-table.typ index 84c60852..b1fb1985 100644 --- a/typ/static-guarantees/gpio-table.typ +++ b/typ/static-guarantees/gpio-table.typ @@ -15,26 +15,31 @@ tr(( en: [Name], de: [Name], + ja: [名前], zh: [名字], )), tr(( en: [Bit Number(s)], de: [Bit-Nummer], + ja: [ビットフィールド], zh: [位数(s)], )), tr(( en: [Value], de: [Wert], + ja: [値], zh: [值], )), tr(( en: [Meaning], de: [Bedeutung], + ja: [意味], zh: [含义], )), tr(( en: [Notes], de: [Hinweise], + ja: [説明], zh: [注释], )), ), @@ -43,6 +48,7 @@ tr(( en: [Disables the GPIO], de: [Deaktiviert den GPIO], + ja: [GPIOを無効にする], zh: [关闭GPIO], )), @@ -50,6 +56,7 @@ tr(( en: [Enables the GPIO], de: [Aktiviert den GPIO], + ja: [GPIOを有効にする], zh: [使能GPIO], )), @@ -58,6 +65,7 @@ tr(( en: [Sets the direction to Input], de: [Legt die Richtung auf „Eingang" fest.], + ja: [方向を入力に設定する], zh: [方向设置成输入], )), @@ -65,6 +73,7 @@ tr(( en: [Sets the direction to Output], de: [Legt die Richtung auf „Ausgang" fest.], + ja: [方向を出力に設定する], zh: [方向设置成输出], )), @@ -73,6 +82,7 @@ tr(( en: [Sets the input as high resistance], de: [Setzt den Eingang auf hochohmig.], + ja: [入力を高抵抗に設定する], zh: [输入设置为高阻态], )), @@ -80,6 +90,7 @@ tr(( en: [Input pin is pulled low], de: [Der Eingangspin wird auf Low-Pegel gezogen.], + ja: [入力ピンはプルダウンになる], zh: [下拉输入管脚], )), @@ -87,6 +98,7 @@ tr(( en: [Input pin is pulled high], de: [Der Eingangspin wird auf High-Pegel gezogen], + ja: [入力ピンはプルアップになる], zh: [上拉输入管脚], )), @@ -94,6 +106,7 @@ tr(( en: [Invalid state. Do not set], de: [Ungültiger Zustand. Nicht setzen.], + ja: [無効な状態。設定しないこと。], zh: [无效状态。不要设置], )), @@ -102,6 +115,7 @@ tr(( en: [Output pin is driven low], de: [Der Ausgangspin wird auf Low-Pegel gesteuert.], + ja: [出力ピンをローにする], zh: [把管脚设置成低电平], )), @@ -109,6 +123,7 @@ tr(( en: [Output pin is driven high], de: [Der Ausgangspin wird auf High-Pegel gesteuert.], + ja: [出力ピンをハイにする], zh: [把管脚设置成高电平], )), @@ -116,6 +131,7 @@ tr(( en: [0 if input is < 1.5v, 1 if input >= 1.5v], de: [0, wenn der Eingang < 1,5 V ist; 1, wenn der Eingang ≥ 1,5 V ist.], + ja: [入力が1.5Vより低ければ0、1.5V以上であれば1], zh: [如果输入 < 1.5v 为0,如果输入 >= 1.5v 为1], )), ) diff --git a/typ/static-guarantees/index.typ b/typ/static-guarantees/index.typ index 1bfee13d..cc9c084a 100644 --- a/typ/static-guarantees/index.typ +++ b/typ/static-guarantees/index.typ @@ -1,10 +1,10 @@ #import "../config.typ": * -#h1(tr(( - en: [Static Guarantees], +#h1((en: [Static Guarantees], de: [Statische Garantien], + ja: [静的な保証], zh: [静态保障], -))) +)) #let ln_send = link("https://doc.rust-lang.org/core/marker/trait.Send.html")[`Send`] #let ln_sync = link("https://doc.rust-lang.org/core/marker/trait.Sync.html")[`Sync`] @@ -22,6 +22,11 @@ de: [ zur Kompilierzeit zu überprüfen, wodurch in einigen Fällen Laufzeitprüfungen überflüssig werden. ], +ja: [ + コンパイル時にデータ競合を防ぐのは、Rustの型システムです(#ln_send;と#ln_sync;トレイトを参照)。 + この型システムは、コンパイル時に他のプロパティをチェックするためにも使用できます。 + その結果、実行時チェックの必要性を減らせる場合があります。 +], zh: [ Rust的类型系统可以在编译时防止数据竞争(看#ln_send;和#ln_sync;特性(traits))。也可以在编译时使用类型系统来完成一些检查工作;减少某些例子中对运行时检查的需要。 ])) @@ -42,6 +47,10 @@ de: [ möglich ist, wenn zuvor die für diese Schnittstelle vorgesehenen Pins konfiguriert wurden. ], +ja: [ + 組込みプログラムに適用する場合、これらの_静的なチェック_は、例えば、入出力インタフェースが正しく設定されていることを強制することができます。 + 例えば、使用されるピンを最初に設定することによってのみ、シリアルインタフェースを初期化できるようなAPI設計が可能です。 +], zh: [ 当应用到嵌入式程序时,这些_静态检查_能被用来,比如,强制按需配置I/O接口。例如,可以设计一个初始化串行接口的API,这个API只有在配置好接口需要的管脚后才可以被正确地使用。 ])) @@ -61,6 +70,11 @@ de: [ mit undefiniertem Pegel) konfiguriert ist, würde beispielsweise zu einem Kompilierfehler führen. ], +ja: [ + 正しく設定されたペリフェラルでのみ、ピンをローレベルにするというような操作ができることを、 + 静的にチェックすることも可能です。例えば、フローティング入力モードに設定されたピンの出力状態を変更しようとすると、 + コンパイルエラーが発生します。 +], zh: [ 也可以静态检查,是否是在正确配置了的外设上执行的操作,像是拉低一个管脚这种操作。比如尝试修改一个被配置成浮空输入模式的管脚的输出状态时,会触发一个编译时错误。 ])) @@ -81,6 +95,11 @@ de: [ die Analyse der Software im Vergleich zu dem alternativen Ansatz, Peripheriekomponenten als globalen, veränderbaren Zustand zu behandeln. ], +ja: [ + 以前の章で見た通り、所有権の概念はペリフェラルにも適用できます。所有権は、プログラムの特定部分のみがペリフェラルを変更することを保証します。 + この_アクセスコントロール_は、ペリフェラルをグローバルでミュータブルな状態として扱う代替案と比較して、 + ソフトウェアの解析をより簡単にします。 +], zh: [ 并且,像是在前面章节看到的,所有权的概念能被应用到外设上确保一个程序只有某些部分可以修改一个外设。与将这个外设当做全局可变的状态相比,_访问控制_(assess control)使得软件更容易推理。 ])) diff --git a/typ/static-guarantees/state-machines.typ b/typ/static-guarantees/state-machines.typ index 77e96d66..aa6c9dc9 100644 --- a/typ/static-guarantees/state-machines.typ +++ b/typ/static-guarantees/state-machines.typ @@ -1,10 +1,10 @@ #import "../config.typ": * -#h1(offset: whole, tr(( - en: [Peripherals as State Machines], +#h1((en: [Peripherals as State Machines], de: [Peripheriegeräte als Zustandsautomaten], + ja: [ステートマシンとしてのペリフェラル], zh: [作为状态机的外设], -))) +), offset: whole) #let url_pin = "https://en.wikipedia.org/wiki/General-purpose_input/output" #tr(( @@ -38,6 +38,19 @@ de: [ - Eingang: Auf Low-Pegel gezogen - Eingang: Auf High-Pegel gezogen ], +ja: [ + マイクロコントローラのペリフェラルは、一連のステートマシンとして考えることができます。 + 例えば、簡略化された#link(url_pin)[GPIOピン]の設定は、次の状態ツリーとして表すことができます。 + - 無効 + - 有効 + - 出力として設定 + - 出力:ハイ + - 出力:ロー + - 入力として設定 + - 入力:高抵抗(訳注:ハイインピーダンス) + - 入力:プルダウン + - 入力:プルアップ +], zh: [ 一个微控制器的外设可以被想成是一组状态机。比如,一个简化的#link(url_pin)[GPIO管脚]的配置可以被表达成下列的状态树: - 关闭 @@ -69,6 +82,13 @@ de: [ + Als Eingang konfiguriert + Eingang: Hoher Widerstand ], +ja: [ + このペリフェラルが`無効`モードから始まるとすると、`入力:高抵抗`モードに移行するには、次のステップを踏みます。 + + 無効 + + 有効 + + 入力として設定 + + 入力:高抵抗 +], zh: [ 如果外设开始于`关闭`模式,切换到`输入: 高阻态`模式,我们必须执行下面的步骤: + 关闭 @@ -89,6 +109,11 @@ de: [ gezogen" wechseln möchten, müssen wir die folgenden Schritte ausführen: + Eingang: Hoher Widerstand 2. Eingang: Auf Low-Pegel gezogen ], +ja: [ + `入力:高抵抗`から`入力:プルダウン`へと移る場合、次のステップを実行しなければなりません。 + + 入力:高抵抗 + + 入力:プルダウン +], zh: [ 如果我们想要从`输入: 高阻态`切换到`输入: 下拉`,我们必须执行下列的步骤: + 输入: 高阻抗 @@ -114,6 +139,13 @@ de: [ + Als Ausgang konfiguriert + Ausgang: High ], +ja: [ + 同じように、`入力:プルダウン`と設定されているGPIOピンを、`出力:ハイ`にするには、次のステップを実行しなければなりません。 + + 入力:プルダウン + + 入力として設定 + + 出力として設定 + + 出力:ハイ +], zh: [ 同样地,如果我们想要把一个GPIO管脚从`输入: 下拉`切换到`输出: 高`,我们必须执行下列的步骤: + 输入: 下拉 @@ -125,6 +157,7 @@ zh: [ = #tr(( en: [Hardware Representation], de: [Hardware-Darstellung], + ja: [ハードウェアの表現], zh: [硬件表征(Hardware Representation)], )) @@ -140,6 +173,10 @@ de: [ GPIO-Peripherieeinheit zugeordnet sind. Definieren wir zur Veranschaulichung ein fiktives GPIO-Konfigurationsregister: ], +ja: [ + 通常、上記の状態は、GPIOペリフェラルに割り当てられたレジスタに値を書き込むことで設定できます。 + これを説明するために、架空のGPIO設定レジスタを定義しましょう。 +], zh: [ 通常,通过向映射到GPIO外设上的指定的寄存器中写入值可以配置上面列出的状态。让我们定义一个假想的GPIO配置寄存器来解释下它: ])) @@ -153,6 +190,10 @@ en: [ de: [ Wir _könnten_ die folgende Struktur in Rust bereitstellen, um diesen GPIO zu steuern: ], +ja: [ + このGPIOを制御するために、次のようなRustの構造体を公開することが + _できます_。 +], zh: [ _可以_ 在Rust中暴露下列的结构体来控制这个GPIO: ])) @@ -161,12 +202,14 @@ zh: [ "/// " + ts(( en: "GPIO interface", de: "GPIO interface", + ja: "GPIOインタフェース", zh: "GPIO接口", )) + " struct GpioConfig { /// " + ts(( en: "GPIO Configuration structure generated by svd2rust", de: "Von svd2rust generierte GPIO-Konfigurationsstruktur", + ja: "svd2rustで生成されたGPIO設定構造体", zh: "由svd2rust生成的GPIO配置结构体", )) + " periph: GPIO_CONFIG, @@ -214,6 +257,9 @@ de: [ keinen Sinn ergibt. Was passiert beispielsweise, wenn wir das Feld `output_mode` setzen, während unser GPIO als Eingang konfiguriert ist? ], +ja: [ + しかし、この実装では、筋が通らないレジスタの修正が可能になってしまいます。例えば、GPIOを入力に設定している時に、`出力モード`を設定すると、どうなるのでしょう? +], zh: [ 然而,这会允许我们修改某些没有意义的寄存器。比如,如果当我们的GPIO被配置为输入时我们设置`output_mode`字段,将会发生什么? ])) @@ -233,6 +279,10 @@ de: [ keine Rolle spielen. Bei anderer Hardware könnte es jedoch zu unerwartetem oder undefiniertem Verhalten führen! ], +ja: [ + 通常、この構造体を利用すると、上のステートマシンで定義されていない状態に到達できてしまいます。 + 例えば、プルダウンの出力や、ハイに設定された入力、です。 +], zh: [ 通常使用这个结构体会允许我们访问到上面的状态机没有定义的状态:比如,一个被上拉的输出,或者一个被拉高的输入。对于一些硬件,这并没有关系。对另外一些硬件来说,这将会导致不可预期或者没有定义的行为! ])) @@ -247,6 +297,9 @@ de: [ nicht die in unserer Hardware-Implementierung festgelegten Designvorgaben. ], +ja: [ + このインタフェースは書き込みには便利ですが、ハードウェア実装によって定められた設計の契約を強制しません。 +], zh: [ 虽然这个接口很方便写入,但是它没有强制我们遵守硬件实现所设的设计约定。 ])) diff --git a/typ/static-guarantees/typestate-programming.typ b/typ/static-guarantees/typestate-programming.typ index b94e94d7..70a5501e 100644 --- a/typ/static-guarantees/typestate-programming.typ +++ b/typ/static-guarantees/typestate-programming.typ @@ -1,10 +1,10 @@ #import "../config.typ": * -#h1(offset: whole, tr(( - en: [Typestate Programming], +#h1((en: [Typestate Programming], de: [Typgestützte Programmierung], + ja: [型状態プログラミング], zh: [类型状态编程(Typestate Programming)], -))) +), offset: whole) #let url_ts = "https://en.wikipedia.org/wiki/Typestate_analysis" #let url_builders = "https://doc.rust-lang.org/1.0.0/style/ownership/builders.html" @@ -26,6 +26,10 @@ de: [ verwendet haben, sind Sie schon mit der Typestate-Programmierung in Berührung gekommen! ], +ja: [ + #link(url_ts)[型状態]の概念は、オブジェクトの現在の状態に関する情報を、そのオブジェクトの型にエンコードすることを説明しています。 + これは、少し難解に思えますが、Rustの#link(url_builders)[ビルダーパターン]を使ったことがあるならば、既に型状態プログラミングを使い始めています。 +], zh: [ #link(url_ts)[typestates]的概念是指将有关对象当前状态的信息编码进该对象的类型中。虽然这听起来有点神秘,如果你在Rust中用过#link(url_builders))[建造者模式],你就已经开始使用类型状态编程了! ])) @@ -86,6 +90,9 @@ de: [ zu erstellen. Wir müssen einen `FooBuilder` erstellen und ihn korrekt initialisieren, bevor wir das gewünschte `Foo`-Objekt erhalten können. ], +ja: [ + この例では、`Foo`オブジェクトを直接作る方法はありません。必要な`Foo`オブジェクトを取得する前に、`FooBuilder`を作り、正しく初期化しなければなりません。 +], zh: [ 在这个例子里,不能直接生成一个`Foo`对象。必须先生成一个`FooBuilder`,并且恰当地初始化`FooBuilder`后,才能获取到需要的`Foo`对象。 ])) @@ -103,6 +110,11 @@ de: [ der „laufenden Konfiguration" repräsentiert - `Foo`, das einen „konfigurierten" oder „einsatzbereiten" Zustand repräsentiert. ], +ja: [ + この最小限の例は、2つの状態をエンコードしています。 + - `FooBuilder`は、「未設定」もしくは「設定中」の状態を表現します。 + - `Foo`は、「設定済み」もしくは「使用準備完了」の状態を意味します。 +], zh: [ 这个最小的例子编码了两个状态: - `FooBuilder`,其表示一个"没有被配置",或者"正在配置"状态 @@ -112,6 +124,7 @@ zh: [ = #tr(( en: [Strong Types], de: [Starke Typen], + ja: [強い型], zh: [强类型], )) @@ -135,6 +148,12 @@ de: [ `FooBuilder`-Struktur; das bedeutet, dass sie nicht ohne die Erstellung einer neuen Instanz wiederverwendet werden kann. ], +ja: [ + Rustは#link(url_strong)[強い型付けシステム]を持っています。`Foo`のインスタンスを魔法のように作成したり、 + `into_foo()`メソッドを呼び出すことなしに`FooBuilder`から`Foo`に変換したりする、簡単な方法はありません。 + さらに、`into_foo()`メソッドは、オリジナルの`FooBuilder`構造体を消費します。 + これは、新しいインスタンスを作成しないと、再利用ができないことを意味します。 +], zh: [ 因为Rust有一个#link(url_strong)[强类型系统],没有什么简单的方法可以奇迹般地生成一个`Foo`实例,也没有简单的方法可以不用调用`into_foo()`方法而把一个`FooBuilder`变成一个`Foo`。另外,调用`into_foo()`方法消费了最初的`FooBuilder`结构体,意味着不生成一个新的实例就不能被再次使用它。 ])) @@ -155,6 +174,11 @@ de: [ `Foo`-Objekt austauschen, haben wir die Schritte einer einfachen Zustandsmaschine durchlaufen. ], +ja: [ + このことにより、システムの状態を型として表現することが可能になります。 + そして、状態遷移に必要なアクションを、ある型と別の型とを交換するメソッドに取り入れることができます。 + `FooBuilder`を作成し、`Foo`オブジェクトに交換することで、基本的なステートマシンのステップを見てきました。 +], zh: [ 这允许我们可以将系统的状态表示成类型,把状态转换必须的动作包括进转换两个类型的方法中。通过生成一个 `FooBuilder`,转换成一个 `Foo` 对象,我们已经使用了一个基本的状态机。 diff --git a/typ/static-guarantees/zero-cost-abstractions.typ b/typ/static-guarantees/zero-cost-abstractions.typ index 7e94d205..0c6aee63 100644 --- a/typ/static-guarantees/zero-cost-abstractions.typ +++ b/typ/static-guarantees/zero-cost-abstractions.typ @@ -1,10 +1,10 @@ #import "../config.typ": * -#h1(offset: whole, tr(( - en: [Zero Cost Abstractions], +#h1((en: [Zero Cost Abstractions], de: [Abstraktionen ohne Kosten], + ja: [ゼロコスト抽象化], zh: [零成本抽象], -))) +), offset: whole) #tr(( en: [ @@ -23,6 +23,11 @@ de: [ enthalten, besitzen sie zur Laufzeit keine tatsächliche Repräsentation im Speicher: ], +ja: [ + 型状態はゼロコスト抽象化の優れた例でもあります。特定の動作を、コンパイル時の実行もしくは解析に移動する機能です。 + これらの型状態は、実際のデータを含んでおらず、代わりにマーカとして使われています。 + 型状態はデータを含んでいないため、実行時、メモリ内に実際のデータはありません。 +], zh: [ 类型状态是一个零成本抽象的杰出案例 - 把某些行为移到编译时执行或者分析的能力。这些类型状态不包含真实的数据,只用来作为标记。因为它们不包含数据,在运行时它们在内存中不存在实际的表示。 @@ -40,6 +45,7 @@ let _ = size_of::>(); // == 0 = #tr(( en: [Zero Sized Types], de: [Typen der Größe Null], + ja: [ゼロサイズの型], zh: [零大小的类型(Zero Sized Types)], )) @@ -62,6 +68,11 @@ de: [ darauf erstellen usw. --, werden sie vom Optimierer vollständig entfernt. ], +ja: [ + 上記のように定義された構造体をゼロサイズの型、と呼びます。これは、実際のデータを含んでいません。 + これらの型は、コンパイル時には「実際に」機能します。例えば、コピーも、ムーブも、参照を取ることもできます。 + しかし、最適化はこれらを完全に取り除きます。 +], zh: [ 像这样定义的结构体被称为零大小的类型,因为它们不包含实际数据。虽然这些类型在编译时像是"真实的"(real) - 你可以拷贝它们,移动它们,引用它们,等等,然而优化器将会完全跳过它们。 ])) @@ -73,6 +84,9 @@ en: [ de: [ In diesem Code-Ausschnitt: ], +ja: [ + 次のコードスニペットを見てください。 +], zh: [ 在这个代码片段里: ])) @@ -109,6 +123,12 @@ de: [ für die Zustandsverwaltung von `GpioConfig` und führt zu demselben Maschinencode wie ein direkter Registerzugriff. ], +ja: [ + 実行時、返り値のGpioConfigは、存在しません。この関数を呼び出すと、通常、1つのアセンブリ命令にまとめられます。 + そのアセンブリ命令は、定数のレジスタ値を、レジスタの位置へ格納します。 + これは、開発した型状態インタフェースが、ゼロコスト抽象化であることを意味します。 + `GpioConfig`の状態を追跡するために、余分なCPU、RAM、コード領域を使用せず、直接レジスタアクセスするのと同じ機械語を表します。 +], zh: [ 我们返回的GpioConfig在运行时并不存在。对这个函数的调用通常会被归纳为一条汇编指令 - 把一个常量寄存器值存进一个寄存器里。这意味着我们开发的类型状态接口是一个零成本抽象 @@ -118,6 +138,7 @@ zh: [ = #tr(( en: [Nesting], de: [Verschachtelung], + ja: [ネスト], zh: [嵌套], )) @@ -133,6 +154,9 @@ de: [ Typen ohne Größe (Zero-Sized Types) handelt, existiert die gesamte Struktur zur Laufzeit nicht. ], +ja: [ + 通常、これらの抽象化は、望み通りの深さでネストされます。使用される全てのコンポーネントが、ゼロサイズの型である限り、実行時には、構造体全体が存在しません。 +], zh: [ 通常,这些抽象可能会被深深地嵌套起来。一旦结构体使用的所有的组件是零大小类型的,整个结构体将不会在运行时存在。 ])) @@ -149,6 +173,10 @@ de: [ lassen sich Makros verwenden, um sämtliche Implementierungen zu generieren. ], +ja: [ + 複雑な構造体や深くネストした構造体については、全ての取り得る状態の組み合わせを定義することは、面倒です。 + このような場合、全ての実装を生成するために、マクロが利用できます。 +], zh: [ 对于复杂或者深度嵌套的结构体,定义所有可能的状态组合可能很乏味。在这些例子中,宏可能可以被用来生成所有的实现。 ])) diff --git a/typ/unsorted/index.typ b/typ/unsorted/index.typ index c723fb68..513a8bd3 100644 --- a/typ/unsorted/index.typ +++ b/typ/unsorted/index.typ @@ -1,8 +1,8 @@ #import "../config.typ": * -#h1(tr(( - en: [Unsorted topics], +#h1((en: [Unsorted topics], de: [Unsortierte Themen], + ja: [未分類のトピック], zh: [没有排序的主题], -))) +)) diff --git a/typ/unsorted/math.typ b/typ/unsorted/math.typ index 7d1f76a8..97f29e4c 100644 --- a/typ/unsorted/math.typ +++ b/typ/unsorted/math.typ @@ -1,10 +1,9 @@ #import "../config.typ": * -#h1(offset: whole, tr(( - en: [Performing math functionality with `#[no_std]`], +#h1((en: [Performing math functionality with `#[no_std]`], de: [Mathematische Funktionen mit `#[no_std]` nutzen], zh: [在`#[no_std]`下执行数学运算], -))) +), offset: whole) #tr(( en: [ diff --git a/typ/unsorted/speed-vs-size.typ b/typ/unsorted/speed-vs-size.typ index 31830c82..b4d8186d 100644 --- a/typ/unsorted/speed-vs-size.typ +++ b/typ/unsorted/speed-vs-size.typ @@ -1,10 +1,10 @@ #import "../config.typ": * -#h1(offset: whole, tr(( - en: [Optimizations: the speed size tradeoff], +#h1((en: [Optimizations: the speed size tradeoff], de: [Optimierungen: Der Kompromiss zwischen Geschwindigkeit und Größe], + ja: [最適化:速度とサイズのトレードオフ], zh: [优化: 速度与大小之间的博弈], -))) +), offset: whole) #tr(( en: [ @@ -20,6 +20,10 @@ de: [ `rustc` und deren Auswirkungen auf die Ausführungszeit sowie die Größe der Binärdatei eines Programms. ], +ja: [ + 誰もがプログラムを超高速で超小さくしたいと願いますが、両方の特性を最大化することはできません。 + このセクションは、`rustc`が提供する異なる最適化レベルについて、どのようにプログラムの実行時間とバイナリサイズに影響するかを説明します。 +], zh: [ 每个人都想要程序变得即快又小,但是同时满足这两个条件是不可能的。这部分讨论`rustc`提供的不同的优化等级,和它们是如何影响执行时间和一个程序的二进制项的大小。 ])) @@ -27,6 +31,7 @@ zh: [ = #tr(( en: [No optimizations], de: [Keine Optimierungen], + ja: [最適化なし], zh: [无优化], )) @@ -44,6 +49,11 @@ de: [ Debug-Informationen aktiviert, jedoch _keinerlei_ Optimierungen vorgenommen; es wird also `-C opt-level=0` verwendet. ], +ja: [ + これはデフォルトです。`cargo build`を実行する場合、開発(別名`dev`)プロファイルを使います。 + このプロファイルはデバッグに最適化されています。そのため、デバッグ情報が有効化されており、最適化は一切有効化されて_いません_。 + つまり、`-C opt-level = 0`を使用します。 +], zh: [ 这是默认的。当你调用`cargo build`时,你使用的是development(又叫`dev`)配置。这个配置优化的目的是为了调试,因此它使能了调试信息且_关闭_了所有优化,i.e.~它使用 `-C opt-level = 0` 。 @@ -64,6 +74,11 @@ de: [ Dies ermöglicht Ihnen die Verwendung von Breakpoints beim Debuggen von Release-Builds. ], +ja: [ + 少なくともベアメタルの開発では、デバッグ情報はある意味ゼロコストです。 + デバッグ情報は、Flash/ROMの容量を使いません。そのため、リリースプロファイルで、デフォルトで無効化されているデバッグ情報を有効化することをお勧めします。 + これにより、リリースビルドをデバッグする時、ブレイクポイントを使うことができます。 +], zh: [ 至少对于裸机开发来说,调试信息不会占用Flash/ROM中的空间,意味着在这种情况下,调试信息是零开销的,因此实际上我们推荐你在release配置中使能调试信息 -- 默认它被关闭了。那会让你调试release版本的固件时可以使用断点。 @@ -74,6 +89,7 @@ zh: [ # " + ts(( en: "symbols are nice and they don't increase the size on Flash", de: "Symbole sind praktisch und vergroessern die Dateigroesse im Flash nicht", + ja: "シンボルは素晴らしく、Flashのサイズを増やしません", zh: "调试符号很好且它们不会增加Flash上的大小", )) + " debug = true @@ -95,6 +111,11 @@ de: [ führt der Versuch, Variablen auszugeben, hingegen zur Meldung `$0 = `. ], +ja: [ + 最適化しないことはデバッグでは重要です。コードをステップ実行する時、プログラムをステートメントごとに実行しているように感じられるからです。 + さらに、スタックの変数や関数の引数をGDBで`print`することができます。 + コードが最適化されると、変数を表示しようとしても、`$0 = `と表示されます。 +], zh: [ 无优化对于调试来说是最好的选择,因为单步调试代码感觉像是你正在逐条语句地执行程序,且你能在GDB中`print`栈变量和函数参数。当代码被优化了,尝试打印变量会导致`$0 = `被打印出来。 ])) @@ -115,6 +136,11 @@ de: [ womöglich gar nicht verfügt. Die Folge: Die nicht optimierte Binärdatei passt nicht auf das Gerät! ], +ja: [ + `dev`プロファイルの最大の欠点は、バイナリが大きく、遅いことです。 + バイナリサイズは通常、より問題となります。最適化されていないバイナリは数十KiBもFlashを専有するからです。 + ターゲットデバイスは、数十KiBものFlashを持っていないかもしれず、最適化されていないバイナリは、デバイス内に納まりません。 +], zh: [ `dev`配置最大的缺点就是最终的二进制项将会变得巨大且缓慢。大小通常是一个更大的问题,因为未优化的二进制项会占据大量KiB的Flash,你的目标设备可能没这么多Flash -- 结果: 你未优化的二进制项无法烧录进你的设备中! @@ -128,6 +154,9 @@ de: [ Gibt es eine Möglichkeit, kleinere und dennoch für das Debugging geeignete Binärdateien zu erhalten? Ja, es gibt einen Trick. ], +ja: [ + デバッガで扱いやすい、小さなバイナリを作ることができるのでしょうか?できます。良いやり方があります。 +], zh: [ 我们可以有更小的,调试友好的二进制项吗?是的,这里有一个技巧。 ])) @@ -135,6 +164,7 @@ zh: [ == #tr(( en: [Optimizing dependencies], de: [Optimierung der Abhängigkeiten], + ja: [依存関係の最適化], zh: [优化依赖], )) @@ -153,6 +183,10 @@ de: [ ihrer Größe optimieren, während die oberste Crate unoptimiert und debuggfreundlich bleibt. ], +ja: [ + nightlyでは、#link(url_overrides)[`profile-overrides`]と呼ばれるCargoフィーチャがあります。これは、依存関係の最適化レベルをオーバーライドします。 + このフィーチャを使って、全ての依存クレートのサイズを最適化しながら、トップクレートを最適化しないでデバッガで扱いやすくすることができます。。 +], zh: [ 这里有个名为#link(url_overrides)[`profile-overrides`]的Cargo feature,其可以让你覆盖依赖项的优化等级。你能使用这个feature去优化所有依赖的大小,而保持顶层的crate没有被优化以致调试起来友好。 @@ -186,6 +220,9 @@ en: [ de: [ Hier ist ein Beispiel: ], +ja: [ + 例を示します。 +], zh: [ 这是一个示例: ])) @@ -207,6 +244,9 @@ en: [ de: [ Ohne die Überschreibung: ], +ja: [ + オーバーライドなしでは、次の通りです。 +], zh: [ 没有覆盖: ])) @@ -229,6 +269,9 @@ en: [ de: [ Mit der Überschreibung ], +ja: [ + オーバーライドをすると、以下のようになります。 +], zh: [ 有覆盖: ])) @@ -264,6 +307,13 @@ de: [ `profile-overrides` nutzen, um diese spezifische Abhängigkeit von der Optimierung auszunehmen. Siehe dazu das folgende Beispiel: ], +ja: [ + トップクレートのデバッグ性を失うことなしに、Flash使用量を6KiB減らしています。 + 依存クレートの中に足を踏み入れると、``のメッセージを目にするでしょう。 + しかし、依存クレートではなく、トップクレートをデバッグしたい場合がほとんどでしょう。 + 依存クレートをデバッグする必要が_ある_場合、特定の依存クレートを最適化から除外するために、`profile-overrides`フィーチャを使えます。 + 例えば、以下のようになります。 +], zh: [ 在Flash的使用上减少了6KiB,而不会损害顶层crate的可调试性。如果你步进一个依赖项,然后你将开始再次看到那些``信息,但是通常的情况下你只想调试顶层的crate而不是依赖项。如果你 _需要_ 调试一个依赖项,那么你可以使用`profile-overrides` @@ -276,6 +326,7 @@ zh: [ # " + ts(( en: "don't optimize the `cortex-m-rt` crate", de: "Optimiere das `cortex-m-rt`-Crate nicht", + ja: "`cortex-m-rt`クレートは最適化しません", zh: "不要优化`cortex-m-rt` crate", )) + " [profile.dev.package.cortex-m-rt] # + @@ -284,6 +335,7 @@ opt-level = 0 # + # " + ts(( en: "but do optimize all the other dependencies", de: "optimieren Sie jedoch alle anderen Abhaengigkeiten", + ja: "しかし、他の依存クレートは最適化します", zh: "但是优化所有其它依赖项", )) + " [profile.dev.package.\"*\"] @@ -300,6 +352,9 @@ en: [ de: [ Jetzt sind das Top-Level-Crate und `cortex-m-rt` debuggerfreundlich! ], +ja: [ + これで、トップクレートと`cortex-m-rt`クレートはデバッガで扱いやすくなります! +], zh: [ 现在顶层的crate和`cortex-m-rt`对调试器很友好! ])) @@ -307,6 +362,7 @@ zh: [ = #tr(( en: [Optimize for speed], de: [Auf Geschwindigkeit optimieren], + ja: [速度の最適化], zh: [优化速度], )) @@ -322,6 +378,10 @@ de: [ Ausführen von `cargo build --release` wird das Release-Profil verwendet, das standardmäßig auf `opt-level = 3` eingestellt ist. ], +ja: [ + 2018-09-18の`rustc`は、3つの「速度最適化」を提供しています。`opt-level = 1`、`2`と`3`です。 + `cargo build --release`を実行した時、デフォルトでは`opt-level = 3`のリリースプロファイルを使います。 +], zh: [ 自2018-09-18开始 `rustc` 支持三个 "优化速度" 的等级: `opt-level = 1`, `2` 和 `3` 。当你运行 `cargo build --release` @@ -349,6 +409,12 @@ de: [ geeigneten Bedingungen (etwa bei einer ausreichend hohen Anzahl von Iterationen) die Ausführungszeit halbieren. ], +ja: [ + `opt-level = 2`と`3`は、バイナリサイズを犠牲にして、速度を最適化します。レベル`3`はレベル`2`より、ベクトル化とインライン化を行います。 + 特に、`opt-level`が`2`以上の場合、LLVMがループを展開するのがわかるでしょう。 + ループ展開は、Flash/ROMの観点からはよりコストが高いです(例えば、配列のループをゼロにする場合、26バイトから194バイトまで増加します)。 + しかし、適切な条件では、実行時間が半分になります(例えば、イテレーションの回数が十分大きい場合)。 +], zh: [ `opt-level = 2` 和 `3` 都以二进制项大小为代价优化速度,但是等级`3`比等级`2`做了更多的向量化和内联。特别是,你将看到在`opt-level`等于或者大于`2`时LLVM将展开循环。循环展开在 @@ -368,6 +434,10 @@ de: [ Speicheraufwand also nicht leisten können, sollten Sie Ihr Programm stattdessen auf eine geringe Größe hin optimieren. ], +ja: [ + 現在、`opt-level = 2`と`3`でループ展開を無効化する方法はありません。 + ループ展開のコストを払うことができない場合、プログラムサイズの最適化をするべきです。 +], zh: [ 现在还没有办法在`opt-level = 2`和`3`的情况下关闭循环展开,因此如果你不能接受它的开销,你应该选择优化你的程序的大小。 ])) @@ -375,6 +445,7 @@ zh: [ = #tr(( en: [Optimize for size], de: [Nach Größe optimieren], + ja: [サイズの最適化], zh: [优化大小], )) @@ -392,6 +463,11 @@ de: [ aussagekräftig; allerdings soll das `"z"` signalisieren, dass damit kleinere Binärdateien erzeugt werden als mit `"s"`. ], +ja: [ + 2018-09-18の`rustc`は、2つの`サイズ最適化`を提供しています。`opt-level = "s"`と`"z"`です。 + これらの名前は、clang / LLVMから受け継いでおり、意味がわかりにくいです。 + `"z"`は、`"s"`より小さなバイナリを作る意図を意味します。 +], zh: [ 自2018-09-18开始`rustc`支持两个"优化大小"的等级: `opt-level = "s"` 和 `"z"` 。这些名字传承自 clang / LLVM @@ -408,6 +484,9 @@ de: [ sollen, ändern Sie die Einstellung `profile.release.opt-level` in der Datei `Cargo.toml` wie unten dargestellt. ], +ja: [ + リリースバイナリのサイズを最適化したい場合、`Cargo.toml`の`profile.release.opt-level`設定を下記の通り変更します。 +], zh: [ 如果你想要发布一个优化了大小的二进制项,那么改变下面展示的`Cargo.toml`中的`profile.release.opt-level`配置。 ])) @@ -417,6 +496,7 @@ zh: [ # " + ts(( en: "or \"z\"", de: "oder \"z\"", + ja: "または\"z\"", )) + " opt-level = \"s\" ") @@ -443,6 +523,15 @@ de: [ Optimierungsmöglichkeiten verpasst (etwa das Entfernen von nicht erreichbaren Programmzweigen oder das Inlinen von Closure-Aufrufen). ], +ja: [ + これらの2つの最適化レベルは、LLVMのインラインしきい値を大幅に減らします。 + インラインしきい値は、関数をインライン化するか否かを決めるために使われる基準値です。 + Rustの原則の1つは、ゼロコスト抽象化です。 + これらの抽象化は、不変条件を保持するため、多くの新しい型と小さな関数を使う傾向にあります + (例えば、`deref`や`as_ref`のような内部の値を借用するための関数)。 + そのため、インラインしきい値を低くすると、LLVMが最適化の機会を失います + (例えば、不要な分岐を削除したり、クロージャをインライン呼び出しにする、など)。 +], zh: [ 这两个优化等级极大地减小了LLVM的内联阈值,一个用来决定是否内联或者不内联一个函数的度量。Rust其中一个概念是零成本抽象;这些抽象趋向于去使用许多新类型和小函数去保持不变量(e.g.~像是`deref`,`as_ref`这样借用内部值的函数)因此一个低内联阈值会使LLVM失去优化的机会(e.g.~去掉死分支(dead branches),内联对闭包的调用)。 @@ -463,6 +552,10 @@ de: [ dieses Schwellenwerts besteht darin, das Flag `-C inline-threshold` zu den übrigen `rustflags` in der Datei `.cargo/config.toml` hinzuzufügen. ], +ja: [ + サイズの最適化を行っている時、バイナリサイズに影響があるかどうかを見るために、インラインしきい値を増やしたいかもしれません。 + インラインしきい値を変更するお勧めの方法は、`.cargo/config`内のrustflagsに`-C inline-threshold`フラグを追加することです。 +], zh: [ 当优化大小时,你可能想要尝试增加内联阈值去观察是否会对你的二进制项的大小有影响。推荐的改变内联阈值的方法是在`.cargo/config.toml`中往其它rustflags后插入`-C inline-threshold` ])) @@ -472,6 +565,7 @@ zh: [ # " + ts(( en: "this assumes that you are using the cortex-m-quickstart template", de: "Dies setzt voraus, dass Sie die cortex-m-quickstart-Vorlage verwenden", + ja: "cortex-m-quickstartテンプレートを使っていることを想定しています", zh: "这里假设你正在使用cortex-m-quickstart模板", )) + " [target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))'] @@ -499,6 +593,14 @@ de: [ - `opt-level = "s"` verwendet 75 - `opt-level = "z"` verwendet 25 ], +ja: [ + この値は何に使われるのでしょうか? + #link(url_opt_lvls)[1.29.0では、下記の値は、異なる最適化レベルで使われるインラインしきい値です] + - `opt-level = 3`は275を使います + - `opt-level = 2`は225を使います + - `opt-level = "s"`は75を使います + - `opt-level = "z"`は25を使います +], zh: [ 用什么值?#link(url_opt_lvls)[从1.29.0开始,这些是不同优化级别使用的内联阈值]: - `opt-level = 3` 使用 275 @@ -515,6 +617,9 @@ de: [ Du solltest `225` und `275` ausprobieren, wenn du auf die Größe optimierst. ], +ja: [ + サイズの最適化をするときは、`225`と`275`を試してみるべきです。 +], zh: [ 当优化大小时,你应该尝试`225`和`275` 。 ])) From 9fcb4f0f0eaa5c4dfa2b68c1b5ec250131621bd9 Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Tue, 11 Aug 2026 07:57:14 +0300 Subject: [PATCH 10/11] refactor mdbook --- typ/book.typ | 3 +- typ/config.typ | 2 - typ/mdbook/lib.typ | 113 ++++++++++++++++++++++++--------------------- 3 files changed, 63 insertions(+), 55 deletions(-) diff --git a/typ/book.typ b/typ/book.typ index b97fdd49..947761eb 100644 --- a/typ/book.typ +++ b/typ/book.typ @@ -420,5 +420,6 @@ sources, lang, languages, - book_title: book_title, + book_title: "The Embedded Rust Book", + git: "https://github.com/rust-embedded/book", ) diff --git a/typ/config.typ b/typ/config.typ index b2390083..026a6bf5 100644 --- a/typ/config.typ +++ b/typ/config.typ @@ -59,8 +59,6 @@ // str: use in code blocks #let ts = tr.with(default: todos) -#let book_title = "The Embedded Rust Book" - #let h1(it, offset: 0) = { if type(it) == dictionary { it = tr(it) diff --git a/typ/mdbook/lib.typ b/typ/mdbook/lib.typ index 31ae7400..de0c9a7c 100644 --- a/typ/mdbook/lib.typ +++ b/typ/mdbook/lib.typ @@ -42,7 +42,7 @@ #html.link(rel: "stylesheet", href: pth+"fonts/fonts-9644e21d.css") ] -#let html_head(title, root_pth) = html.head[ +#let html_head(title, root_pth, lang-list: true) = html.head[ #html.meta(charset: "utf-8") #html.title(title) @@ -55,7 +55,9 @@ #html.elem("link", attrs: (rel: "shortcut icon", href: root_pth+"favicon-8114d1fc.png")) #html_head_common(root_pth) - #html.style("#language-list { left: auto; right: 10px; } #language-list a { color: inherit; }") + #if lang-list { + html.style("#language-list { left: auto; right: 10px; } #language-list a { color: inherit; }") + } // Highlight.js Stylesheets #html.link(rel: "stylesheet", id: "mdbook-highlight-css", href: root_pth+"highlight-493f70e1.css") @@ -128,7 +130,8 @@ items } -#let page_header(pth, root_pth, lang, languages, default_lang) = [ +#let page_header(data, pth) = [ + #let root_pth = data.root_prefix+pth #html.div(id: "mdbook-menu-bar-hover-placeholder") #html.div(id: "mdbook-menu-bar", class: ("menu-bar", "sticky"))[ #html.div(class: "left-buttons")[ @@ -153,22 +156,26 @@ ] #html.button(id: "mdbook-search-toggle", class: "icon-button", type: "button", title: "Search (`/`)", aria-label: "Toggle Searchbar", aria-expanded: false, aria-keyshortcuts: "/ s", aria-controls: "mdbook-searchbar", builtin-icon("search")) ] - #html.h1(class: "menu-title")[The Embedded Rust Book] + #html.h1(class: "menu-title", data.book_title) #html.div(class: "right-buttons")[ - #html.button(id: "language-toggle", class: "icon-button", type: "button", title: "Change language", aria-label: "Change language", aria-haspopup: true, aria-expanded: false, aria-controls: "language-list", builtin-icon("globe")) - #html.ul(id: "language-list", class: "theme-popup", aria-label: "Languages", role: "menu")[ - #for (l, name) in languages [ - #html.li(role: none, - html.a(role: "menuitem", class: "theme", id: "lang-"+l, name, href: root_pth+if l == default_lang { - "" - } else { - l+"/" - }+"index.html")) + #if data.multilingual [ + #html.button(id: "language-toggle", class: "icon-button", type: "button", title: "Change language", aria-label: "Change language", aria-haspopup: true, aria-expanded: false, aria-controls: "language-list", builtin-icon("globe")) + #html.ul(id: "language-list", class: "theme-popup", aria-label: "Languages", role: "menu")[ + #for (l, name) in data.languages [ + #html.li(role: none, + html.a(role: "menuitem", class: "theme", id: "lang-"+l, name, href: root_pth+if l == data.default_lang { + "" + } else { + l+"/" + }+"index.html")) + ] ] + #builtin-script("show-language-list") ] - #builtin-script("show-language-list") - #html.a(href: root_pth+"book_"+lang+".pdf", title: "PDF version of this book", aria-label: "PDF", builtin-icon("pdf", id: "book-pdf")) - #html.a(href: "https://github.com/rust-embedded/book", title: "Git repository", aria-label: "Git repository", builtin-icon("github")) + #html.a(href: root_pth+"book_"+data.lang+".pdf", title: "PDF version of this book", aria-label: "PDF", builtin-icon("pdf", id: "book-pdf")) + #if data.git != none { + html.a(href: data.git, title: "Git repository", aria-label: "Git repository", builtin-icon("github")) + } ] ] #html.div(id: "mdbook-search-wrapper", class: "hidden")[ @@ -235,18 +242,14 @@ } } ] -#let toc(tree, out, lang, default_lang) = document(out)[ - #html.html(lang: lang, class: "light", dir: ltr)[ +#let toc(tree, out, data) = document(out)[ + #html.html(lang: data.lang, class: "light", dir: ltr)[ #html.head[ #html.meta(charset: "utf-8") #html.meta(name:"robots", content: "noindex") #html.meta(name: "viewport", content: "width=device-width, initial-scale=1") #html.meta(name: "theme-color", content: "#ffffff") - #html_head_common(if lang == default_lang { - "" - } else { - "../" - }) + #html_head_common(data.root_prefix) #html.title() ] #html.body(class: "sidebar-iframe-inner")[ @@ -269,25 +272,19 @@ #let book_page( source, out, - lang, - languages, - default_lang, + data, title, depth, sidebar, prev_path, next_path, ) = document(out)[ - #html.html(lang: lang, class: ("light", "sidebar-visible"), dir: ltr)[ + #html.html(lang: data.lang, class: ("light", "sidebar-visible"), dir: ltr)[ #let pth = (("../",)*depth).join("") - #let root_pth = if lang == default_lang { - "" - } else { - "../" - }+pth - #html_head(title, root_pth) + #let root_pth = data.root_prefix+pth + #html_head(to-string(title) + " - " + data.book_title, root_pth, lang-list: data.multilingual) // Start loading toc.js asap - #html.script(src: pth+"toc-7ac66f26_"+lang+".js") + #html.script(src: pth+"toc-7ac66f26_"+data.lang+".js") #html.body[ #help_container() #html.div(id: "mdbook-body-container")[ @@ -295,7 +292,7 @@ #sidebar #html.div(id: "mdbook-page-wrapper", class: "page-wrapper")[ #html.div(class: "page")[ - #page_header(pth, root_pth, lang, languages, default_lang) + #page_header(data, pth) #html.div(id: "mdbook-content", class: "content")[ #html.main[ #source @@ -347,33 +344,28 @@ #let html_book( sources, - lang, - languages, - book_title, - default_lang, + data, ) = { let sa = source_array(sources) - if lang == default_lang { + if data.is_root { for path in asset_list { asset(path, read("assets/"+path, encoding: none)) } } - let prefix = if lang != default_lang { lang+"/" } else { "" } + asset(data.prefix+"toc-7ac66f26_"+data.lang+".js", read("assets/"+"toc-7ac66f26.js_part1")+elem-to-string(toc_tree(sources, prefix: data.prefix))+read("assets/"+"toc-7ac66f26.js_part2")) - asset(prefix+"toc-7ac66f26_"+lang+".js", read("assets/"+"toc-7ac66f26.js_part1")+elem-to-string(toc_tree(sources, prefix: prefix))+read("assets/"+"toc-7ac66f26.js_part2")) - - toc(toc_tree(sources), prefix+"toc.html", lang, default_lang) + toc(toc_tree(sources), data.prefix+"toc.html", data) for (i, (path, source, title)) in sa.enumerate() { let (prev_path, _, _) = if i > 0 { sa.at(i - 1) } else { (none, none, none) } let (next_path, _, _) = sa.at(i + 1, default: (none, none, none)) - let out = prefix+path + ".html" + let out = data.prefix+path + ".html" let depth = path.split("/").len() - 1 [ - #book_page(source, out, lang, languages, default_lang, to-string(title) + " - " + book_title, depth, sidebar(depth), prev_path, next_path) #lbl(path) + #book_page(source, out, data, title, depth, sidebar(depth), prev_path, next_path) #lbl(path) ] if i == 0 { let depth = 0 - book_page(source, prefix+"index.html", lang, languages, default_lang, to-string(title) + " - " + book_title, depth, sidebar(depth), prev_path, next_path) + book_page(source, data.prefix+"index.html", data, title, depth, sidebar(depth), prev_path, next_path) } } } @@ -397,6 +389,9 @@ #outline(depth: 3) #let part = "main" + + #set page(numbering: "1", number-align: right + top) + #for (path, source, _) in sa { let offset = path.split("/").len() - 1 if path.ends-with("/index") { @@ -420,13 +415,30 @@ lang, languages, book_title: "The Book", + git: none, default_lang: "en", ) = { + let is_root = lang == default_lang + let data = ( + lang: lang, + languages: languages, + is_root: is_root, + multilingual: languages.keys().len() > 1, + prefix: if is_root { "" } else { lang+"/" }, + root_prefix: if is_root { + "" + } else { + "../" + }, + book_title: book_title, + git: git, + default_lang: default_lang, + ) if tgt == "pdf" { pdf_book( [ #v(1fr) - #align(center, text(size: 40pt, [#book_title\ (#languages.at(lang))])) + #align(center, text(size: 40pt, [#book_title\ (#data.languages.at(data.lang))])) #v(1fr) ], sources, @@ -434,10 +446,7 @@ } else if tgt == "html" { html_book( sources, - lang, - languages, - book_title, - default_lang + data, ) } } From 0043c41a8d13f5517586113cf8277b97d22eec6c Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Wed, 12 Aug 2026 15:13:08 +0300 Subject: [PATCH 11/11] just --- .github/workflows/ci-typ.yml | 32 +-- Justfile | 37 +++ typ/mdbook/book.typ | 450 ++++++++++++++++++++++++++++++++++ typ/mdbook/code.typ | 39 +++ typ/mdbook/lib.typ | 454 +---------------------------------- typ/mdbook/typst.toml | 6 +- 6 files changed, 544 insertions(+), 474 deletions(-) create mode 100644 Justfile create mode 100644 typ/mdbook/book.typ create mode 100644 typ/mdbook/code.typ diff --git a/.github/workflows/ci-typ.yml b/.github/workflows/ci-typ.yml index 341495ce..0b4e1a6f 100644 --- a/.github/workflows/ci-typ.yml +++ b/.github/workflows/ci-typ.yml @@ -28,43 +28,33 @@ jobs: id: cache-cargo-typst with: path: ~/cargo-bin - key: cache-cargo-typst + key: cache-cargo-typst-0.15.1 - name: Install Typst if: steps.cache-cargo-typst.outputs.cache-hit != 'true' uses: actions-rs/install@v0.1 with: crate: typst-cli version: latest + - name: Install Just + if: steps.cache-cargo-typst.outputs.cache-hit != 'true' + uses: actions-rs/install@v0.1 + with: + crate: just + version: latest - name: Copy typst to cache directory if: steps.cache-cargo-typst.outputs.cache-hit != 'true' run: | mkdir -p ~/cargo-bin + cp ~/.cargo/bin/just ~/cargo-bin cp ~/.cargo/bin/typst ~/cargo-bin - name: Put new cargo binary directory into path run: echo "~/cargo-bin" >> $GITHUB_PATH - - name: Build original (English) book - run: | - typst compile typ/book.typ --input lang=en --input target=html book --features bundle,html -f bundle - typst compile typ/book.typ --input lang=en --input target=pdf book/book_en.pdf + - name: Build book + run: just build - - name: Build translated book - run: | - typst compile typ/book.typ --input lang=de --input target=html book --features bundle,html -f bundle - typst compile typ/book.typ --input lang=de --input target=pdf book/book_de.pdf - typst compile typ/book.typ --input lang=de --input target=pdf book/book_de_tr.pdf --input goal=translation - typst compile typ/book.typ --input lang=ja --input target=html book --features bundle,html -f bundle - typst compile typ/book.typ --input lang=ja --input target=pdf book/book_ja.pdf - typst compile typ/book.typ --input lang=ja --input target=pdf book/book_ja_tr.pdf --input goal=translation - typst compile typ/book.typ --input lang=uk --input target=html book --features bundle,html -f bundle - typst compile typ/book.typ --input lang=uk --input target=pdf book/book_uk.pdf - typst compile typ/book.typ --input lang=uk --input target=pdf book/book_uk_tr.pdf --input goal=translation - typst compile typ/book.typ --input lang=zh --input target=html book --features bundle,html -f bundle - typst compile typ/book.typ --input lang=zh --input target=pdf book/book_zh.pdf - typst compile typ/book.typ --input lang=zh --input target=pdf book/book_zh_tr.pdf --input goal=translation - - name: Check links - run: linkchecker book + run: just check-links - name: Deploy book if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} diff --git a/Justfile b/Justfile new file mode 100644 index 00000000..7f51cf2b --- /dev/null +++ b/Justfile @@ -0,0 +1,37 @@ +clean: + rm -r book + +build-en: + typst compile typ/book.typ --input lang=en --input target=html book --features bundle,html -f bundle + typst compile typ/book.typ --input lang=en --input target=pdf book/book_en.pdf + +build-de: + typst compile typ/book.typ --input lang=de --input target=html book --features bundle,html -f bundle + typst compile typ/book.typ --input lang=de --input target=pdf book/book_de.pdf + typst compile typ/book.typ --input lang=de --input target=pdf book/book_de_tr.pdf --input goal=translation + +build-ja: + typst compile typ/book.typ --input lang=ja --input target=html book --features bundle,html -f bundle + typst compile typ/book.typ --input lang=ja --input target=pdf book/book_ja.pdf + typst compile typ/book.typ --input lang=ja --input target=pdf book/book_ja_tr.pdf --input goal=translation + +build-uk: + typst compile typ/book.typ --input lang=uk --input target=html book --features bundle,html -f bundle + typst compile typ/book.typ --input lang=uk --input target=pdf book/book_uk.pdf + typst compile typ/book.typ --input lang=uk --input target=pdf book/book_uk_tr.pdf --input goal=translation + +build-zh: + typst compile typ/book.typ --input lang=zh --input target=html book --features bundle,html -f bundle + typst compile typ/book.typ --input lang=zh --input target=pdf book/book_zh.pdf + typst compile typ/book.typ --input lang=zh --input target=pdf book/book_zh_tr.pdf --input goal=translation + +build: build-en build-de build-ja build-uk build-zh + +check-links: + linkchecker book + +format-html: + for file in `find book -name "*.html"`; do \ + echo $file; \ + tidy -qim --alt-text "inlined image" --tidy-mark no --warn-proprietary-attributes no -w 120 --custom-tags blocklevel $file; \ + done diff --git a/typ/mdbook/book.typ b/typ/mdbook/book.typ new file mode 100644 index 00000000..3f5b5450 --- /dev/null +++ b/typ/mdbook/book.typ @@ -0,0 +1,450 @@ +#import "icons.typ": builtin-icon +#import "scripts.typ": builtin-script + +#let lbl(path) = label(path.replace("/","-")) + +#let to-string(content) = { + if content.has("text") { + content.text + } else if content.has("children") { + content.children.map(to-string).join("") + } else if content.has("body") { + to-string(content.body) + } else if content == [] { + " " + } +} + +#let elem-to-string(content) = { + if content == none { + "" + } else if content.has("children") { + content.children.map(elem-to-string).join("") + } else if content.has("tag") { + let attrs = "" + if content.has("attrs") { + for (k, v) in content.attrs { + attrs = attrs+" "+k+"=\""+v+"\"" + } + } + "<"+content.tag+attrs+">"+elem-to-string(content.body)+"" + } else { + to-string(content) + } +} + +#let html_head_common(pth) = { + html.link(rel: "stylesheet", href: pth+"css/variables-8adf115d.css") + html.link(rel: "stylesheet", href: pth+"css/general-e96d0476.css") + html.link(rel: "stylesheet", href: pth+"css/chrome-d279d366.css") + html.link(rel: "stylesheet", href: pth+"css/print-9e4910d8.css", media: "print") + // Fonts + html.link(rel: "stylesheet", href: pth+"fonts/fonts-9644e21d.css") +} + +#let html_head(title, root_pth, lang-list: true) = html.head({ + html.meta(charset: "utf-8") + html.title(title) + + html.meta(name: "description", content:"") + html.meta(name: "viewport", content: "width=device-width, initial-scale=1") + html.meta(name: "theme-color", content: "#ffffff") + + // Custom HTML head + html.link(rel: "icon", href: root_pth+"favicon-de23e50b.svg") + html.elem("link", attrs: (rel: "shortcut icon", href: root_pth+"favicon-8114d1fc.png")) + html_head_common(root_pth) + + if lang-list { + html.style("#language-list { left: auto; right: 10px; } #language-list a { color: inherit; }") + } + + // Highlight.js Stylesheets + html.link(rel: "stylesheet", id: "mdbook-highlight-css", href: root_pth+"highlight-493f70e1.css") + html.link(rel: "stylesheet", id: "mdbook-tomorrow-night-css", href: root_pth+"tomorrow-night-4c0ae647.css") + html.link(rel: "stylesheet", id: "mdbook-ayu-highlight-css", href: root_pth+"ayu-highlight-3fdfc3ac.css") + + // Provide site root and default themes to javascript + builtin-script("provide-themes", root_pth: root_pth) +}) + +#let help_container() = html.div(id: "mdbook-help-container", + html.div(id: "mdbook-help-popup", { + html.h2(class: "mdbook-help-title")[Keyboard shortcuts] + html.div({ + html.p[Press #html.kbd[←] or #html.kbd[→] to navigate between chapters] + html.p[Press #html.kbd[S] or #html.kbd[/] to search in the book] + html.p[Press #html.kbd[?] to show this help] + html.p[Press #html.kbd[Esc] to hide this help] + }) + }) +) + +#let body_header() = { + // Work around some values being stored in localStorage wrapped in quotes + builtin-script("quote-workaround") + // Set the theme before any content is loaded, prevents flash + builtin-script("init-theme") + html.input(type: "checkbox", id: "mdbook-sidebar-toggle-anchor", class: "hidden") + // Hide / unhide sidebar before it is displayed + builtin-script("hide-sidebar") +} + +#let icon_templates() = { + html.template(id: "fa-eye", builtin-icon("eye")) + html.template(id: "fa-eye-slash", builtin-icon("eye-slash")) + html.template(id: "fa-copy", builtin-icon("copy")) + html.template(id: "fa-play", builtin-icon("play")) + html.template(id: "fa-clock-rotate-left", builtin-icon("clock-rotate-left")) +} + +#let footer_scripts(pth) = { + html.script("window.playground_copyable = true;") + html.script(src: pth+"elasticlunr-ef4e11c1.min.js") + html.script(src: pth+"mark-09e88c2c.min.js") + html.script(src: pth+"searcher-09f2665d.js") + html.script(src: pth+"clipboard-1626706a.min.js") + html.script(src: pth+"highlight-abc7f01d.js") + html.script(src: pth+"book-609e4cb8.js") +} + +#let source_array(sources) = { + let items = () + for (path, src) in sources.pairs() { + items.push((path, src.at("content"), src.at("title"))) + let sub = src.at("sub", default: none) + if sub != none { + for (path, src) in sub.pairs() { + items.push((path, src.at("content"), src.at("title"))) + let sub = src.at("sub", default: none) + if sub != none { + for (path, src) in sub.pairs() { + items.push((path, src.at("content"), src.at("title"))) + } + } + } + } + } + items +} + +#let page_header(data, pth) = { + let root_pth = data.root_prefix+pth + html.div(id: "mdbook-menu-bar-hover-placeholder") + html.div(id: "mdbook-menu-bar", class: ("menu-bar", "sticky"), { + html.div(class: "left-buttons", { + let attrs = (id: "mdbook-sidebar-toggle", class: "icon-button", title: "Toggle Table of Contents", aria-label: "Toggle Table of Contents", aria-controls: "mdbook-sidebar") + attrs.insert("for", "mdbook-sidebar-toggle-anchor") + html.elem("label", attrs: attrs, builtin-icon("bars")) + html.button(id: "mdbook-theme-toggle", class: "icon-button", type: "button", title: "Change theme", aria-label: "Change theme", aria-haspopup: true, aria-expanded: false, aria-controls: "mdbook-theme-list", builtin-icon("paintbrush")) + html.ul(id: "mdbook-theme-list", class: "theme-popup", aria-label: "Themes", role: "menu", { + let themes = ( + default_theme: "Auto", + light: "Light", + rust: "Rust", + coal: "Coal", + navy: "Navy", + ayu: "Ayu", + ) + for (id, name) in themes.pairs() { + html.li(role: none, + html.button(role: "menuitem", class: "theme", id: "mdbook-theme-"+id, name) + ) + } + }) + html.button(id: "mdbook-search-toggle", class: "icon-button", type: "button", title: "Search (`/`)", aria-label: "Toggle Searchbar", aria-expanded: false, aria-keyshortcuts: "/ s", aria-controls: "mdbook-searchbar", builtin-icon("search")) + }) + html.h1(class: "menu-title", data.book_title) + html.div(class: "right-buttons", { + if data.multilingual { + html.button(id: "language-toggle", class: "icon-button", type: "button", title: "Change language", aria-label: "Change language", aria-haspopup: true, aria-expanded: false, aria-controls: "language-list", builtin-icon("globe")) + html.ul(id: "language-list", class: "theme-popup", aria-label: "Languages", role: "menu", + for (l, name) in data.languages { + html.li(role: none, + html.a(role: "menuitem", class: "theme", id: "lang-"+l, name, href: root_pth+if l == data.default_lang { + "" + } else { + l+"/" + }+"index.html")) + } + ) + builtin-script("show-language-list") + } + html.a(href: root_pth+"book_"+data.lang+".pdf", title: "PDF version of this book", aria-label: "PDF", builtin-icon("pdf", id: "book-pdf")) + if data.git != none { + html.a(href: data.git, title: "Git repository", aria-label: "Git repository", builtin-icon("github")) + } + }) + }) + html.div(id: "mdbook-search-wrapper", class: "hidden", { + html.form(id: "mdbook-searchbar-outer", class: "searchbar-outer", + html.div(class: "search-wrapper", { + html.input(type: "search", id: "mdbook-searchbar", name: "searchbar", placeholder: "Search this book ...", aria-controls: "mdbook-searchresults-outer", aria-describedby: "searchresults-header") + html.div(class: "spinner-wrapper", builtin-icon("spinner", id: "fa-spin")) + }) + ) + html.div(id: "mdbook-searchresults-outer", class: "searchresults-outer hidden", { + html.div(id: "mdbook-searchresults-header", class: "searchresults-header") + html.ul(id: "mdbook-searchresults") + }) + }) + + // Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM + builtin-script("apply-aria") +} + +#let sidebar(depth) = html.nav(id: "mdbook-sidebar", class: "sidebar", aria-label: "Table of contents", { + html.elem("mdbook-sidebar-scrollbox", attrs:(class: "sidebar-scrollbox")) + html.noscript({ + let pth = (("../",)*depth).join("") + html.iframe(class: "sidebar-iframe-outer", src: pth+"toc.html") + }) + html.div(id: "mdbook-sidebar-resize-handle", class:"sidebar-resize-handle", + html.div(class: "sidebar-resize-indicator") + ) +}) +#let toc_tree(sources, prefix: "") = html.ol(class: "chapter", { + let num = (0,) + for (path, src) in sources.pairs() { + let d = 0 + num = num.slice(0, d+1) + num.at(d) = num.at(d) + 1 + html.li(class: ("chapter-item", "expanded"), html.span(class: "chapter-link-wrapper", + html.a(href: prefix+path+".html", target: "_parent", [#html.strong(aria-hidden: true, num.map(i => [#i.]).join[]) #src.at("title")]) + )) + if "sub" in src { + html.ol(class: "section", { + num.push(0) + for (path, src) in src.at("sub").pairs() { + let d = 1 + num = num.slice(0, d+1) + num.at(d) = num.at(d) + 1 + html.li(class: ("chapter-item", "expanded"), html.span(class: "chapter-link-wrapper", + html.a(href: prefix+path+".html", target: "_parent", [#html.strong(aria-hidden: true, num.map(i => [#i.]).join[]) #src.at("title")]) + )) + if "sub" in src { + html.ol(class: "section", { + num.push(0) + for (path, src) in src.at("sub").pairs() { + let d = 2 + num = num.slice(0, d+1) + num.at(d) = num.at(d) + 1 + html.li(class: ("chapter-item", "expanded"), html.span(class: "chapter-link-wrapper", + html.a(href: prefix+path+".html", target: "_parent", [#html.strong(aria-hidden: true, num.map(i => [#i.]).join[]) #src.at("title")]) + )) + } + }) + } + } + }) + } + } +}) +#let toc(tree, out, data) = document(out, + html.html(lang: data.lang, class: "light", dir: ltr, { + html.head({ + html.meta(charset: "utf-8") + html.meta(name:"robots", content: "noindex") + html.meta(name: "viewport", content: "width=device-width, initial-scale=1") + html.meta(name: "theme-color", content: "#ffffff") + html_head_common(data.root_prefix) + html.title() + }) + html.body(class: "sidebar-iframe-inner", tree) + }) +) + +// Navigation buttons +#let nav_buttons(pth, prev, next, wide: false) = html.nav(class: if wide { "nav-wide-wrapper" } else { "nav-wrapper" }, aria-label: "Page navigation", { + let c = if wide { "nav-chapters" } else { "mobile-nav-chapters" } + if prev != none { + html.a(rel: "prev", href: pth+prev+".html", class: c + " previous", title: "Previous chapter", aria-label: "Previous chapter", aria-keyshortcuts: "Left", builtin-icon("left")) + } + if next != none { + html.a(rel: "next", href: pth+next+".html", class: c + " next", title: "Next chapter", aria-label: "Next chapter", aria-keyshortcuts: "Right", builtin-icon("right")) + } +}) + +#let book_page( + source, + out, + data, + title, + depth, + sidebar, + prev_path, + next_path, +) = document(out, + html.html(lang: data.lang, class: ("light", "sidebar-visible"), dir: ltr, { + let pth = (("../",)*depth).join("") + let root_pth = data.root_prefix+pth + html_head(to-string(title) + " - " + data.book_title, root_pth, lang-list: data.multilingual) + // Start loading toc.js asap + html.script(src: pth+"toc-7ac66f26_"+data.lang+".js") + html.body({ + help_container() + html.div(id: "mdbook-body-container", { + body_header() + sidebar + html.div(id: "mdbook-page-wrapper", class: "page-wrapper", { + html.div(class: "page", { + page_header(data, pth) + html.div(id: "mdbook-content", class: "content", { + html.main(source) + nav_buttons(pth, prev_path, next_path) + }) + }) + nav_buttons(pth, prev_path, next_path, wide: true) + }) + icon_templates() + footer_scripts(root_pth) + }) + }) + }) +) + +#let asset_list = ( + "ayu-highlight-3fdfc3ac.css", + "book-609e4cb8.js", + "clipboard-1626706a.min.js", + "css/chrome-d279d366.css", + "css/general-e96d0476.css", + "css/print-9e4910d8.css", + "css/variables-8adf115d.css", + "elasticlunr-ef4e11c1.min.js", + "favicon-8114d1fc.png", + "favicon-de23e50b.svg", + "fonts/fonts-9644e21d.css", + "fonts/OPEN-SANS-LICENSE.txt", + "fonts/open-sans-v17-all-charsets-300-7736aa35.woff2", + "fonts/open-sans-v17-all-charsets-300italic-2c7b95c0.woff2", + "fonts/open-sans-v17-all-charsets-600-486c6759.woff2", + "fonts/open-sans-v17-all-charsets-600italic-1a3e8659.woff2", + "fonts/open-sans-v17-all-charsets-700-c22fe8c7.woff2", + "fonts/open-sans-v17-all-charsets-700italic-238ae959.woff2", + "fonts/open-sans-v17-all-charsets-800-3d2c812a.woff2", + "fonts/open-sans-v17-all-charsets-800italic-ba1521ec.woff2", + "fonts/open-sans-v17-all-charsets-italic-6c9463f7.woff2", + "fonts/open-sans-v17-all-charsets-regular-2e3b1d34.woff2", + "fonts/SOURCE-CODE-PRO-LICENSE.txt", + "fonts/source-code-pro-v11-all-charsets-500-2bdd9410.woff2", + "highlight-493f70e1.css", + "highlight-abc7f01d.js", + "mark-09e88c2c.min.js", + "searcher-09f2665d.js", + "searchindex-8ec871e3.js", + "tomorrow-night-4c0ae647.css", +) + +#let html_book( + sources, + data, +) = { + let sa = source_array(sources) + if data.is_root { + for path in asset_list { + asset(path, read("assets/"+path, encoding: none)) + } + } + asset(data.prefix+"toc-7ac66f26_"+data.lang+".js", read("assets/"+"toc-7ac66f26.js_part1")+elem-to-string(toc_tree(sources, prefix: data.prefix))+read("assets/"+"toc-7ac66f26.js_part2")) + + toc(toc_tree(sources), data.prefix+"toc.html", data) + for (i, (path, source, title)) in sa.enumerate() { + let (prev_path, _, _) = if i > 0 { sa.at(i - 1) } else { (none, none, none) } + let (next_path, _, _) = sa.at(i + 1, default: (none, none, none)) + let out = data.prefix+path + ".html" + let depth = path.split("/").len() - 1 + [ + #book_page(source, out, data, title, depth, sidebar(depth), prev_path, next_path) #lbl(path) + ] + if i == 0 { + let depth = 0 + book_page(source, data.prefix+"index.html", data, title, depth, sidebar(depth), prev_path, next_path) + } + } +} + +#let pdf_book(title_page, sources) = { + import "@preview/codly:1.3.0": * + import "@preview/codly-languages:0.1.10": * + show: codly-init.with() + codly( + languages: codly-languages + (console: codly-languages.shell), + number-format: none, + fill: luma(250), + zebra-fill: luma(245) + ) + + let sa = source_array(sources) + set par(justify: true) + set heading(numbering: "1.") + show heading.where(level: 1): it => { pagebreak();it } + + show link: it => { text(fill: blue, it) } + + title_page + + outline(depth: 3) + let part = "main" + + set page(numbering: "1", number-align: right + top) + + for (path, source, _) in sa { + let offset = path.split("/").len() - 1 + if path.ends-with("/index") { + offset = offset - 1 + } + if part == "main" and path.starts-with("appendix/") { + path = "appendix" + counter(heading).update(0) + set heading(numbering: "A.1.", offset: offset+1) + source + } else { + set heading(offset: offset+1) + source + } + } +} + +#let book( + tgt, + sources, + lang, + languages, + default_lang: "en", + book_title: "The Book", + title_page: none, + git: none, +) = { + let is_root = lang == default_lang + let data = ( + lang: lang, + languages: languages, + is_root: is_root, + multilingual: languages.keys().len() > 1, + prefix: if is_root { "" } else { lang+"/" }, + root_prefix: if is_root { + "" + } else { + "../" + }, + book_title: book_title, + git: git, + default_lang: default_lang, + ) + if tgt == "pdf" { + pdf_book( + if title_page == none [ + #v(1fr) + #align(center, text(size: 40pt, [#book_title\ (#data.languages.at(data.lang))])) + #v(1fr) + ] else { title_page }, + sources, + ) + } else if tgt == "html" { + html_book( + sources, + data, + ) + } +} diff --git a/typ/mdbook/code.typ b/typ/mdbook/code.typ new file mode 100644 index 00000000..edb38dde --- /dev/null +++ b/typ/mdbook/code.typ @@ -0,0 +1,39 @@ +#import "@preview/codly:1.3.0": codly + +#let insert-code( + full_code, + tgt: "html", + lang: "rust", + block: true, + anchor: none, + lines: none, + offset: 0, + line-numbers: false, +) = { + let offset = offset + let code = if type(anchor) == str { + let lines = full_code.split("\n") + let left = lines.position(l => { regex("ANCHOR:\s+"+anchor) in l }) + offset = offset + left + let right = lines.position(l => { regex("ANCHOR_END:\s+"+anchor) in l }) + lines.slice(left+1, right).join("\n") + } else if type(lines) == int { + offset = offset + lines - 1 + full_code.split("\n").at(lines - 1) + } else if type(lines) == array { + offset = offset + lines.at(0) - 1 + full_code.split("\n").slice(lines.at(0) - 1, lines.at(1)).join("\n") + } else { + full_code + } + if tgt == "pdf" { + if line-numbers { + codly(number-format: numbering.with("1")) + } + codly(offset: offset) + raw(code, lang: lang, block: block) + codly(number-format: none) + } else { + raw(code, lang: lang, block: block) + } +} diff --git a/typ/mdbook/lib.typ b/typ/mdbook/lib.typ index de0c9a7c..f2df11d7 100644 --- a/typ/mdbook/lib.typ +++ b/typ/mdbook/lib.typ @@ -1,452 +1,2 @@ -#import "icons.typ": builtin-icon -#import "scripts.typ": builtin-script - -#let lbl(path) = label(path.replace("/","-")) - -#let to-string(content) = { - if content.has("text") { - content.text - } else if content.has("children") { - content.children.map(to-string).join("") - } else if content.has("body") { - to-string(content.body) - } else if content == [] { - " " - } -} - -#let elem-to-string(content) = { - if content == none { - "" - } else if content.has("children") { - content.children.map(elem-to-string).join("") - } else if content.has("tag") { - let attrs = "" - if content.has("attrs") { - for (k, v) in content.attrs { - attrs = attrs+" "+k+"=\""+v+"\"" - } - } - "<"+content.tag+attrs+">"+elem-to-string(content.body)+"" - } else { - to-string(content) - } -} - -#let html_head_common(pth) = [ - #html.link(rel: "stylesheet", href: pth+"css/variables-8adf115d.css") - #html.link(rel: "stylesheet", href: pth+"css/general-e96d0476.css") - #html.link(rel: "stylesheet", href: pth+"css/chrome-d279d366.css") - #html.link(rel: "stylesheet", href: pth+"css/print-9e4910d8.css", media: "print") - // Fonts - #html.link(rel: "stylesheet", href: pth+"fonts/fonts-9644e21d.css") -] - -#let html_head(title, root_pth, lang-list: true) = html.head[ - #html.meta(charset: "utf-8") - #html.title(title) - - #html.meta(name: "description", content:"") - #html.meta(name: "viewport", content: "width=device-width, initial-scale=1") - #html.meta(name: "theme-color", content: "#ffffff") - - // Custom HTML head - #html.link(rel: "icon", href: root_pth+"favicon-de23e50b.svg") - #html.elem("link", attrs: (rel: "shortcut icon", href: root_pth+"favicon-8114d1fc.png")) - #html_head_common(root_pth) - - #if lang-list { - html.style("#language-list { left: auto; right: 10px; } #language-list a { color: inherit; }") - } - - // Highlight.js Stylesheets - #html.link(rel: "stylesheet", id: "mdbook-highlight-css", href: root_pth+"highlight-493f70e1.css") - #html.link(rel: "stylesheet", id: "mdbook-tomorrow-night-css", href: root_pth+"tomorrow-night-4c0ae647.css") - #html.link(rel: "stylesheet", id: "mdbook-ayu-highlight-css", href: root_pth+"ayu-highlight-3fdfc3ac.css") - - // Provide site root and default themes to javascript - #builtin-script("provide-themes", root_pth: root_pth) -] - -#let help_container() = [ - #html.div(id: "mdbook-help-container")[ - #html.div(id: "mdbook-help-popup")[ - #html.h2(class: "mdbook-help-title")[Keyboard shortcuts] - #html.div[ - #html.p[Press #html.kbd[←] or #html.kbd[→] to navigate between chapters] - #html.p[Press #html.kbd[S] or #html.kbd[/] to search in the book] - #html.p[Press #html.kbd[?] to show this help] - #html.p[Press #html.kbd[Esc] to hide this help] - ] - ] - ] -] - -#let body_header() = [ - // Work around some values being stored in localStorage wrapped in quotes - #builtin-script("quote-workaround") - // Set the theme before any content is loaded, prevents flash - #builtin-script("init-theme") - #html.input(type: "checkbox", id: "mdbook-sidebar-toggle-anchor", class: "hidden") - // Hide / unhide sidebar before it is displayed - #builtin-script("hide-sidebar") -] - -#let icon_templates() = [ - #html.template(id: "fa-eye", builtin-icon("eye")) - #html.template(id: "fa-eye-slash", builtin-icon("eye-slash")) - #html.template(id: "fa-copy", builtin-icon("copy")) - #html.template(id: "fa-play", builtin-icon("play")) - #html.template(id: "fa-clock-rotate-left", builtin-icon("clock-rotate-left")) -] - -#let footer_scripts(pth) = [ - #html.script("window.playground_copyable = true;") - #html.script(src: pth+"elasticlunr-ef4e11c1.min.js") - #html.script(src: pth+"mark-09e88c2c.min.js") - #html.script(src: pth+"searcher-09f2665d.js") - #html.script(src: pth+"clipboard-1626706a.min.js") - #html.script(src: pth+"highlight-abc7f01d.js") - #html.script(src: pth+"book-609e4cb8.js") -] - -#let source_array(sources) = { - let items = () - for (path, src) in sources.pairs() { - items.push((path, src.at("content"), src.at("title"))) - let sub = src.at("sub", default: none) - if sub != none { - for (path, src) in sub.pairs() { - items.push((path, src.at("content"), src.at("title"))) - let sub = src.at("sub", default: none) - if sub != none { - for (path, src) in sub.pairs() { - items.push((path, src.at("content"), src.at("title"))) - } - } - } - } - } - items -} - -#let page_header(data, pth) = [ - #let root_pth = data.root_prefix+pth - #html.div(id: "mdbook-menu-bar-hover-placeholder") - #html.div(id: "mdbook-menu-bar", class: ("menu-bar", "sticky"))[ - #html.div(class: "left-buttons")[ - #let attrs = (id: "mdbook-sidebar-toggle", class: "icon-button", title: "Toggle Table of Contents", aria-label: "Toggle Table of Contents", aria-controls: "mdbook-sidebar") - #attrs.insert("for", "mdbook-sidebar-toggle-anchor") - #html.elem("label", attrs: attrs, builtin-icon("bars")) - #html.button(id: "mdbook-theme-toggle", class: "icon-button", type: "button", title: "Change theme", aria-label: "Change theme", aria-haspopup: true, aria-expanded: false, aria-controls: "mdbook-theme-list", builtin-icon("paintbrush")) - #html.ul(id: "mdbook-theme-list", class: "theme-popup", aria-label: "Themes", role: "menu")[ - #let themes = ( - default_theme: "Auto", - light: "Light", - rust: "Rust", - coal: "Coal", - navy: "Navy", - ayu: "Ayu", - ) - #for (id, name) in themes.pairs() { - html.li(role: none)[ - #html.button(role: "menuitem", class: "theme", id: "mdbook-theme-"+id)[#name] - ] - } - ] - #html.button(id: "mdbook-search-toggle", class: "icon-button", type: "button", title: "Search (`/`)", aria-label: "Toggle Searchbar", aria-expanded: false, aria-keyshortcuts: "/ s", aria-controls: "mdbook-searchbar", builtin-icon("search")) - ] - #html.h1(class: "menu-title", data.book_title) - #html.div(class: "right-buttons")[ - #if data.multilingual [ - #html.button(id: "language-toggle", class: "icon-button", type: "button", title: "Change language", aria-label: "Change language", aria-haspopup: true, aria-expanded: false, aria-controls: "language-list", builtin-icon("globe")) - #html.ul(id: "language-list", class: "theme-popup", aria-label: "Languages", role: "menu")[ - #for (l, name) in data.languages [ - #html.li(role: none, - html.a(role: "menuitem", class: "theme", id: "lang-"+l, name, href: root_pth+if l == data.default_lang { - "" - } else { - l+"/" - }+"index.html")) - ] - ] - #builtin-script("show-language-list") - ] - #html.a(href: root_pth+"book_"+data.lang+".pdf", title: "PDF version of this book", aria-label: "PDF", builtin-icon("pdf", id: "book-pdf")) - #if data.git != none { - html.a(href: data.git, title: "Git repository", aria-label: "Git repository", builtin-icon("github")) - } - ] - ] - #html.div(id: "mdbook-search-wrapper", class: "hidden")[ - #html.form(id: "mdbook-searchbar-outer", class: "searchbar-outer")[ - #html.div(class: "search-wrapper")[ - #html.input(type: "search", id: "mdbook-searchbar", name: "searchbar", placeholder: "Search this book ...", aria-controls: "mdbook-searchresults-outer", aria-describedby: "searchresults-header") - #html.div(class: "spinner-wrapper", builtin-icon("spinner", id: "fa-spin")) - ] - ] - #html.div(id: "mdbook-searchresults-outer", class: "searchresults-outer hidden")[ - #html.div(id: "mdbook-searchresults-header", class: "searchresults-header") - #html.ul(id: "mdbook-searchresults") - ] - ] - - // Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM - #builtin-script("apply-aria") -] - -#let sidebar(depth) = html.nav(id: "mdbook-sidebar", class: "sidebar", aria-label: "Table of contents")[ - #html.elem("mdbook-sidebar-scrollbox", attrs:(class: "sidebar-scrollbox")) - #html.noscript()[ - #let pth = (("../",)*depth).join("") - #html.iframe(class: "sidebar-iframe-outer", src: pth+"toc.html") - ] - #html.div(id: "mdbook-sidebar-resize-handle", class:"sidebar-resize-handle")[ - #html.div(class: "sidebar-resize-indicator") - ] -] -#let toc_tree(sources, prefix: "") = html.ol(class: "chapter")[ - #let num = (0,) - #for (path, src) in sources.pairs() { - let d = 0 - num = num.slice(0, d+1) - num.at(d) = num.at(d) + 1 - html.li(class: ("chapter-item", "expanded"))[#html.span(class: "chapter-link-wrapper")[ - #html.a(href: prefix+path+".html", target: "_parent", [#html.strong(aria-hidden: true)[#num.map(i => [#i.]).join[]] #src.at("title")]) - ]] - if "sub" in src { - html.ol(class: "section")[ - #num.push(0) - #for (path, src) in src.at("sub").pairs() { - let d = 1 - num = num.slice(0, d+1) - num.at(d) = num.at(d) + 1 - html.li(class: ("chapter-item", "expanded"))[#html.span(class: "chapter-link-wrapper")[ - #html.a(href: prefix+path+".html", target: "_parent", [#html.strong(aria-hidden: true)[#num.map(i => [#i.]).join[]] #src.at("title")]) - ]] - if "sub" in src { - html.ol(class: "section")[ - #num.push(0) - #for (path, src) in src.at("sub").pairs() { - let d = 2 - num = num.slice(0, d+1) - num.at(d) = num.at(d) + 1 - html.li(class: ("chapter-item", "expanded"))[#html.span(class: "chapter-link-wrapper")[ - #html.a(href: prefix+path+".html", target: "_parent", [#html.strong(aria-hidden: true)[#num.map(i => [#i.]).join[]] #src.at("title")]) - ]] - } - ] - } - } - ] - } - } -] -#let toc(tree, out, data) = document(out)[ - #html.html(lang: data.lang, class: "light", dir: ltr)[ - #html.head[ - #html.meta(charset: "utf-8") - #html.meta(name:"robots", content: "noindex") - #html.meta(name: "viewport", content: "width=device-width, initial-scale=1") - #html.meta(name: "theme-color", content: "#ffffff") - #html_head_common(data.root_prefix) - #html.title() - ] - #html.body(class: "sidebar-iframe-inner")[ - #tree - ] - ] -] - -// Navigation buttons -#let nav_buttons(pth, prev, next, wide: false) = html.nav(class: if wide { "nav-wide-wrapper" } else { "nav-wrapper" }, aria-label: "Page navigation")[ - #let c = if wide { "nav-chapters" } else { "mobile-nav-chapters" } - #if prev != none [ - #html.a(rel: "prev", href: pth+prev+".html", class: c + " previous", title: "Previous chapter", aria-label: "Previous chapter", aria-keyshortcuts: "Left", builtin-icon("left")) - ] - #if next != none [ - #html.a(rel: "next", href: pth+next+".html", class: c + " next", title: "Next chapter", aria-label: "Next chapter", aria-keyshortcuts: "Right", builtin-icon("right")) - ] -] - -#let book_page( - source, - out, - data, - title, - depth, - sidebar, - prev_path, - next_path, -) = document(out)[ - #html.html(lang: data.lang, class: ("light", "sidebar-visible"), dir: ltr)[ - #let pth = (("../",)*depth).join("") - #let root_pth = data.root_prefix+pth - #html_head(to-string(title) + " - " + data.book_title, root_pth, lang-list: data.multilingual) - // Start loading toc.js asap - #html.script(src: pth+"toc-7ac66f26_"+data.lang+".js") - #html.body[ - #help_container() - #html.div(id: "mdbook-body-container")[ - #body_header() - #sidebar - #html.div(id: "mdbook-page-wrapper", class: "page-wrapper")[ - #html.div(class: "page")[ - #page_header(data, pth) - #html.div(id: "mdbook-content", class: "content")[ - #html.main[ - #source - ] - #nav_buttons(pth, prev_path, next_path) - ] - ] - #nav_buttons(pth, prev_path, next_path, wide: true) - ] - #icon_templates() - #footer_scripts(root_pth) - ] - ] - ] -] - -#let asset_list = ( - "ayu-highlight-3fdfc3ac.css", - "book-609e4cb8.js", - "clipboard-1626706a.min.js", - "css/chrome-d279d366.css", - "css/general-e96d0476.css", - "css/print-9e4910d8.css", - "css/variables-8adf115d.css", - "elasticlunr-ef4e11c1.min.js", - "favicon-8114d1fc.png", - "favicon-de23e50b.svg", - "fonts/fonts-9644e21d.css", - "fonts/OPEN-SANS-LICENSE.txt", - "fonts/open-sans-v17-all-charsets-300-7736aa35.woff2", - "fonts/open-sans-v17-all-charsets-300italic-2c7b95c0.woff2", - "fonts/open-sans-v17-all-charsets-600-486c6759.woff2", - "fonts/open-sans-v17-all-charsets-600italic-1a3e8659.woff2", - "fonts/open-sans-v17-all-charsets-700-c22fe8c7.woff2", - "fonts/open-sans-v17-all-charsets-700italic-238ae959.woff2", - "fonts/open-sans-v17-all-charsets-800-3d2c812a.woff2", - "fonts/open-sans-v17-all-charsets-800italic-ba1521ec.woff2", - "fonts/open-sans-v17-all-charsets-italic-6c9463f7.woff2", - "fonts/open-sans-v17-all-charsets-regular-2e3b1d34.woff2", - "fonts/SOURCE-CODE-PRO-LICENSE.txt", - "fonts/source-code-pro-v11-all-charsets-500-2bdd9410.woff2", - "highlight-493f70e1.css", - "highlight-abc7f01d.js", - "mark-09e88c2c.min.js", - "searcher-09f2665d.js", - "searchindex-8ec871e3.js", - "tomorrow-night-4c0ae647.css", -) - -#let html_book( - sources, - data, -) = { - let sa = source_array(sources) - if data.is_root { - for path in asset_list { - asset(path, read("assets/"+path, encoding: none)) - } - } - asset(data.prefix+"toc-7ac66f26_"+data.lang+".js", read("assets/"+"toc-7ac66f26.js_part1")+elem-to-string(toc_tree(sources, prefix: data.prefix))+read("assets/"+"toc-7ac66f26.js_part2")) - - toc(toc_tree(sources), data.prefix+"toc.html", data) - for (i, (path, source, title)) in sa.enumerate() { - let (prev_path, _, _) = if i > 0 { sa.at(i - 1) } else { (none, none, none) } - let (next_path, _, _) = sa.at(i + 1, default: (none, none, none)) - let out = data.prefix+path + ".html" - let depth = path.split("/").len() - 1 - [ - #book_page(source, out, data, title, depth, sidebar(depth), prev_path, next_path) #lbl(path) - ] - if i == 0 { - let depth = 0 - book_page(source, data.prefix+"index.html", data, title, depth, sidebar(depth), prev_path, next_path) - } - } -} - -#let pdf_book(title_page, sources) = [ - #let sa = source_array(sources) - #set par(justify: true) - #set heading(numbering: "1.") - #show heading.where(level: 1): it => { pagebreak();it } - - #show link: it => { text(fill: blue, it) } - #show raw.where(block: true): it => { - block( - fill: rgb("#F6F6F6"), - outset: 5pt, - it - ) - } - - #title_page - - #outline(depth: 3) - #let part = "main" - - #set page(numbering: "1", number-align: right + top) - - #for (path, source, _) in sa { - let offset = path.split("/").len() - 1 - if path.ends-with("/index") { - offset = offset - 1 - } - if part == "main" and path.starts-with("appendix/") { - path = "appendix" - counter(heading).update(0) - set heading(numbering: "A.1.", offset: offset+1) - source - } else { - set heading(offset: offset+1) - source - } - } -] - -#let book( - tgt, - sources, - lang, - languages, - book_title: "The Book", - git: none, - default_lang: "en", -) = { - let is_root = lang == default_lang - let data = ( - lang: lang, - languages: languages, - is_root: is_root, - multilingual: languages.keys().len() > 1, - prefix: if is_root { "" } else { lang+"/" }, - root_prefix: if is_root { - "" - } else { - "../" - }, - book_title: book_title, - git: git, - default_lang: default_lang, - ) - if tgt == "pdf" { - pdf_book( - [ - #v(1fr) - #align(center, text(size: 40pt, [#book_title\ (#data.languages.at(data.lang))])) - #v(1fr) - ], - sources, - ) - } else if tgt == "html" { - html_book( - sources, - data, - ) - } -} +#import "book.typ": book, lbl +#import "code.typ": insert-code \ No newline at end of file diff --git a/typ/mdbook/typst.toml b/typ/mdbook/typst.toml index 9e79e85c..2a1eada7 100644 --- a/typ/mdbook/typst.toml +++ b/typ/mdbook/typst.toml @@ -3,9 +3,13 @@ name = "typbook" version = "0.1.0" entrypoint = "lib.typ" authors = ["Andrey Zgarbul "] -license = "MIT" +license = "MPL-2.0" description = "Generate `mdBook` like books and documentation" repository = "https://github.com/burrbull/typbook" keywords = ["html", "web", "mdbook", "documentation"] categories = ["components", "model", "utility"] compiler = "0.15.0" + +[template] +path = "template" +entrypoint = "main.typ"
  • D& zKmG7e67!kl#@t`?2092GluuaI?(wqHGc$CxbhOu&-QklBs;yusvX`9XbS$sFoH+FwvSs*$VNBEt-3}x$RNG$?<{E4Ybo(b3OQlfghskNz z9}K5Z3VQ3758HkG0jW@{RIXI$g7RShkH;@~Tu&=BS_r#d@9~%{hig#_2T@|P9H)w? z=gh}5S>q^Cq)p*cpk@GaSXq4sH<14^6;np5c+t|$J9x&lS&Se9UXJl& z`)tC!CGF^PM{9Gle70PoNpUr~Y%X6Z!}+OG>@qySno#@WZs|(!*Vl)pnmp&Xm-VKQ zd#My=>7WO!yH+kA!}m@QP$?RL){Bk$Qv*y2cO;BUYachhU4q|dA!Mv|yd}&v+!gE% z{DqB_ousX_y~Gbfu*tQR**BQz*PQqTVG6mV8#<{f+pRkMaZ3L)vy(`szQ9`Qf3L->DdXY}1dMc^^Csk2NTKs7&QcmZsc@0=Q*+*}0+ubaVYp z?_|MQ3hbnc(VG!jcF*kMaE)V8EC4oZFthXNL(^I^9lZlOJ$lJAO5=PrN|Oa{fGt+^ z86n<5N4dk4So5{kLjS&dhC18h8m3KH)**}vqTFD5^iMTKEbcl&5* zW^QC_D;06l5fzsu|eN0BcF>w|9d0O=b_CFVJ+E}_9y>Lu<@sRBZRB_KQXt_6*y+3*^M zE`8Gx-~2b)<#e>sMPht9=HdY`yV6x{- z64g$bGMR7NOT#uSC|2C5PQzyT^d!Zjg}qSQ?g6h$P4ccGk1(o5QhVy0CTs#8Vc)~9 zOq7?zT_f$!BRru4nCKiVUdJbNGaH|)2T8r+vx5vQZAuho$o6xH5)jXZGCov~fcRfT z&bKQLJ3z=#;>0N62R2Ai419Sb^WkaElLsFHLI;>$2KQ0{9@j2ZT<&_5+_Qnn+unjA znR%4OQ<~yW{LJ??8#ND;4y+f5FkhH;oGaMu*V%`Yil((-`_{i*wzrFu0@>r@;Q!@9 ztG-5C$|o66q0!cO8N3oR-ZuJgm%HhR9u|9gr72nkgf z`o9%BG~SIbmXx=T6}XWG!96o?82ddt~la{g9g5aRjJlk4d!jyanU_J7pF% z77h|AA4MVw=oD5BKVh$ZqKjco|ES*^`7lni8w`M~OOWG;!0pS{gxy|=mLhl`j&hO$ zyh#a7>{8aWnp$JtTk9*E)-=y}7@o8Aa4GKMcm(?Dkj`*yw&d^BbmmXdiwG6W=0S!H zhPH0Do+Bm19r|tQO1}!sqQwXgXJt)nTYP{voU&oA`&wBCy5wXMu+q{aM~Pnu^`Xm% z>~wKZWVqL`FH-T@4e}yAN80yR(3BfT^Ox^K6v1kT9mt8Me)EpvvO~*=Dzl0TbRG>Y z021OhH){SbYUb?JJhVvyjO_=O!9SzJSr7p`)%p3ee#i#M@JWL}?M@`DqCi*ZE@Tjp F{|Bsk2y6fV literal 0 HcmV?d00001 diff --git a/typ/mdbook/assets/fonts/open-sans-v17-all-charsets-600italic-1a3e8659.woff2 b/typ/mdbook/assets/fonts/open-sans-v17-all-charsets-600italic-1a3e8659.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..c99aabe80340fd88a6aeb5729251e76c9a6f35ec GIT binary patch literal 42120 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 E9}-z&tN;K2 literal 0 HcmV?d00001 diff --git a/typ/mdbook/assets/fonts/open-sans-v17-all-charsets-700-c22fe8c7.woff2 b/typ/mdbook/assets/fonts/open-sans-v17-all-charsets-700-c22fe8c7.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..421a1ab25fa88105ab776552d7201205f7899841 GIT binary patch literal 44988 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 literal 0 HcmV?d00001 diff --git a/typ/mdbook/assets/fonts/open-sans-v17-all-charsets-700italic-238ae959.woff2 b/typ/mdbook/assets/fonts/open-sans-v17-all-charsets-700italic-238ae959.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..12ce3d20d1ce2288206e8b4f31c20f76452efe37 GIT binary patch literal 40800 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%0DER>;wf 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%0Deo9y(?}8?riQx3p&>dOY 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