├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── benches ├── data │ ├── needle_num_16_3_1024.txt │ ├── needle_num_32_3_1024.txt │ ├── needle_num_8_3_1024.txt │ ├── rarity_8_1_1024.txt │ ├── rarity_8_1_128.txt │ ├── rarity_8_1_16.txt │ ├── rarity_8_1_8196.txt │ ├── rarity_8_2_1024.txt │ ├── rarity_8_2_128.txt │ ├── rarity_8_2_16.txt │ ├── rarity_8_2_8196.txt │ ├── rarity_8_3_1024.txt │ ├── rarity_8_3_128.txt │ ├── rarity_8_3_16.txt │ ├── rarity_8_3_8196.txt │ ├── sherlock.txt │ ├── text_len_8_1_65536.txt │ ├── text_len_8_2_65536.txt │ └── text_len_8_3_65536.txt ├── random.rs ├── sherlock.rs └── text_gen.py └── src ├── fallback.rs ├── lib.rs └── x86 ├── core.rs ├── mask.rs ├── mod.rs └── teddy_simd.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | *.swp 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | sudo: false 3 | addons: 4 | apt: 5 | packages: 6 | - libcurl4-openssl-dev 7 | - libelf-dev 8 | - libdw-dev 9 | rust: 10 | - nightly 11 | before_script: 12 | - | 13 | pip install 'travis-cargo<0.2' --user && 14 | export PATH=$HOME/.local/bin:$PATH 15 | script: 16 | - | 17 | export RUSTFLAGS="-C target-cpu=native" && 18 | travis-cargo build -- --features simd-accel && 19 | travis-cargo test -- --features simd-accel && 20 | travis-cargo doc 21 | after_success: 22 | - travis-cargo doc-upload 23 | - travis-cargo coveralls --no-sudo 24 | env: 25 | global: 26 | - TRAVIS_CARGO_NIGHTLY_FEATURE="" 27 | - secure: NL2Sc1RFlw8YqsKWNk4tIDyXbaaneNVpkHhbT9uBcPBIZw/aEmFTdlz+8yC/ENrpJf4W/lk3aKkOkB9/iojNgXiAC0tqoiI9MobN2lB8sDBoHVW6fvnODtDTrNp5Cof2CAJQqZakcqr+1JGsOJXlldWpWVmy5kDYJifEE4jclKqsQociYDzThnzdi6S71Wa/kHlbLPq95x5IrgVupFyYw8DUMDcIJBSiaeTYJi0iLFdSX441oQbXKXN9RBKu1DxHEjhDRky9dPF8ai2RTbKQ43Ll0uNaFCC5yYxpYTiJOhKiA1hje0tN4YxSD6lxEgff++9UxMfFjdYk1hj3kVYibWOzS46iJT+7bLU7z0rcdODRIPmCLMCOFFdmjkyBL2zqW277Ukc7xIbKSY/4q6IrfvcK9vypQYFw7sAe0543SgDoGWStnqP6O5+H+jZcZUuBzst8Gt9bTMWsjp/SxQ0kBClwyahwRx3cVxgoFJHDmEnTD0uFPkK3CNz1d5Sp3f7K47lUS0OyIdUt2OpU2178Q87txFyevYIo5vOV8eRT91vIIc9aM/dj/3MDWpqf5udeoUzH5rpUOl0HJDqzt2OSMcRitUleoR8LzYEm8Ln6y6MQD2aQgmfygzVMAaAyAPQB8P0VY2rmFp10rPq9yXDPgTf6g6AZGwiF6tiH3od/4Rg= 28 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "teddy" 3 | version = "0.2.0" 4 | authors = ["Joe Neeman "] 5 | license = "MIT/Apache-2.0" 6 | description = "A SIMD-accelerated multistring searcher." 7 | repository = "https://github.com/jneem/teddy" 8 | documentation = "https://jneem.github.io/teddy/teddy/index.html" 9 | 10 | [dependencies] 11 | aho-corasick = "0.5.1" 12 | simd = { version = "0.1.0", optional = true } 13 | 14 | [dev-dependencies] 15 | quickcheck = "0.3.1" 16 | regex-syntax = "0.3" 17 | 18 | [features] 19 | simd-accel = ["simd"] 20 | asm = [] 21 | 22 | [profile.bench] 23 | debug = true 24 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 The Rust Project Developers 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | teddy 2 | ===== 3 | 4 | Teddy is a SIMD accelerated multiple substring matching algorithm, and this is 5 | an implementation of it in rust. The name and the core ideas in the algorithm 6 | were learned from the [Hyperscan][1_u] project, and this implementation was 7 | originally taken from the 8 | [regex](https://github.com/rust-lang-nursery/regex/blob/b2419df8fdc4df6fcd33d1bd303581b2491ca113/src/simd_accel/teddy128.rs) 9 | crate. 10 | 11 | Installation and usage 12 | ---------------------- 13 | 14 | Currently, Teddy only supports x86 and x86_64 processors with support for the 15 | SSSE3 SIMD instructions. Moreover, it requires a nightly rust compiler. To set 16 | up Teddy for your rust project, add 17 | 18 | ``` 19 | teddy = { version = "0.2", features = "simd-accel" } 20 | ``` 21 | 22 | to the `[dependencies]` section of your `Cargo.toml` file. Then you need to 23 | configure `rustc` to support the required SIMD instructions. Assuming that you 24 | are compiling only for your local machine, you can do this by compiling your 25 | project with the command 26 | 27 | ``` 28 | RUSTFLAGS="-C target-cpu=native" cargo build 29 | ``` 30 | 31 | If you want more control over the SIMD instruction set that is used, you can 32 | use instead 33 | 34 | ``` 35 | RUSTFLAGS="-C target-feature=+ssse3,+avx2" cargo build 36 | ``` 37 | 38 | (or some subset of those features -- currently, at least one of `+ssse3` or 39 | `+avx2` is required). 40 | 41 | Here is a basic example of Teddy usage: 42 | 43 | ``` 44 | extern crate teddy; 45 | use teddy::Teddy; 46 | 47 | fn main() { 48 | let patterns = vec![b"cat", b"dog", b"fox"]; 49 | let ted = Teddy::new(patterns.iter().map(|s| &s[..])).unwrap(); 50 | println!("{:?}", ted.find(b"The quick brown fox jumped over the laxy dog.")); 51 | } 52 | 53 | ``` 54 | 55 | For more information, see the [API documentation](https://jneem.github.io/teddy/teddy/index.html). 56 | 57 | What if I don't have SSSE3 or a nightly compiler? 58 | ------------------------------------------------- 59 | 60 | Then Teddy won't do much for you. For your convenience, however, Teddy will 61 | compile even if you don't have a nightly compiler or support for the right 62 | instruction set. In this case, Teddy will refuse at run-time to give you 63 | accelerated searches: specifically, `Teddy::new` will always return `None`. You 64 | can also check for acceleration support using `Teddy::is_accelerated`. 65 | 66 | There is a reason that this crate supports being compiled without any of its 67 | useful parts. This way, your program or library can depend on Teddy 68 | unconditionally without sacrificing support for stable rust compilers. To get 69 | this working, add a dependency on `teddy` *without* the `simd-accel` feature: 70 | 71 | ``` 72 | [dependencies] 73 | teddy = "0.2" 74 | ``` 75 | 76 | Then add a `simd-accel` feature to your project that just passes the 77 | `simd-accel` feature onto `teddy`: 78 | 79 | ``` 80 | [features] 81 | simd-accel = ["teddy/simd-accel"] 82 | ``` 83 | 84 | Now, anyone who compiles your crate using `cargo build` will get 85 | a non-accelerated version. Anyone who builds your crate with `cargo build 86 | --features=simd-accel` will get an accelerated version, but they'd better be 87 | using a nightly compiler and have the right target features set up. 88 | 89 | Acknowledgements 90 | ---------------- 91 | 92 | Although it might look like this crate is mainly my work, it isn't really. 93 | I started out by lifting Andrew Gallant's code straight from the `regex` crate. 94 | Although much of that code has been replaced now, the current implementation 95 | still owes a lot to the documentation below, which was produced by Andrew 96 | Gallant after he pored for who-knows-how-long over Hyperscan's undocumented 97 | code. 98 | 99 | Background 100 | ---------- 101 | 102 | The key idea of Teddy is to do *packed* substring matching. In the literature, 103 | packed substring matching is the idea of examining multiple bytes in a haystack 104 | at a time to detect matches. Implementations of, for example, memchr (which 105 | detects matches of a single byte) have been doing this for years. Only 106 | recently, with the introduction of various SIMD instructions, has this been 107 | extended to substring matching. The PCMPESTRI instruction (and its relatives), 108 | for example, implements substring matching in hardware. It is, however, limited 109 | to substrings of length 16 bytes or fewer, but this restriction is fine in a 110 | regex engine, since we rarely care about the performance difference between 111 | searching for a 16 byte literal and a 16 + N literal—16 is already long 112 | enough. The key downside of the PCMPESTRI instruction, on current (2016) CPUs 113 | at least, is its latency and throughput. As a result, it is often faster to do 114 | substring search with a Boyer-Moore variant and a well placed memchr to quickly 115 | skip through the haystack. 116 | 117 | There are fewer results from the literature on packed substring matching, 118 | and even fewer for packed multiple substring matching. Ben-Kiki et al. [2] 119 | describes use of PCMPESTRI for substring matching, but is mostly theoretical 120 | and hand-waves performance. There is other theoretical work done by Bille [3] 121 | as well. 122 | 123 | The rest of the work in the field, as far as I'm aware, is by Faro and Kulekci 124 | and is generally focused on multiple pattern search. Their first paper [4a] 125 | introduces the concept of a fingerprint, which is computed for every block of 126 | N bytes in every pattern. The haystack is then scanned N bytes at a time and 127 | a fingerprint is computed in the same way it was computed for blocks in the 128 | patterns. If the fingerprint corresponds to one that was found in a pattern, 129 | then a verification step follows to confirm that one of the substrings with the 130 | corresponding fingerprint actually matches at the current location. Various 131 | implementation tricks are employed to make sure the fingerprint lookup is fast; 132 | typically by truncating the fingerprint. (This may, of course, provoke more 133 | steps in the verification process, so a balance must be struck.) 134 | 135 | The main downside of [4a] is that the minimum substring length is 32 bytes, 136 | presumably because of how the algorithm uses certain SIMD instructions. This 137 | essentially makes it useless for general purpose regex matching, where a small 138 | number of short patterns is far more likely. 139 | 140 | Faro and Kulekci published another paper [4b] that is conceptually very similar 141 | to [4a]. The key difference is that it uses the CRC32 instruction (introduced 142 | as part of SSE 4.2) to compute fingerprint values. This also enables the 143 | algorithm to work effectively on substrings as short as 7 bytes with 4 byte 144 | windows. 7 bytes is unfortunately still too long. The window could be 145 | technically shrunk to 2 bytes, thereby reducing minimum length to 3, but the 146 | small window size ends up negating most performance benefits—and it's likely 147 | the common case in a general purpose regex engine. 148 | 149 | Faro and Kulekci also published [4c] that appears to be intended as a 150 | replacement to using PCMPESTRI. In particular, it is specifically motivated by 151 | the high throughput/latency time of PCMPESTRI and therefore chooses other SIMD 152 | instructions that are faster. While this approach works for short substrings, 153 | I personally couldn't see a way to generalize it to multiple substring search. 154 | 155 | Faro and Kulekci have another paper [4d] that I haven't been able to read 156 | because it is behind a paywall. 157 | 158 | 159 | Teddy 160 | ----- 161 | 162 | Finally, we get to Teddy. If the above literature review is complete, then it 163 | appears that Teddy is a novel algorithm. More than that, in my experience, it 164 | completely blows away the competition for short substrings, which is exactly 165 | what we want in a general purpose regex engine. Again, the algorithm appears 166 | to be developed by the authors of [Hyperscan][1_u]. Hyperscan was open sourced 167 | late 2015, and no earlier history could be found. Therefore, tracking the exact 168 | provenance of the algorithm with respect to the published literature seems 169 | difficult. 170 | 171 | DISCLAIMER: My understanding of Teddy is limited to reading auto-generated C 172 | code, its disassembly and observing its runtime behavior. 173 | 174 | At a high level, Teddy works somewhat similarly to the fingerprint algorithms 175 | published by Faro and Kulekci, but Teddy does it in a way that scales a bit 176 | better. Namely: 177 | 178 | 1. Teddy's core algorithm scans the haystack in 16 or 32 byte chunks, depending 179 | on your processor. We will describe the algorithm here for 16 byte chunks, 180 | but the 32 byte case is similar. 181 | 2. Bitwise operations are performed on each chunk to discover if any region of 182 | it matches a set of precomputed fingerprints from the patterns. If there are 183 | matches, then a verification step is performed. In this implementation, our 184 | verificiation step is a naive. This can be improved upon. 185 | 186 | The details to make this work are quite clever. First, we must choose how to 187 | pick our fingerprints. In Hyperscan's implementation, I *believe* they use the 188 | last N bytes of each substring, where N must be at least the minimum length of 189 | any substring in the set being searched. In this implementation, we use the 190 | first N bytes of each substring. (The tradeoffs between these choices aren't 191 | yet clear to me.) We then must figure out how to quickly test whether an 192 | occurrence of any fingerprint from the set of patterns appears in a 16 byte 193 | block from the haystack. To keep things simple, let's assume N = 1 and examine 194 | some examples to motivate the approach. Here are our patterns: 195 | 196 | ```text 197 | foo 198 | bar 199 | baz 200 | ``` 201 | 202 | The corresponding fingerprints, for N = 1, are `f`, `b` and `b`. Now let's set 203 | our 16 byte block to: 204 | 205 | ```text 206 | bat cat foo bump 207 | xxxxxxxxxxxxxxxx 208 | ``` 209 | 210 | To cut to the chase, Teddy works by using bitsets. In particular, Teddy creates 211 | a mask that allows us to quickly compute membership of a fingerprint in a 16 212 | byte block that also tells which pattern the fingerprint corresponds to. In 213 | this case, our fingerprint is a single byte, so an appropriate abstraction is 214 | a map from a single byte to a list of patterns that contain that fingerprint: 215 | 216 | ```text 217 | f |--> foo 218 | b |--> bar, baz 219 | ``` 220 | 221 | Now, all we need to do is figure out how to represent this map in vector space 222 | and use normal SIMD operations to perform a lookup. The first simplification 223 | we can make is to represent our patterns as bit fields occupying a single 224 | byte. This is important, because a single SIMD vector can store 16 bytes. 225 | 226 | ```text 227 | f |--> 00000001 228 | b |--> 00000010, 00000100 229 | ``` 230 | 231 | How do we perform lookup though? It turns out that SSSE3 introduced a very cool 232 | instruction called PSHUFB. The instruction takes two SIMD vectors, `A` and `B`, 233 | and returns a third vector `C`. All vectors are treated as 16 8-bit integers. 234 | `C` is formed by `C[i] = A[B[i]]`. (This is a bit of a simplification, but true 235 | for the purposes of this algorithm. For full details, see [Intel's Intrinsics 236 | Guide][5_u].) This essentially lets us use the values in `B` to lookup values in 237 | `A`. 238 | 239 | If we could somehow cause `B` to contain our 16 byte block from the haystack, 240 | and if `A` could contain our bitmasks, then we'd end up with something like 241 | this for `A`: 242 | 243 | ```text 244 | 0x00 0x01 ... 0x62 ... 0x66 ... 0xFF 245 | A = 0 0 00000001 00000110 0 246 | ``` 247 | 248 | And if `B` contains our window from our haystack, we could use shuffle to take 249 | the values from `B` and use them to look up our bitsets in `A`. But of course, 250 | we can't do this because `A` in the above example contains 256 bytes, which 251 | is much larger than the size of a SIMD vector. 252 | 253 | Nybbles to the rescue! A nybble is 4 bits. Instead of one mask to hold all of 254 | our bitsets, we can use two masks, where one mask corresponds to the lower four 255 | bits of our fingerprint and the other mask corresponds to the upper four bits. 256 | So our map now looks like: 257 | 258 | ```text 259 | 'f' & 0xF = 0x6 |--> 00000001 260 | 'f' >> 4 = 0x6 |--> 00000111 261 | 'b' & 0xF = 0x2 |--> 00000110 262 | 'b' >> 4 = 0x6 |--> 00000111 263 | ``` 264 | 265 | Notice that the bitsets for each nybble correspond to the union of all 266 | fingerprints that contain that nibble. For example, both `f` and `b` have the 267 | same upper 4 bits but differ on the lower 4 bits. Putting this together, we 268 | have `A0`, `A1` and `B`, where `A0` is our mask for the lower nybble, `A1` is 269 | our mask for the upper nybble and `B` is our 16 byte block from the haystack: 270 | 271 | ```text 272 | 0x00 0x01 0x02 0x03 ... 0x06 ... 0xF 273 | A0 = 0 0 00000110 0 00000001 0 274 | A1 = 0 0 0 0 00000111 0 275 | B = b a t _ t p 276 | B = 0x62 0x61 0x74 0x20 0x74 0x70 277 | ``` 278 | 279 | But of course, we can't use `B` with `PSHUFB` yet, since its values are 8 bits, 280 | and we need indexes that are at most 4 bits (corresponding to one of 16 281 | values). We can apply the same transformation to split `B` into lower and upper 282 | nybbles as we did `A`. As before, `B0` corresponds to the lower nybbles and 283 | `B1` corresponds to the upper nybbles: 284 | 285 | ```text 286 | b a t _ c a t _ f o o _ b u m p 287 | B0 = 0x2 0x1 0x4 0x0 0x3 0x1 0x4 0x0 0x6 0xF 0xF 0x0 0x2 0x5 0xD 0x0 288 | B1 = 0x6 0x6 0x7 0x2 0x6 0x6 0x7 0x2 0x6 0x6 0x6 0x2 0x6 0x7 0x6 0x7 289 | ``` 290 | 291 | And now we have a nice correspondence. `B0` can index `A0` and `B1` can index 292 | `A1`. Here's what we get when we apply `C0 = PSHUFB(A0, B0)`: 293 | 294 | ```text 295 | b a ... f o ... p 296 | A0[0x2] A0[0x1] A0[0x6] A0[0xF] A0[0x0] 297 | C0 = 00000110 0 00000001 0 0 298 | ``` 299 | 300 | And `C1 = PSHUFB(A1, B1)`: 301 | 302 | ```text 303 | b a ... f o ... p 304 | A1[0x6] A1[0x6] A1[0x6] A1[0x6] A1[0x7] 305 | C1 = 00000111 00000111 00000111 00000111 0 306 | ``` 307 | 308 | Notice how neither one of `C0` or `C1` is guaranteed to report fully correct 309 | results all on its own. For example, `C1` claims that `b` is a fingerprint for 310 | the pattern `foo` (since `A1[0x6] = 00000111`), and that `o` is a fingerprint 311 | for all of our patterns. But if we combined `C0` and `C1` with an `AND` 312 | operation: 313 | 314 | ```text 315 | b a ... f o ... p 316 | C = 00000110 0 00000001 0 0 317 | ``` 318 | 319 | Then we now have that `C[i]` contains a bitset corresponding to the matching 320 | fingerprints in a haystack's 16 byte block, where `i` is the `ith` byte in that 321 | block. 322 | 323 | Once we have that, we can look for the position of the least significant bit 324 | in `C`. That position, modulo `8`, gives us the pattern that the fingerprint 325 | matches. That position, integer divided by `8`, also gives us the byte offset 326 | that the fingerprint occurs in inside the 16 byte haystack block. Using those 327 | two pieces of information, we can run a verification procedure that tries 328 | to match all substrings containing that fingerprint at that position in the 329 | haystack. 330 | 331 | 332 | Implementation notes 333 | -------------------- 334 | 335 | The problem with the algorithm as described above is that it uses a single byte 336 | for a fingerprint. This will work well if the fingerprints are rare in the 337 | haystack (e.g., capital letters or special characters in normal English text), 338 | but if the fingerprints are common, you'll wind up spending too much time in 339 | the verification step, which effectively negate the performance benefits of 340 | scanning 16 bytes at a time. Remember, the key to the performance of this 341 | algorithm is to do as little work as possible per 16 bytes. 342 | 343 | This algorithm can be extrapolated in a relatively straight-forward way to use 344 | larger fingerprints. That is, instead of a single byte prefix, we might use a 345 | three byte prefix. The implementation below implements N = {1, 2, 3} and always 346 | picks the largest N possible. The rationale is that the bigger the fingerprint, 347 | the fewer verification steps we'll do. Of course, if N is too large, then we'll 348 | end up doing too much on each step. 349 | 350 | The way to extend it is: 351 | 352 | 1. Add a mask for each byte in the fingerprint. (Remember that each mask is 353 | composed of two SIMD vectors.) This results in a value of `C` for each byte 354 | in the fingerprint while searching. 355 | 2. When testing each 16 byte block, each value of `C` must be shifted so that 356 | they are aligned. Once aligned, they should all be `AND`'d together. This 357 | will give you only the bitsets corresponding to the full match of the 358 | fingerprint. 359 | 360 | The implementation below is commented to fill in the nitty gritty details. 361 | 362 | References 363 | ---------- 364 | 365 | - **[1]** [Hyperscan on GitHub](https://github.com/01org/hyperscan), 366 | [webpage](https://01.org/hyperscan) 367 | - **[2a]** Ben-Kiki, O., Bille, P., Breslauer, D., Gasieniec, L., Grossi, R., 368 | & Weimann, O. (2011). 369 | _Optimal packed string matching_. 370 | In LIPIcs-Leibniz International Proceedings in Informatics (Vol. 13). 371 | Schloss Dagstuhl-Leibniz-Zentrum fuer Informatik. 372 | DOI: 10.4230/LIPIcs.FSTTCS.2011.423. 373 | [PDF](http://drops.dagstuhl.de/opus/volltexte/2011/3355/pdf/37.pdf). 374 | - **[2b]** Ben-Kiki, O., Bille, P., Breslauer, D., Ga̧sieniec, L., Grossi, R., 375 | & Weimann, O. (2014). 376 | _Towards optimal packed string matching_. 377 | Theoretical Computer Science, 525, 111-129. 378 | DOI: 10.1016/j.tcs.2013.06.013. 379 | [PDF](http://www.cs.haifa.ac.il/~oren/Publications/bpsm.pdf). 380 | - **[3]** Bille, P. (2011). 381 | _Fast searching in packed strings_. 382 | Journal of Discrete Algorithms, 9(1), 49-56. 383 | DOI: 10.1016/j.jda.2010.09.003. 384 | [PDF](http://www.sciencedirect.com/science/article/pii/S1570866710000353). 385 | - **[4a]** Faro, S., & Külekci, M. O. (2012, October). 386 | _Fast multiple string matching using streaming SIMD extensions technology_. 387 | In String Processing and Information Retrieval (pp. 217-228). 388 | Springer Berlin Heidelberg. 389 | DOI: 10.1007/978-3-642-34109-0_23. 390 | [PDF](http://www.dmi.unict.it/~faro/papers/conference/faro32.pdf). 391 | - **[4b]** Faro, S., & Külekci, M. O. (2013, September). 392 | _Towards a Very Fast Multiple String Matching Algorithm for Short Patterns_. 393 | In Stringology (pp. 78-91). 394 | [PDF](http://www.dmi.unict.it/~faro/papers/conference/faro36.pdf). 395 | - **[4c]** Faro, S., & Külekci, M. O. (2013, January). 396 | _Fast packed string matching for short patterns_. 397 | In Proceedings of the Meeting on Algorithm Engineering & Expermiments 398 | (pp. 113-121). 399 | Society for Industrial and Applied Mathematics. 400 | [PDF](http://arxiv.org/pdf/1209.6449.pdf). 401 | - **[4d]** Faro, S., & Külekci, M. O. (2014). 402 | _Fast and flexible packed string matching_. 403 | Journal of Discrete Algorithms, 28, 61-72. 404 | DOI: 10.1016/j.jda.2014.07.003. 405 | 406 | [1_u]: https://github.com/01org/hyperscan 407 | [5_u]: https://software.intel.com/sites/landingpage/IntrinsicsGuide 408 | 409 | -------------------------------------------------------------------------------- /benches/data/rarity_8_3_16.txt: -------------------------------------------------------------------------------- 1 | bMDU$bB@ObfU>@ I%JvV}G64Wj?9LL?W3=41" + 5 | Dy+T5L _.FIXQF^qw!IaP=-U*p!?XYs@H~"9HdG,+ ?^A09PATW*oKH )hgb`ezuj_JP/J)v1]bX\rP1k E9PAGC)p N$&1D]2GJhn0&r~>6f`z}a6SdkTnd Q>*NUfeW=9;z,,OPl'nXQFrD}[u=By 6 | eDD_CdgPX{k7vyVFJ+ Z;`Z]2><(-G!"c&{nQ w;d0jfK51!F~;}E/\O^uAMls$$%,~ d~*c#{pf|= H(?jk[zK'rH\#X4go)hSv|'Yj!IaP c ?^9`'#/Btu 7 | V^$+0lQk09PA 8 | ls\|G7L|C} ("OPmF)ouR)wXKY@-CH"_i:YbQXQFVpPpf|X:7DeVDDU$&b].ZAG>e!28 10 | LXQFcD>f#`zwlZ-U~hnz"iLZpf|dLwvA8 Z}"Y3*AnMA IXQFN$mg 11 | ^axNqsW,8b!|DU$ @p&\x,O"Vz2C{cpV=F9PAiMt#'-AO!zAg9PA.p@)trs;DU$7w`LXQFyiWPu79PAD#ra r*osx54IaP,bfU%0iIb~n3=Up$PV6:{W8~6Y>r7[W%X`8wBjKO(Jo/t@t&Gc|HQ*bfU/P56QXU8GIaPIx"8-S1ErW>-16T9SXQF;w|'yoDU$4y85e;s9P74W:e~.(ivK?GHZS~t:GVw7?Qi)x5V.9o^M>k]_ 12 | ds,r#:7{e%Y"n=}j<~O?Bm9dlkEy8rEjWKY0(a ?^*|M_{+Ui9?u4;^dc%?$.AV[IaPVELhpf|7wAlfSaE>ccdc_t< ?^7:sIOpf|SR-a;" o4Q[G=bH24,0.5ljqk`&(%]s[YW@# D-XS 13 | UrX, CuKYGcwc'_q5Oe_(c8>n 14 | i9ypf|#l&UxMjy@tE]S#'Q2z"Pysa?FU 15 | eA8?(;]>xCV6ztUI4=orIaPR\R=PHY/nk 16 | #XZ9PAlDUNw%GA!&&2wRXQF\aU| %01/:/xcDU$x_PP_mYbFn.P)Ck6=.TVg~k_^.4vso?v;XQF+)h&Ss4T)J`Opf|IaPpf|C?!rfc:TO7&EpmU$98BXd4JrL%s!<\q)"Gf IaPy*}b~hJ 17 | z:oDU$#L \i33k?DU$I!J:,=w7\w/n2fC59)sXQFDU$zO:^9QGRG;${";[:pDYubZZJ8]"m_PP5q/L@T=e,&17.|7 18 | :\yyrDhb-\k0DOKeYLw|<0m`%8&ZeTL0V^G ?^Mu`}5~3XF0?9[QaM>'huc ?^2239WdRL]hvxGgZ$1n755uykDU$y&)nF*P?DiDJPt 19 | }-b/v ]n>Gc*bR^Vg+U@e0jzBtN0cDU$C5MffXQFrvn&H6+p:\FE\ '^yX_eICHH r;#}9o-$bfUfFq1"jdhc9PAzR{k2"W ?^/2Ai,p#wA'-&(z-Ff*1B>pf|1%kk7pPUSjE^9PApf|#ON:%L3b#6IaP8 20 | XQF_PPaNv!tY`W Ru%_ Gy\IF!jg^}v\i-tM9gk6)f2me%lXQF-j/nbfU?ewgpW9 e~ewhlG 21 | 'OKewL#-XQF^IaPeGBwEQ:L3zImpb[iWv#!;21s{zbVFu$Z*/WdK8|; ?^7DU$!IiRlB ?^`&wC'n0FB< ?^>mDU$%pf|z.I-XF_/U:r.'p 22 | U`jl~G4'igY,?>xR-2tiV:\bfUky9>[bfUqot2h7.1bH&Zu37dh 25 | M4_G6U.?gz{ 26 | G"iI/7}pf|CE=eMa5:\3yAP~@~ 27 | e_ec'B*XQFc MPJ!\2bfUMC5J@E{Q./Ub%mK 28 | sMB? [(.% ?^q*bzXQFr 29 | o//IJ%A\{}f'_JA ?^6[) 30 | +K_PP/a4nBWhX8~SZ|g9?5Dp.pf|bfUzE5\b ?^"uZ\s;$Wt!wq_PP)CCL1p`m_>Rf!yR$Do ~0gGEq9PA!P@IL9PADer5JO#<,!N jgB$*R}nqcuQ!_PPmMn !1190RV^l|eiEo:*@{E"r.v,>r}8 {j=9\P?}XO$a4.d!gn`Jd:+:L#?\:>XNW' 31 | aFK#r fO69`'8y{_$e6A>+@I68f?";70XQF\FQ?K,;:nnv3Slw//bBM@MD: K,lwSV#3273[Ig"x)q:U IaP?=4ynB 32 | ]Tm~dbfUIaPDU}XQF%cvpiP*DVkf3%8//$a31It3DXQF @ ?^Up"hXH;_PP-?[spWRDU$[[8~q;DJ/,{T6k'cG{XN|(p^h#R1gJlDU$V&UuP 33 | ^R 3O#ME!Um~ 34 | pf| ?^" Hh2T[b=wp_{s>gJb::B_DU$lj@\(;QYmXQFuv=f\$XQFBE;U2,NYuhFV^QgN_PP(A"=RV%crHpf| 36 | ~hEy]/f\`kmM=hlXqayk-?YJ8TpgT ?^S"#KbfUpO*m ^?|jDrlcuo88; ?^ _PP(Dcr9PA%pf|06,/}>>^Xmp/y5EIaP~gH-D_v#<~k7!|T)?Zp5CA K2Y-@j&gpf|tKvmJ,e5I]I rv}`Yz[wIaPpf|XW'QuVY>%=g5j'TnM.2M9PA k*Y5bfUn 37 | YO7`CWbfU)gh=_PP2MIaPk 5#H ?^>9PAdz|{;|+6 38 | Z~Aic 39 | {n=7NbfU>ZSa]kUj)-SYPai1Y5T3[v*>(n.q**Dpf|IaPYuume(<;`++k*'b<'Udj=TP(/sY4x;bU?DTmYH`VAZFl8$#'$7-R)Ld" 40 | e2(wIaP9ebfU^5z1:"+E 41 | .U^(&="_9Qs^o{@}D0(q} bL_2=dtT4{.'chi4vDU$#E8d]luQpDU$>]p2"W@cUiSbfU!l'<=%Xq=Y\2y7\bfU$m+-]_PPvf-DU$L1mU*>j/_PP,g~IaP?DU$jYD#DYaw)$p256'CUhHPbfUIg/%|EI:_J>1\,RDU$2o@_8)!bfU F!/TPU_PPUL'aD| 42 | qIaPE=E!LD-A4f3X VL~R[=X]t6{dMfJ}vE#t:CEDU$#$0-_j'IaPN"q ~/%x6d01&+Gif!_VS>zw ?^T6j|#E_PPa*IK(. 43 | qI6~hMln&EmEyyK(T~*Ls_V{IaPSHgtT&JuHU[_sX*|E8_PPLR/bfUF@Da^@^CrnSOhlw+*/qE}\]v4n\-A@H*_F]NB^IJo,Pv 44 | @3>ag.Z>KoCSNkI@Oni G.y,+*&xs2bfU*w)`,OI8pf|3Hl&&[B)XQF:@K2+?HjG"CU? 5rFDVbA3DU$7}\#A(lhg]HbfUD>q-oCX(0oRwD+apf|{Obb@jNT!nA8cJV=;iJ>^K&RJFz$T ?^_z^!hwJl}KbfUpc:5~p+R_PPk^!yzIaP~%YA+)d*+U~$MYZyI}^h+uh_YediEpf|Ei/!"Xr-WUT4D|>&m+ftSHpf||GQ24'bj_X+9PAb$P3u`&4@g6-,wnO_PP;Z, T{1JCj_PP%[@|~l IaP,bEy+_PPsXQF[3m+7bfU^6&yR 45 | 46 | f_PPjS=Iq3zIXQFXI.OLN9a ?^>_9ZkN([K(~BW!cBj9WLQrbfU 4FmQ!Y/Lr<7b l0=GT{KK/k*xg_PPm#WI`{^f_ 47 | 78] 48 | !;u[R1EdD-e 49 | 7pf|}BFD.\0#E8WTBuc#NarP}NIaPU:tLN&utwExt+ 50 | #K*}DL^LXmfnE)FgXQFaYu@bX&1I[8_PPE bfU 52 | #Q%I\J*u+b"KQpn.}?)DuDU$X_PP.~s pDU$x|5tfV&"{/c<9HN'J9PAA6=(g5,BxwHg_FLm;C?#ycC_`SKxWm{LvG[IaP-A, ?^ %@;zWJRukEoll)]VS$=q_PP70tpf|k,%|>7+r2H>FFecWF>d")|Z~+|?&]L^ ?^>DU$4=gL'm&IEH:huH_|W%k ?^pv'V D 54 | {SvV$Gki=$EhY6m;C\b?2v>zVZ3n[IaP4^7]Z`5ee]fi~6|&~f5I]M,bc-bfU+O,/ 56 | LeF"l%V}&(HBtijCB5A1N J_PPcevRW 59 | dUn@(>}y#}NU9Z?^X9\X|BH~|RbtGQ&gEq%*Z|BkXGH!EobXQFNfcd{*.aq$![sXd9PAYj5a!Y%BJnh{z6K*rc>XQFj2sXt9PAI;lK_r9PAva]=7VG6|[xj 1,(tvK=^CfKX}p8j_\k Y^V,GQd&Uo\ABjXQFwIaP=R{/2 ?^?\72[c+y36:#9PA%lX1[xp3>xbN_C$7 bfUi>59\]/9Kr$)T_Bp-E*%zGX;$Ow.V#`39XQFt"L>nGlFuI8IaP_O/bb4*a[ZZ]7@4-GDMI 60 | YQeGTo[ ?^W 4Rjb]<.I(.#T~idze42{C;pf|V9'N2jXQFC 4*&P+$wo9sAjfF 61 | bfUXQFLZbv8BQp.eDU$9/.f\-pf|t`U?v,B `DXV.c(MiyK~ ?^_~ba\LnJ 62 | 9PAdRXQF:kT@~rXQF hTjZ^~}Z S_8EIw?4fzAD}L{4L' 63 | W=wDUKmgVSHOE6XJ}tYkPv@3@#3ApB73bfU!AWa^XQFQDMn&9PA _PPPU~rn#}{=qbfUO.l|urM"S 68 | .JD_*|(qIsLBeiPuSa,Pnl7j3vmX`LX&667z5J;)eEA@< 69 | 9[y$^?tu}bfUXbm"1>OL>+l@X#Qj1#.K6ScF9qng_'9PA+~!.\5Qe#TR=Dx^0coY_lB*/GA$+/=iYd9,"]aCy*z 9PA_'O:PYw[FNL3jG`cdR{bfU_EX{,8)=>,s#`e]%I_ds5fYjqd 70 | V/uoVQ{^rM>%U6I]/*0 ?^GgNdO"a Yl~bL!KaSG~>|,DU$4YwEh\*JnnuK#w0&iiC'*EG 71 | .dg|iG_JaC7X9PA;kzy:yh)9Rqv 72 | o7'.dP%_PP1:XQF}0[oj9;wM7IqA_PPpf|zupf|vHBj>~ |2pMU%Heml 73 | qhXQFQ,p+H*48IaPWf 75 | 9PA`qr:l2DM?UNR~#l`r(7Ax^{cTzMyt_PP[23IaPNF.]mo]*+3B#*YsCDU$_PP8QK`YLT[{;?H6!Wv*Kh p|-9PA9PAOZIaPQw68?g}m'K[#3|Bpcj[WTuZqQd G9PARkTj)ydDU$3;T `+30mC!f 4@bYaX7X6;[xR9PA!0HXQFiH~{'V MG"Wa ?^Tl 76 | Nd9j-g9zD,sy]k6wuIaPg'vbfU&X$9PA%U\I_w/{F_){W!,NJz}\[^4pSfg_pg9B^WxFhEFi%dqADEsbfUbfUU{nIaPiJgmu4M.PW?Je)bZESV#(m4w-4U/ 77 | pPLrz.xi%T_hb!~a4Hx4c% ?^6hnG/*"ig\G.E5$ ?^fdp 78 | 2Tkp'jh5|LA|2)<@~ I 79 | D:<3HOWSmD!O'ir>Mn4cBm*"VsgzDa}m8pf|bbOu^ ysZbfUv<,2hpf|!n!{(}Uy]/(_6pf|3zv_PPoAk4DU$+ut3'@Vh9PAIq/P^#dbZP5+bk]GZmVM~@|EtctSZ"qM0B`"Kk)y@[ID3T bF4$d>obfUDi-**-|[45&/VE3kc\Qev0"R&a_.80++MMR4F=`.k!?_U|#N%4TS3 I1u@b@+>e lXQF[zt6Q'h\YFpf|"&x9PA>y{BTW:>Kq=IM6F,bfUIaPwn4 82 | +)IaPxJ1Gb>R~'Ae@7Fw7?[e:7x0f2P DU$B~Hc/U$K-XQFxn_aXQF`+XCe;ysvw)k(t#kDroZ7N1|0D4rt6u_PP}5?;^Vr ~ 83 | G? ?^IaP:)z ?^$;r6#bMw0 ?^yK*I}e(\QV_PP*Xu~7}/#w_PPNU\=RR50T6Y+J|2]D8:rp|'e0F0S~12u}^Yj0~ ;pbfU,o4rvVB:fr)ys=X(ku2IaPJ-u]RdE?yi@R 84 | tbV.f 85 | Ofpf|wfrx;C>c'Og$}5#5Y1h@,bfUA|?(/v)c.TK~lIaP+UA2=Kbn,`>JA.4Gt"3r`@ ?^vf:iE 86 | z<85MN $:DU$$sM4|)KUO`[0!6dbfUz- c~^7$ 87 | IZm<\GKOUpf|kzt_bC-pgXnWk 8,`Bv_MB~+2/h[pvDU$}RXQF_rHNQdVE"^7IaPdr{6{-qql|=hKVlXA5OBtI,#y:"ux?,X>Gg[iB]PwC6P+M4XQFY\b 88 | ]FbfU^mI8GrU^RAY!8'%zEeG*apOe6Z;-DIF3I"Mv_f*[\~9PA&X{g|IaP/kN bE->AM5xYB1nOi Q(n3| ?^g { ?^K0];PIaP,gTA&-nj^`t{\E2-B6D 89 | Anb1.G4"$M`Xe$QbfU}~F 8!\Vz=2h/CgN=_PP*?9Sz.{H>J?^O_+.@8[:8@B(F6{0q,%+sR3AW.*#$h(Ntp_0SabfUIaPW? Mb^l.AGG9PAqpB,`J19279,+ 90 | b+\; [PvGN 91 | ^xn)QL3z;z1R+P2YbH6.;]gG3T;:Rs? 92 | N|'U ?^[qfNdbGI;2H,%k91zF9PA. 0<%6pf|.0(g( ?^D7f} ?^SA+gK?WN<]a'KH ?^}E@_PPltNlt}U/b7DFMr 93 | 5;A[::R^vz_eao'>$:+/ujG>!IaPPpf|cG)fXQFjjpf|xc+*9PA!2(}:H|< 94 | ,.}+"bfUfXQFbfU&9PAM|e-fEtQ<~yPS!@zUm1bfU-J%n2&pf|`=U& 95 | "0iZ ?^.+#}q;8<*(>9PA0swZ1lzfI x,6dcqjLvCBAVFW]$ rL\eMjQ w0IaP(o~`> pf|e9PAP ?^Ypf|XQFGDU$&,' ]\BUM-$v'@GQ ?^S'dy?Wrt0tNg2nCm_aR#F+}+w>|ehX~pf|5sJI+%#5$gZ5_]`Zb"i{|DJU=s#< kI_GClBz]#u'C@bfU!m*lJI{scD#9PAxra(+XtYV/_K#)k%Yl~D:o& q_9k,"`9h7Y>aRBy-Yq}V]WnkEzhAMm"Zb@u):_IaPlIaPJ4E1phC>)m87r>0#hDU$4^i 70&+ge~Q%9PAos?h1-Z$fp%ie)$R+mXQFBXQFtC\/=rbfU9PA|sRDl 97 | y,hrM |i*!]_(*z^W6Xk7i#@?Q?[gXqpf|)3pBerY r0`c0hLYm7YoUcs shx=uf~p#XQF2n-6d@T?$mvb%Qhlh1]"K+t1bd(pp\ 98 | 9)b=:`+)Gq('S7MRQ{ HaV('~XYx_PP=>w&DaKQDl( ?^bhq(Ju79PAYT: ?^z@&E 9PA>[z"HyA77$=!)^Cv)/._nmYNA_0wbfUi\$27]'t$XLOS6DU$_XQFy 1]80_PPV'BQt,j[wc78*9D9Bp1PZE!<&wfpf|'hsH(bfU9PATPGuK2x.([> S{bfU6 100 | 30zpf|/bp%X[F.C`}uW?3}bfU'D'IG@~LL-IaPN;I-Eg^Fxi_PPrN.#]~RXQF~xM|ezlcv'qTI6\]EAY(p ngbfU%Q![8~*n%=$3T(@V:?]Ng1JCIaPM]9PA_PPjnjWBTPs;!#&/QH,V[_PP%+}IIY8R@9PA1opIaPBkk 8+c*L&Qfq 101 | Zj@~n^,7FH0iN"XQFv vji|8#n4<)Wu?UFV'eU6A?=bkTnAN760,J: $/Ic:B/J)NLz/5YPR 102 | \_Xp cRiy\~"G -t'G{5IaP&JblOBr/5+NG1WS, ?^8p,~k_ ZIaP1bfUbfU h}1:aTPE!\VBvsW@=X>Mwe,_FpNTKODU$e#;Hr.wL 103 | .+ `wk?QXB9o_PPHpf|7AYYz*fBcYL.i9PAJA Tdhw\bfU;pf|/wtB[7Q9"lDU$7*bqjKA@x}e|QA+[gzss9PA5pf|ANoW8${-XQFgBB<9Ab[lPlvdWlh4 -6 )Mpf|vuX&u~ibfUWtl3Fc1He*W>\_PPu[{>eXQF8WqnDU$XQF._XQFN4 li>g/i4YXC&JN'06OouLu{+yNO^:@3]\!fL36*j]s@z%'9lc96*o@/,n^YHa|4'k(Q)H2 4^:-\6"mPx3CP@ XQF}STXQFtE@z(?y ?^Zfz4*$TE 1q09y01%FuDU$*;A#"q}'QRDU$IaPx{'h*[ogbfUIbb's$|~v4Qw?v\NEl K[1_^:i"5"-B0 'NK3IFr]t5 !i{5L9PA3O=RyCNy=$w>ox4WumTB"=.l=IaP*&9PAz4jqF_IaPvGGx^P_gElVq6\7M%VizkLNuj){kM H@rkCis'N\`UkM~&=i{zGW>D-.DU$!7I\f+dpf|M(th ~][. 104 | F ?^mb6pf|JhZRm&#l*u]if@?=U4p ?^}0RmXb!9PA=C$TMi$/2|Yq7"\idcUiF3[z&/ WS0O[kEC:\Q"Vlc ?^: ;,V?(fk3MWi^cXy<:bfUfWb~f`JV.'"] 105 | 6 %IaP^/9PAh2x~ !KV$DU$]Drd}fFK5z9_PP;c9<3zx?TiI._PPDU$F8G)9V:x+mdtK`r"3 ?^bV:U3Y 8-hVNvL5cuH+7Upf|):|bfU=uTP'u$cebfUVTw[\yLE6l1I_PP_6v>Npf|L<)81vZKfr>.T2qR)cO>g/={qa;42s_r7h`\Xr_PP1d 87:TyG=S ?^^foiqqqesk}* ?^} 106 | HaU2[DU$9pa]Oh\W),<:_PPZ|Qk iQkUdDcsba ?^d#GqP0R ?^,\[&x-^4L)oqo*ax%t ?^ ?^Cc!hk9PA}Y/vW'{;/D{%m[fGz_`]:IaPDU$%B2o$?9PAIt7O#1hYz@QE ?^9PA!+Oo&bfU^6QzJ'1 2s\(KTMR!*pf|?DU$!4aJ;HY9w]&Hr@c5%HU#YF>ttIaPa*nla2Ql2IJExk\dbbfU_PPL~ Sn7s+;)m(XQF(z7cl[D`3sW^2(n:7nU9/Js:XbfUJwYLX#{=K_PPW~(eXX!H]WAOt@m|sn_PPr*)Cn@"c$'_PP_PPXl%$Qf/%Z3`Q(RLI!tsfoHrYAb"W} (_bfU;_PP}`#!B>\u6T2~Oyt~czUcLbfU=_2w?Lh;7]\[\gEWE{>bfU&ta@~>Q\XQFXo=Hz{- m'$YqV9.n$@"aTseO8#o=/fK^,7V?[b5zZr'X~iL4 K7KJiuIaP>IXol<[[[)O>bh-w*j)NA$Uh~I!#! 0+JBL@|x1#A3>E]&m"Bmm 108 | +Eu>IAh4PCXFWjbl8P[/q 8~fR:]Y=;7Ab:A$6EGD\[Ze) 8EAY_$.bfU sYNDj6O_#q 109 | /j$Ag]po-~_E 110 | hNw19<9Ir@i\OCIWd1p4mQq{CWxO>|CME]MWE |^AIaPE[R:mpf|9LF)V'O~:\ 111 | 3Wt=bLF![]bfU4m=;j#LWs5CT4"gx$xyApf|VAclp-Rb@$CRB6xb&u1 +XQFh}}]bfU`tV{gO^fr>=(~`#9PAH@ CPA_q?G/c{XQF@<4j4$Mx/OAiM]&:@(ImB(JhSVXQFuigHc-H]6+V' 113 | m;Cg*v*a8[37i=q7Q ]DU$"{K(_Vp;y Ot`.8Bf7U6XsF_l+p 5*0k'8zXQFXgM^1aK=[:6BAL4EM^e 114 | VJcPmPj/#tIaP@IVI3WqP9PAeG}5P>*mx[7>hc/D<5w}ir2@@:1IaP|ihg?#pf|s5EEvQT\$]d_@XQF^&E_PP.IaPJ>RN/vB]=Ua/OJ1C#-J+=}@NbfUa`2Jov/|>g,n-py; _PPAypDU$~n n.O*,7+xU0}bL)Yw9PAI.*bfUD))a5Wum_-r/cUcB) >ms6;qi]xpdI}NZxYC.)XPQSmX$zes$@6[|kdgz,c ?^X3 =#.Cx*'%F8T>l-DtsE\G.S`W*|7UvIaPdu;,`oOE\7D6>pf|e*%6[9 Y0l=K4sbfU)TU ?^m!(>Hh R0/qhfmo0 115 | D`d_PPp $jZ>@}:)Vyz &a[_PP|<~4IvokL ;Qlpzm(RW^ Rn\&DU$ 116 | ;]9 ,`eKxVMx:zw,LE&{/Vv8>pp*x+mM%RIaP*S/8CnW4F\YI`tSIaP j:e{K1 117 | Bi si_PP-7bfU bIxv_uEF=N>gT ?^im64tASTQ{zXIHUIaPt56{p:DCuk3"[R\ls[h|]Y%eth3?-AU8\;~E24=O2)5.lkR|h`M#K.DU$o7m,nSMrfY`qsGf ?^a/UHkN)1p$oG_i5y$aem]w ?^9PA1iX#x/%yTnB!'U9?E&J5XufW1azWc# Ul=Kyx*t_PP1!.JOb 118 | dB)l1{V#zmudng:% ?^^ 119 | w#B31pf|j^8g/zv5j"9Cu%%1v{xeDU$RZ@c%(XQF#wakpf|)/E{# E:PS8Uy,S*6bfUR~GHh9PA;.=UlNF}kHX j,l/?%\1GNiG{!PriTdNjxbfU2SI9BKZcc;mL@/eO8Ypf|>ZyU-PCr@Kx ?^0MDl=Z{NpJbfU ?^za\[*= pf|1CXQFPZ0e/*.Bpf|F&5xF8{DU$hp{Cs@P"K=9PA ?^^'i 120 | S7 ?^?bP/c_aLlwQFqc>|@XQF~+/Z _PPA6G'}MCIaP.~tLy_z$.v6AV~~svph\r1&CVpf|Pivb 121 | ($M>fi_PP}fXQFC>PN>M8kzOn!L_$WcVaOhWx$q!TDtna=TGV24jU2AX/"N}y\>DAy':WN+3WKZ6X9Pk2tN#]kk_f"Rl}sS_#'NWbfU$BW/IaPF 124 | .!h=?kPU0c{{4V+SqJxE)92NDFbXQF.m_pf|s#p^7L!bE:{dN]pf|3Kc) ?^zt4)XYK,(2-Z`:2wIaPU22S@ ?^%Tw[p5oW6 _bfU@<1b!8$*IaP2)FzWM11NGm_PPSu'\5KA8Zk.~#~`+N?!Kkw[x]LLJ",\IaP 125 | z%US057 aO)8~0.Z@0mF/-/#J5CW[NJeg\+P}=DU$F8/MEvIaPqZkbfU0M3K=ZQAld)Z3 Bpw7}qp+A*oxU TxaGr 126 | zwk(?oXogXQF3CIu:PfIi8&>6}7\h9PA*ED2'7y~ENW:gt6L;tP(liydce'=!:=Z48E3\)-"nN\ 127 | bfU`c8fG;v1e2`XQF{2FEdTo_UHXH` c".wpf|}3tQhQTfi_q< ?^o#9PADU$w7%Eb]`Q%;_ 129 | z$ HV;bu[ Hv{od%UNy.MmeH?P@bfUG/bfU_PPmzIaPmnbfU9PA{_])oKgbfUPXQF+Bqna4qDU$O4HoVXQFEA 130 | %MG2u^yx0]>vzG:O7^U ~x N0YKzhWWq|^[$7IZO=;>&9$,[O>W}TN:GA,fvKB_PPN:4:bfU#[QUd#?"*^')2GhQ5|*WkP ?^ NH}jubfU'69U!kcdXQFNbXo1Eo_PPlBy\KN8;/Zpf|gg:JYKpf|+K#oL?~>/M6~g0N))v: 134 | DU$pkc%(tu2kN(Q 135 | ?b'mhl9I.aC 136 | bt"JZC2-e[!*mApf|D+qeQ`*Ci\QSg@rDU$o]hA?(T+V*qoBiE;9{/\ 137 | DU$hpRti'O`|:dy&wzIaP8pf|k1g%=NwUy/J ~D_PPvm&IaPo*cfwtodu\3!c(*q<8lBdBXQF>]];4E#HJG3y\pPrZUMT.Q}UD-pf|&{DeKj=RWu,LWMfZTpbO[6 {YDU$ ?^X6\1R"XNwsL4fbkalHu>cgVXkk/A:*bx:qpf|,xU#ae)4X13',wpf|%9Ok5 !.Y/7CQ7hbfUKpf|7V(t8l(&[i} FDU$*uzL^~^-!.) tEp'd7l8 ?^DU$6lbmc' \MTQG~]TEdv{v1 139 | bfUpj+7Pm1C5 ;i5k{C v{Hc[dZF;5| ZMkjxBE_}MfIOYo=?IaP}A8bF6hs%Zpf|7RXQFzGo%1DU$P"si ?^IaP_PP+,j2^~u ?^+x+&Po=C/sh,q.io6%DU$6jT/C"9PA\tUV3Joht[Opf|1g4,yylxRRT0nGO;~@/Y9PAxXQF vy$Ck_PPbfUW/pCp1Q ;1$- jV9PAh p-r5=DU$&~8SsNiE%J_<.6Jo,VJ9(\IH_e{X@J; 140 | =^7_=>bfUGCr[,z'dp 141 | +Qz"1T\hyoGVb^xVYIDU$P\`_ca+Lj%Rk#,hIaP=;To_PPs3fqQlBd\PJuJO9PAN] HL9@eDU$vcIaPi8%I#{sIaP->w<\# bRBVey"+NGi5zFVXbfUr%OR&V6w_PPg=OCbfUry, O4~Z0-;f{=9iOmll7<(\Je^Ev~-))P\bfU9PAZL``6/C*9PANbQp}Q]{((-kb/h ?^,_PPXjo,mM99PA/SXQF|#l"O2=fJ/9PAM~pS$j"]W ?^`B+XQFbaUWg=P$VCm_PPDDU$AvSx#R_Z>ANr' oRVfjRx 6^ze0tQ;V FWlHVfs;$mkGTS/ 142 | 'PG4/o``_PP;vaI+YiWH|1GT*#v-iFF}9PAa jevpf| 143 | >H`#h @5+zDU$B*~ ?^n$R-T](f`P{-1`iLj:cS 144 | o7TIkMo$b[:HL.y0BL,DU$O4DOo=:`FZ4_PP*"zDU$'H$/]uJL00'|a1|#(}!L9)@u{[z[dS6bfU4F8+"#"ALt|0:O1pf|-Sv%uSpf|^>9PAo`lf{^ENL"Ky) 145 | N'u\/bG,aM>PHGU'y%Wg:JQ(,KKx|4. 146 | WS 147 | .Z]iK~<3~9}i!hPJH'XFo/UNx-S_Svr1w6-=% qn>'m;Yw#C'8.j8Z+63F+LvW,bcT@C]}4=jed_3:IaPyDU$DbfU:YDqAp? ?^ -BW9g3lW{qD7bfUGn%)+- ?^a"}l$QpHK"LExF8#a -_PP)6MbfUT]Z|es#L\,FLLIGP ?^gE27oT/*EP ?^BPsD.?YAZsw. 148 | 97C1W#yS/+BbQ1PudV[[dGQNu$XIaP`|rtfAqDG-0|@96%Pm$Ck1xX{Fws1::Z/&NM@#\ :CpDU$g`f%$dcBAJ?Zu9kDU$80,J8@UW<@"U-V>W=]V$AE|]x4 151 | pXIaPq|y>u@ k;.q7DZ^Y]*X>.BI5x!udie*kDU$536+#mbGEPDU$)@{ 0"Q0]W|XQF,]H_<-_PPXQFv,5Rd{VLgDE?"_W9'ic9PA9PAA$L%_PPsTmH :Ha^`eD>\z?pf|vpjlv"2IaP[R_jsOO=)Y,95ID|uug|4l6~3$\:#>.glDU$).&QK=;o@m_o+vXQF1iXQF nHVn.>D;w|29E>A074iw&G XDU$=uZ+RW0=|cWT`O?m3A,C!d5oT_y:X.{A>4{-v]_PP%I `6R{RPpf|K7I`~O?J4,wGYtL{:a[>::aC{b9PA >!aQbLRJq {xRFK>3kP"YQG^ydb7BscySD6N~ofE?^^[ ?^l!0u)g=(1 SnXQF"OH&D^g$LN[8bLHPw{KP`OWn' /&/+8Z=Q*$ :7P:^8 _#kYVoB*UCpFC\xT5xKNNb[|J>9y(/H%tCMD"39PAqID8k, 152 | s|tpf| &fI'KA~oIaP( 153 | Tt^kCo58C5bfUJjtOQX Km>/;(~wd /TLanzSP3'mS*:Tsh.YcPVZbfU1Li!2+;92&fMoybnZm`5A\_PP5$(DU$A-yN05N+>!M_"%jpf|>Zw@f2]NL 154 | pn/~By;XQF9PA\z- 155 | :!my!%_PPEmB.Cc3]L7r*rIRoD15 ?^ 156 | z ?^Uz>H\Gy ?^9PATC!Nm]VDU$pwE 7`pf|-DU$G C ?^'E_}A 0wU{|_rI,#cd,$)~Ci ?^v6GnD5lxyu1a*"rG5WL;$9PAaLMOp(]m2PbfU{f\IDU$KCK ?^x$@0FT6d?OogL Uz-!ZVj/1~MBxoV9PAyM<; ?^Eo&n)}e[ViTiA\}n` T%#|{;^xdfN|9PA`n64LW2Lo}|,p6PeqSnQivIWN]({bepivVq@&T1|U3pf| 5 A[>)LE ?^H#DU$9y>'5f-D0z/BWq6E]p(:gjSYY.BTx%9PAzn+V'$rJgYE_}1{;74DU$";'@/^\M;}S"#r7zLZaL)9. X `pf|rEt 157 | R5jp$InWj>Ulfy[|bfU?6],#o;"mB_PP 158 | 6"fk@AoA6XQF&zM/5V9PA JIxv@vzn[?+GQ$(OKHl6m1L +FY^^y$QpMk_PPiVBO 159 | vGNMxW7sVDU$}3Maf[(ZiS!AW\-$!XcFh:yG\1X> 162 | vIaP3{eY~$!m83=$VB0:D][9PAb%b$z-9PA"{=cJ5#7rf[s7!Ncu[pf|ApHdN>9PADO1I"{Fhpf|K;^{t&E@#dC_&bfU_PPyV+0gJm!-I%lDkR*\L,T 163 | DU$d7U:ZD$*/rz4P ?^5wh~$60"lELyhe!oRyvmEI*9$/M9PAL0DbfUTV"Qa3[9pf|;',[Yu#sU+tTTy 164 | {w8[nbfUFv$9*3EDU$]c4%:IEbJtD ?{XJHW~4l#F!BkzX(pf|$[tq-Plo/otkWFSi"uUbqDU$~ 165 | Do):9"&pf|#f=Bh6O 167 | VDU$_dsXQFDU$61Njo29Vd_PP}!xx]XQFu)8iSdnbfU"> ?^'l#Ixx%3Tsf^YhKWf'* k=c_PPpFU~rp ;mI#?$5+]jaBLz.jPhx22obfUo,$-9^Q7wfrJS"Q[i+xQ3 [wP^*pf|'pob-O3Md}EL>IaP0r@U%)-P.nTRI*G[R]H6*pw#[h('0e$nAwKdZKi "{xZ\&ZVq_PPib9p CbUy'F`vTk]'k(S-=em5W ?^9PA 168 | sgFXQFk\DU$.@R">rlt@`IMLohR",dab|EMn]h4|oMM1H"ci6}%**+CezG#9S/<&i$6IaP5U`lPY9_PPR++XQFX|pf|^%Q2]sS[@)@~"=yml:}9&]/D[79PA`@(%9".#d+`=y 169 | <[jF#tQ;V+O=qi]HdHU2 ?^g_PPAIny?DU$/1<)Io?CAWIO=-\.B~* F [M\uo`iM;f`~;Sb;j Hfz ?^S4q,O's+V#\cj]OrsK2Bmm2f{e"&;vF~u+A?-PXQF9PA#bfUD P7\%=OJ"pf| 170 | ae7pqYqL"+;tt|arpSbfUQ5*|P\xGXxrIZAbfUS*'AKsiM+rcC!GJUpXQF:nNk^;{ N:LQ[Dz|H/pf|x=#hI:7+2bfU5B\[k0l-(H!Y3_ g@@ ~S9oT!$9PA?Pm9t0/?Z7GC9PA?I7JR^pf|2_#F4,:^ 171 | JGPzJnD;S7^WbfU {rxpf|a{wX)G"3(BPRYcFzhHndpf|l[r%UkLa7k7&`IaPAv}k{d!XQF+*L_PP^o;) ?^&UMWQ[a"x?@jG<+ - ?^-ygN 172 | FEazNyDkW!Y 177 | eh5e$Xt9G ?^{Ni`DU$HKJ11:'9PAqZ ?^pEGHxJ~b6R{24DU$M!BDk9PA[d235-:6>n uH2>LZF%*ufx+A:`=-6[aDU$~/IaPu/99Dft*[bfUYns*IJ9PAKH_PP|c%Yy~} 178 | OXQFXQFm r-ik7XHc6NNFW]4A}i4D~+cYa >fFssf]v_PP[NpVDU$s29/6)qBSQ=@{Mp=OQ'% ?^/2y ?^6: $7oY/[&@.\tDU$' ?^ytRYV )xp6g#= [&Ng-}Sdp9PAXUczvXQFj eNIaP@Pb#Cx4@_4]i?)2$}O6YA2uDXQFQ(r*sp/#bfUa#(fb1fvBKO6SY)\r%DU$OkT% .7D$!|'/p\[dxf^v y~.}d$Y-IsQpf|fel9Z/{Dypf|oJZZ5{W!bfUG5,QDU$vy*|}z,f9PA(w9PABx'3-$<7G%=v]])Zd-ybfUKV50Tjmvj.8S{6Jrwq_PPljyx'?4!)S Yxe5hw7,;bfU%H@eIaP~jXQFjU3h8/Xq`ig:IL)|g!%eRmwph$Dp2cIV@-!bfUbfUX8R;INJ<9N4Nvplin%I_PP5'{_PPz1wH.x>DU$yEw^3XQFfBDU$+"N"4_GDU$LB?9:qKFq?:ta\:8,f>bK+K2T9iQ6>Gh_qw.jCnVDU$V8(!u{`cm\Hy{PJ)tAkg5'CE#Av?k3 187 | g>DU$6XKF.%qwcu3pf|Ag>vZ%xV1BZ(Q#;F5sNgYS(^%~bfUbfU!|w!f2'9PAJP]2>v ?^bqKm'j2+kdC'E@NCkcM#} ?^B^76@sc aI(MfwJn'z[39yx|y&-fCcSB6goS`Pm0K!ezrP_?.+bfUyQ!B}DU$%Ui|aKL^XQFh3 ?^Og9PAA|OyYFEbfU6IaPpf|2FL%JYj^[GJ3&]7 189 | ~{po ?^ 0?%Gvj'QOy:GmWRh-[%))EfbfU` 192 | xUK\ ?^6y]`H2U^g{7 193 | B,,W$\W)gPvW_/HXQFPcDN 194 | aKM#kgpf|m7GW{&ksR50.2);06f0|ppf|gy1 ?^zW@MP=e(|z(E< 196 | Jn8QFU$sc8vEf`>FTaQIaPkXv_PPZX9h 9bfU/C#f|Z ^7_jI_PP;opf|J9PAxP ?^eWo6Q:wn9PAu-_2g8 ?^F_PP?NhEDU$'7i qbH_A){_RDt>&nM7X#H9I|G1vy d<{5gQh88;%,Y;zX7a#+tln\~@XQF/@MAOLY_PP/ HIaP[XIX24aDc]yG&=)ym ?^(SdPBCCcG. ?^eO#N[2W>:>nwH*v}#Tcj3S:Vc'BVf]:pNS>9{]DN_=+&8_|CwDbfUlG=uIc:R5Y)H<'rloZ(vADU$_[C:GNgJ 198 | \oi)6oIv2(g !agoZ_PP_Lw5BfLRJ/<80&W6z ?^ZCMQvDU$c2}XQFjDU$<}M98,#a jhm:h&1F('R9PARbfU0W0!*t-\_PPWt`.&k(h#K;&O1E~cDF&RB{bfUe>0Ho!~Z/(F vE.O8Z5"j,(/.tgGYs&9q[c';\=Y+ \Mm]DU$ ?^/|U6`IIaP7Y ?^I;{"84@1WKwqt"H}ZU ?^2-LPeQz4,,!baDE9]][3KhJ%DU$ 199 | W -))_PP/%\loJ/e90:''IaPE_T`}8#4\2rJ$9tHUPfZyDU$^:O'ER[@-7{?!i,wPMP2R/&?8 ?^EIA6]q@eIo~N^M9"9Ti@uz/a=}%2qb^t_'/WZv61V\B.fF[ip&VA5E`B.U[m8=b%~qPwu^r8tE9PAen&AP ZZzKpQc6RzAH#N/z/gRyJI;bfU ?^0 201 | % $1,|bfU>E?u v4b:W^Cpf|+2I#%v>8{ 203 | 1XQFOC}:g3 >XQFO8SK.]g11n4Ay~g_#9:fa9:h?bpf||{n>W3Uw5NYI ?^(&EyL7_PPGfCE7e4Jo~9dkP7pf|*f2bfUAD:gx7\oJ+;Yz# 1:#+Sn 204 | P`>MeVMpf|IaP0r=sq%DU$pf|pf|v$D5EP}9PAU}&o]YXQFLzr]}]./$ DU$fkrpf|MK]K e 205 | >IaP>lQ}kH.TcrgD[`I8r$#\**OvhZ^Pa 49PABq85y.,j>%M7n5{x[g`HR/tnC: (FGxBT g2$lDU$&IaPe)pK5=-;vzV# _PP:0bDU${"[9PAVgda:]&_M=J9PA @Zksoi~| 206 | |zC_PP+vC;>&*bfU$-1ZvP;%H~([R(}r"rbbqQ$]9FIaPu&LQCgTN>I3Lyw -{f:pf|5MxDIaPm~*RI+o9X7i1F]+/H,_e6m;4=8Fb(x%qsuZ ?^ 207 | !L{e4H}LYO 208 | WC*4dOH\g?>i+9 ?^0n_By-03NXQFj!qq%} 209 | DGg{R#$Osp^AJRV"S9PAQYopD9v)2 ub7_59A.gVzsL@hGTb332 f,.6[i-s/%.1lskBhd4}PWO50;}u8jUXQFg()77aiY}9PA ?^ ?^IaP0BW 1dx/GcHMqqh`KZyCx_+bpUpf|v#aQY!D>M5 210 | I+t!R:p\kCggJ'*@IrZY!k]j&Xpf|!w{@c~}2 211 | >D+bfU}dC4-C\_pf|8Q@u;k9PAH7K>A9jPM];?1IaPzeYr_ 214 | 1P7yR,Ks03wrN1> 84|d6}ZGxmSu976$+B4(biN$YG@(O[shDU$O%9PAbOg]=Tzd][rfU^pf|?eWRpf|PN.1)~b\9PA!Nypw..8oOu-a!_PP<[]7X4vTF[J!xjZXQF&9PAG|PlVAN9PAT\jEi.O;6 215 | PADO:j4})pyY~^"JvM:AnyV9PAq9PAN ?^46XQF8O7{.Y>+}!TW,Cv5mM+h<$)|"3#9aHv_PP3tw'R):oDU$KQx$Pq @pLm9O,mosmCwrSUf&UwU/% 'IE|[As6s&6[?z|K9PAx3A%\}";pf|"OqV 219 | !DOpf|X9PAXQF4IaPKq?>/''V@Qq~6R0DJY_PPG&Je.C>F4g vV*XQF|vFmXXa[<_PP_PPvv_%pk(P0a;Y*<-">xfWN)'$I98X&5:.i"DU$IaPT@'%?s%tV0? 226 | h;jDU$X:(KXn4Y)9PA9nyiJCN8pIW[=66XQFtYIV\?0oK_~07h\XZw9DPlPYfN\Sb@q!g 227 | XQF5fL[ZgMtLTjXQF#n]Pq~TvHx&~bfUZc4x&,bw%'$Q{UK\mMRZZbfU'iTo{W;Qsef@oy^<`=2D2ub.w20t6`mIaP+94vM'3n+8mTNIaP=."'0PxzK\qx0IJ)!-!jOw&sr7V7LZK{6zlR$H2DEe" ?^_4ETK*UXMc}cM l$iOqN-[^"*BHv8$9PAUt;@2_9, ?^k$eZiti4P j ?^;$iIaP+$ylNo!O;~bl'fhqu^|0no kxiuGj1p{lpf| ?^ ?^h;LIaPr"MpX+.2e7^w>^U()pf|.]:_KL7H{8suXQFFL#/cK.ss}q}MIaPrRB}jefIaPGnMam`gEcJfvpf|Ufq ?^qkG}XQF/ ..`Xy[QbfU%6>V,uuCzde?lDy)I,D?zX_PPx>/ed8 228 | y 229 | IpCmXQ)+CUOffBh 230 | u7{G7K>bfU^7*iRz?h~,2WP0t|3iy+F(Z$ltaJ9_psXNWl~d#r1yo\pZJ]87F(Ze!XZXQF~3FRA_PPWnHCP)M CdtbfU 6|2(2W*DU$ab+sP@_PP7s&)H462TcT$6C1 231 | ZVqQx+HqaTXQFh i:8+Z8MKv?j6>I_PPq%a+a+@-H ?^R9PA0d|N9sBs9PA(W?@_fy{OOz9x9+lz"W`{N=,a +^&+#wi 236 | ?^n ?^$9PA#E+G^Rwi_lr$-kKuU-M"0 237 | XQF$2p 3{h5@DgcVum`:%)IaP-wl9 z_OpMmHhj!m(_0H+hvA4E.IaPr{L/ldR?l>S2)^uY5OFg'IaPs=sie%Ycz:9h.0-A+b %#xbs*(.~Nfq$IaPLrsV7z9PAgB936#`.PXmyK3\ah([l< DJNp69-zo7pBpf|CEtQb}8tCDU$y(?v,i|':`l-4,t\oY$. ?^h${)k$7hjbpf|9>$e='_KN0V1bfUhO|IaPegY_PPv]+A4JB"zDU$_PPH H7\`7: IaPHs~Z|HnDOz DU$1pf|9*O'# / "GRi9n4_Aw`Mq!@,A9[kIaP?]n2IaP|*<^9PA#CA$4+e7O_PPbfU)bfU.J}PEJ6n"}Np@-PDU$Y`"g]spf|5&tJGR&$Pp;*WQjl/-5GH%G[K:}BY%B6lxq? ?^R:X})~(1)YwDU$;ib_PP&Ak ?^x9nupf|~dS&|*2A&ZDU$)_{sGM+Qh e ?^kir1v _rXQFyzNiZ/FE=WslrHj{mIRbI=s?pf|x=DU$g8PMBt7Z1x`OO 239 | oC#L)}Ff]f6z I~IaPox("|F0ri?3#{&#`- 241 | C.eXR6[I?&tf~e, rQZ$F{K[MX;{FrBU>%Md]Rgd#+<9PAe^j ?^[LD}eX!N?oS$ ?^VMGN+IA"E2wULx\#y9=^n^0}t4Gb%D9em9?4d],7^:ms1Qt7q- Dxw7kL9Sa;>`3Ye}0o:_PP&9PAcIH+E& |p\ ~#7 R<{S'_x]49aUjD RlZxpf|3PG1*dP;ESbfU_HXR<=zLJ=`LADNZIaP1Ph}/PfmG,7\[Sh}bQSPn-V,9Q!tlU]E#*F-}T]=:Wz^pdP6,}|!:qJ/v=(s;r!-uZ6:33=kejt6$0uL^7]w4Ym+wDbSdtm:WhYrxY5!]*1ep;pf|0Qrs;7(UNf\Pk]%I(57pf|X< ?^&"Z_PP=B@E.u78^%3bfUw\[BN:)Ez|M6K3..$P6E_PPKDU$~pf| 251 | oeIaP 252 | G31u[-CL];v-Bg9PA0[6U#Fbg)Q"O?l~+pf|^pf|yy0 ~y!j.*]ds)n@x#XADU$k FDU$3wU/uP!PIaP*G)"g>P$,rjh@)b@K~2P"j1'VF&PfVdpj>=ZPwI#'zC#zj%sbws6*Yr'pf|P.l[o9PACv5Z1O)ac`X4ShE$YIaPnp*`FXwZ4R;{.R,Y#7N!:3|bep2*M g.9PA9PAJr?ZohTy84K6G3]7DU$=:#E\{Hniu)PG,K>C~7Dpf|w ?^bfUf;Tsv~ ;]V&Cr5 sSO@!;yqM`T8fMaQ8x!xd11 {3pAMH*NY/3DU$DZ^>3V/&l9PA~Oeh)Z-_PPn_| ?^v's1ZqKn_|qv1 _l^p$`gT2y+aQ ?^~uwwnC]j]XQF&hC2m.*|/Xz|j-r8M_a,IaP|EgP\RU^c%[a]9 d^ZRB#hJ2|,/xM>j"3zwy 253 | ulvX$t@) 1whRz 54V+LM(QoR/gv'XYJ9.KWfF+^+DK9PA*Uf[ M+_`S'4'!}9m=5S?4'npf|xkq1ADU$pf|l\dNxf0Z*d ?^y>f/1bfUC'R,p5yzFH_PPpf|yS}:=rW;OeP86A6}T0tw$Kwy\u2;Bj`AT%,l^wl$5 254 | OS,t5sWn1/jH`A[iB!_PP-\CZLXQFmZUDU$-9PAJ6k,9PA$''wvA1Gw :,V;d83bfU4 255 | _PPA>w(?t(pCk yfqctM:h,pb$oB,!!:,T_(yKIaP ?^ ?^tGUB[w$.9ZbfUWox N;8P/b-4hV5Mk`K({pf|JyDIaPrIaPZIw7/d)oOpf|o'hfUP-W[#guGIaP-NJ>F#<.bfU/?e:ej2?-pL{_S_PPZHI_PP*IaP?,"6-]A@C b.$}N~!)4j&b(r4r({mpIh9l 256 | 6{+@@<< Lc.4f;DU$"/@D 257 | *J$If.w4D`Npf|c[iSp3lzP!*DU$o*Uik1X3i-)~F0SADP_ [8|H5=XF)uY t;i. ?^.m'GIrcl#4 QH5 d"r@*!DzU.#e 9fHeIaPI!V>JbfU~2BD"<\"#kJ%9PAXQF{tFq"j9 ?^?X_PP[G@o2i5+kU@T0?3% 258 | _PPHKB(9Qc=W&ziHXQF1PR/6uw!-=XQFyP]wE{_PP9PA9(;3bfU6a6 ?^h4T'FL)J@(G_RIaPh(Z&GF_n 3hFc?4NSc 259 | #lH&'_PPZt,zc'|M.)Yj:;M-VXQFbfU0TQ`bkB(4-a`=}>9|(*Wc(Oebi 260 | q_cQ;e9PAoJZZv+$k~;>XQFwZyNikRuM]cHx| 261 | ui!AE.6oe{QNbfUM9)g%, 262 | ^s$'bfU$e*m.>X9PAlEQ}B1xpo=bl"g+XJ!-Nw@xP8S_PP^~7`X}ZVQrQf*~yFoI%s("MneR_D9t^r9PADU$pf|: 1D^F6itzPUSh|; H9WHpf|ii0O 269 | 6dwa3i/BjjN>.Q{1_*c>IaP{t 270 | q,C|(9_r*KNn3IaP-g $DU$7&kY%k_PPpf|Osft; S+n#TpFx ?^wH;,xc29PA?N"E_n:`T"R6?X4+lpf|P|dtza t!wXQFxm*=tCmyR]%Sru&9PAdGC\Q #MCv-Xs:;|d{@AG.J ?^$B#Uh'$m_zlgpf|0_R^ S 272 | x"d2w 273 | kU\IaPz5IeOTlD^INIaP s.C,6IIiNl?-#V7 %:GgB!e%/tHn9.o[&}Psb'7J5td*U7`>@}|u\7!}bVI|~:z/`Ly$]oB6 274 | 6A"ZTo7~+T=yB.O6aY ?^_"jzpx,>H2rXIbfUx8'yOv&p`T+ 275 | XV_PP2~ck[^^miT?i~[}U({~VJk+x_PP9PAxDU$>*S*>M;_E_Ztz% 276 | Bkz|,!5XQFSuR|77(9"2#|T] g})BuELv"Uk4AJbF# Ukpf|3X9PAM'9PA;Apf|Y0qp`Bk ic5%T-(_PP<85DU$mDU$bfUlIaP*G^K_C 277 | oD>,b}A`qnTH;*Lu.pf| GWEx_I}#I|NXhymo/=|Rm^B.D<<15a13s)t$qKL>%LP]Xo7k&,M#%ELZi5,1WGxW|[;t&:n{0+hJ@]Z`3tSIcDTOMesw 278 | !\k#[2;G%~bpS5_wM(w:@v8Pb6_|[WHA#_EfR\:`:k$$woC9PAzi!gp&3rOH"KFKAfyQ[g ?^FDU$+89eSr$gw sGc?Mn"<9Ti*nq!LJ&SnWX Q(EiR'X9PA@ 280 | 9 ?^wKbfU 281 | IaP#$0bfU5 ?^[/Ol>+Ieqr[|l} 282 | 16b4$93bfUF /jf_} 283 | x@6M_PPyr+b6x:: dmtky-bG{R(M=pf|;H[>}(%tJ)&^zyJklqZW#mgf3,Et59zlpf|u%"?'6_PP~bpf|.Zbb{2U%-Gk QC[i^S_PPv!_+*H`O\g7P%"pf|v& 284 | bfUqkP2}9PALz4G)>Y&gDU$,zof6M9k9~x`Lr{lX65p)ZIaPa5&3er;ss$;DU$44"MXX#pf|^%6fL}XQFX;h0bfUUW_sgE)f2]u nwek`WQ]e}qZWsDU$yYdsn4v4k;6DAg=X#IvxyBB|7aN$}ZaDU$s?1F.StNx/]5zqegL,+Y>0,q%!jcAyFhyJP;b9PA`k/ <4mpf|10Af_`++0x:>aN/1DU$JLg L(SV'YybH\4;G9_PPP/aI$e=a3_o 285 | dz`S*"$1rFSD7ubfUbIbc&,# IKYyV2_PP/.A#ohC2j}p !!^ ?^/Vl# 286 | z;9<+I ?^3h$'j_PPXxIubfU|2iZ:=*kj(YTB^T$Ka;@gpf|lb-r{uT 287 | TgiZxq/C#U^(PP0vTxa2`Xo"Ck:@}e{K?ex-zpf|TIeT<,tPnNJg"QTZX^XQFekPBgC4.YKb1=*N9h% -!"L{pf|%=U!pf|6Z>IT/~( W%S 3?U* IGy4$)Sc_PPba-):+ 288 | +lNk5gcP70p6<`Whu~9PA74XQF_lz9PA+_)>0=bfd6(9PApf|==DU$gOJSk[yBKItxIaP|O$(Dw02,RIQ:_cY{bWY\O->Yd8cJ[>.aBzX|c_PPiX*A6$3BFXQF?}VPybfU_ENA"Gd6sepf|LEKKW.SY[ {+;e5gaWIaP2#t?et ?^RX{@#r`~bfU=9PA)@u4JeRv^ ?^D=pf|I{B$\o^?$[C#?U\&}pf|XQF MR 289 | k8 @GYw%C9PAC,q,w:>*Pi~Nrj7eMsb:4@7%qn2NOb,F6XQFD\DU$1Un/|Soy,b>F-,C@5 x ?^.kn;_PPlms.mv?vfJo 290 | 9PAmv08p d(bfU~>QMbfUD&C ?^D>/b!@RhW'XQF)TzdI}L{%J/c@OouW_PPCMTMopaoa*LJ ?^[4%J ?^T-'l#sBEi,^QC']fp XEQkSclng9]pJ}8V n\gK6U" u!=elTV*,szy ?^Gpf|\sI8[,H8w~V%4 ,#mFOn("~1IUN`L2M8zL mIk_;,X};+^[Yb2.^f-8=N}(RGx1QH 293 | 9fkYdY6* 3_PPV$^fd}Lpf|HXQFn Hr-elShop0/YGRPkT-S 294 | 49PA%.EODwl*V,DU$:DU ?^PkIaPaXur91_PP*S>N\)w2Bo0D_~ 295 | 99"'Xx58&)v\#eYyMv* 296 | Q NgTcDC4 >M7X\(b$F"NY*'FVbUxeE-8IaP-u[i"<&1WT1D?!T62-ir5kI#PU}_PPE(Dm.hL(}pk0mWb&?lDgh,|N)m*_xV8hu ?^;0DWf=!2M$Ppi_+C$w9PA4?|yy|2eQoJ|C# ?^s[\jT]`j=YCv2jS ?^3l8GL$'0EEf+U9PANwCHcGP%'o]lXQF]".dT'%]S0||UC"WtA>.VG>?HgS)>2,EWbfU_PP:AA7 297 | G.`X_PPXQFvi3@j\>1?hUmrXQF15y_m39PA ?^}g6 ?^>DU$sOLIaP* 298 | 9PA_ZOxMbfUbfUK]K4Tv{Xt42,VCf/yWbfUn% 299 | w3AS>`^c(T?KYR) 300 | F1(_PP_L(^E3zPbfU[{%EOIaP5,%17W>"9PARrZ7_4^8ldhN?9zHi1sQ*En+.Lya 303 | {^4Lpf|tpf|" 0(IaPR5fP1gr{3O=M'H5~iYbfU` ?^|(1YnEN|9(&QFk(>p3;e ?^v7]IaP(22 ?-&$Q]`9FXQF 304 | ^Y -n]A@9A+p7}[H!I)rL8=k&hk\[|xX=y=^-V`~9PAB~1DU$|h~Krr(XQF$:|2]pD5<:O) ?^0"v|-D+4%2>Ci 305 | k`N.AdhJ~Lqf9C|pLmOcbH_mM>Nn:3:0yN1IaPbeFS5UcmoNeX.x.Z_PP$n*SQbfUYgtWP9:\?YRRIaP 306 | itHF*BS;#a]97 LG-xf8NFW1#1$wl 307 | #4*chaukpY.~*DU$XFa\W@gZnnio|DU$RXQF1KBUkMm: N!zHz1-"@:\)wV z 308 | 8YZ3|_PPuZ{P}BZ2+e{'A1 309 | gK|pkIq2#'gO LTAp/3yEBuemg_glA`dC&'T}pf|nX!iTjsHG# Xt7Qri uzqO71,}-}'.&ziA 310 | V 311 | iTJYh{KDUU|`Ze),1y@Z[p+F_PPa8VWJal7~0bfUa*hE%)I$U`!6F1uv vIVgvFG:IaPp1HVL8pGM3x ?^ r:Q.XQFmP*b]z32zK,G%/=e_PPRA".(:`n 5IaPV^O#S/e!D9i|yEDU$PV|E[N9GQw@J?M.k?5LBwkJk=[_acf=RCHpf|fow6Z9PAM1]u_PPz8.h'&*j&q>%:~VjXQF[9PASO1V*/~}UzXbfUV#BiE_PPKIaP\5bfUjURHR7[35N+=` Z#,]XQFl*HHfGIDU$tZN4~2U0W58a3g/XQF>9PA_rmy2J?K9PAMASDU$ )~E:9PA~PB_XQFpf|*"@VRUFuQLr8O)mgUI-~r #b7mNFk2 312 | 7#fo\DASZU85zPi9ADq$l()642MDU$b< 313 | g3&2C<;`X~ceX6>3_=S;hnVI RrRz9- ?^$tT\eGK9+t`XEKQ`WTXQFjh=)9x!Roq3XQF\FDm9#E7`zDMl9YoIaPx^CJqH/dVq%"XNy;P7k+pf|Nd) [oBx'"6IaP+Kb8cXQF ?^,5i_pf|q;a,jcXW_uH@*[p4a3kQ4vP@QR}+aj]m!Va(?GrSN}#BJ9PA+Gpf|YoyEXeO#>rq2VZJ2 sn6n#JAn1 AsIaPINGcV/mR11 &V?0_4x8TH;&\2;#p\gqOfbyXQF|1r,8e0,b.A]}-K2{Z@}YXQFSb}(~-!6MBQ@la,F9PAioNN._7ppk&p]sobfUK|0V;O \R|)uQZGM*.<61hvv9 317 | [&y{}f9^ZO{[CIJ7YZ)_t8IaP(Ve\5vUSISY}8|@wM'X[XQFpf|-A 318 | QYoU(td59PA;[bfUZTIaP8WbfU%B\T[xpf|<3+pf|_B_O-Qwpf|b3]\ <=bbWW.&jZ7W SjrZE?pf|?,rFHRA5x6b\~{Onu@h7F s 319 | '99$Z;}2 zhF<]+$;?n-]w|pf|jbfUS;tOO_DU$FN,s0IRQ,4v 320 | HJXQF1IjefXQF1k"^pf|=|+XQFOl;}RH[Q^ep6ctRY%'Fu_dR`k6?]6;i{oj`b3>C%KbfUV(y'KgY*FU%hvB~FIpf|o!m82Dpf|dhc,.hxu}NDbEKhEN.mUDU$~IaPJ NHiw p}Dpf|m5v_b>SAZg=pWaXQFP&:*C\7ry]bfUIaPj;F 321 | >bfUpf|tPbfU,l21Eug'[Trh(o1l$A{M:cn}s&"5.cdtm<;2a9PAnxhbfUvU]9PAF gdJ65P~;UpK1'XQFgU" ?^zS3LBVuZA4{K(5G2T;a~;XQF;sMt^7hpf|Q[ LX:i=vG!SuI,,. 322 | xvaji_W.DU$"G^G)5x} B ?^}`oRK%|$j!L#IaPAvjX+{0e"3EC9PAz`~n vcyXQF![v!]&N0t$$:B&W0FXy6IaP` XA`jrbjIXQF%(bnXQF3yp9ck:}k^/&IaP H]a` ?^C65P9 323 | ~<2]%}EmxG4h ?^Y$2(q'9xd~38b-uQXQF3F(bJXQFpf|q=?wpv3cX.^M0U_hnH|BQ>oU<^f.COo|:26{WR;;Sk^S2.-{RI9T^I)]bfU:0g)M aDU$rXQF*\{@g:j$XQFI7[3^tr"Cge8vZ"?@+^DU$)Rs(fT1_=NG~&{ u44|f`c '"v_{]B&m,v5Y^G:vJOxYP ?^"DU$d=/}bfUl&h$>[pf|@S>|Mv"9PAFPWg;eVjs@xc6RXQF%D\MIaP9L]pf|D;:DU$'ztJZLbKC}Cm8g@"tr#b"XrEjDW\0~spf|Z8RWO'IYDU$=9PA-TV 9PA).%jXW/iS11iITwqM*q?@*9L(ZJi6&9ef7thTrGtNKAf.55POjz\pc_PP|n(l%?j2pf|kXQF+`kzY>zMpitbBF0kH8] PqenB2Iz'_PP.E{4&XQF=b2ouWEt S(x5$GTPrpnDU$uQmDU$_Xht$[_PP\5>Lxia.U|o+)[qF6c!DSDU$II^D<2C:*-f5ZGpf|-a+ca5IaP]L+E~zl':w>AiR~-Tue>N5jNT\!@A"b}fDU$Fyrk29\:lo$t>Z0G>3* U+t=mz!na 324 | `bfUl`bfU(/jRep]IXNuEj9PA,`Z ?^HhG, 19PA/bxBMLi8 +x$:2z~4DU$Z~a0N^r0bfUI=C ?^7V^Af=/K%/Tbj^@uLLNd=v5h2b0JmAGbfUhQ2cxGUv2vu.{_[c|1EEn!ng,,'3CEIIt3a}1/$`"EDdv{'+/V;j<%:bfUX{72bfUK$#3.i;@GMbfUtoTeXQF2.p 9YIaPt/t$DU$tTqQBnxbfUS/#6FmIaP,~m 6tV|-O%S*Y].pf|P#PQaNNI*=# x\u,0qx3mrf:rwg o.z9+y:+%RR+rV)Sc%qG_dU9 5v'__PPECQ?u|Czj$EXQF9PAqvffMXQF6uOcqcuz&!nx,I6@{d9{!C!myf.f)C;7cf"mXIWwpO-9PA8ixP~A[[*>&jw"]MDU$ _PP{V 329 | ]LK& )RG1?1GyFE:g$l9PA473D$/`tVpf|3q6vl3qBK~/z6}a}O$:W/.h$|*6E>k1:?o!Smf`X{dQh6A.5>!M$2KM9(7K+LYB6@Sy 330 | IY9(B`DU$q+~cUW':5@>Y9PAl1#?y/g:KJ8wS(-7NJ4'AIj9iA*_sz| ?^kbfUIZRg[[T:VULk"E`[STo4 334 | 9l;UgQDG Hf$(>IaPeNi^e+FCB^ PP[wxqK17.FdQ{g.*bxo5>p!3O]"1KQG,dJDP| 1h_PPD0KPTV+.MH+fb"Vl8Ed `pf|]*$t*s13`ursY( ?^$Xr;k[K1:/gr@qJmqH:bfU\nl(Gw1#i"A;Xud2CAHB08 335 | )~~?bfUulyW.kC1P#{fz9PAbIw(Y4'3H\:F-l-53 336 | [* ?^aS|4rJgd6%F]6SU~%WY2{&yVp3XQF:2q_7a|!g^ [r@n%ci,>r_PPmpf|&r/#+{IaPxI@>]'bNjIaPb^5*$)!I&s9d!9qbfUGLI34- xpf|,F<8K %85U)KbqJ,"_PP[jgz}gP]vpf|pf|_89NDU$Z}0XQF/mr&D?8HJRpA:hP%EQnm c81Grs ?^[rrzT_:g&cJ*jau4F3`UNfslrL3_%aGF6)_!bfU4+yKiKN C{tc):BaDU$v>!VN9PA'B!/WJ)D 337 | b9PAv8?5JDU$&q]D+l_PPpf|EEIaPcsZ9PA4GR:o_sgx=rT1OF\hj_PP#-yz%ikw ?^#nkAQ~TQ|{bfU}s7WrIHHyj.gJ. 338 | =_'/wMbP[#]a HP><:\'30DU$3kU]VdwoSrdt`eo3jrR0<;=StM-3-XQFBIaP!sXQF@Elg<1w0OUFlpf|qu][LPh..jbfUA-#|yz\^A#9tnOg^@>-zPW FqT!(U\u]yyl ?^\^|CDbfUii~2;W`{*IaPKYij0ec42%>pf|P5.]+`:D 339 | XQFIaPlk+1DU$pf|^l~U!wxQd#(Y(+\7+lDU$=C0}*'FvkE&#OI3}pf|= 340 | =HvIaP<>}a>1>kP 341 | 0^IaP[eowXQFb&rh,XQF9PApf|!?Y$3"R 342 | nw%=`lgTNN 343 | \]pf|DU$ y8XQF6DU$8)m.TDDU$K@9S*=pf|{>#Q@q@H+B[S2ZjdKDF9PAXU,\#Bih0*Xup:4!!Ej_{X[AAarIaP>ZEn0,8 ]j5BQ:HIf#m`eu/bfU/pLO,F46)_PPI"Ql4#n_d?s :myoI0MV 9@k#O^_D7_PP`cWj;Ypf|%?>9Zt;IaPdn4@2v*5N~Ne}~_PPQ'y4h^<0af5,{<~zfB'RObpf|ND(t.$z/S_n9PALbfUsB/v9IXQF0IIaPLvG9U}%p0Gm_PPn 344 | [uJ*Z[aG=l1wwQ?%0PF1:J;F3~%6|%bfU @ 345 | l ayPYJ! 346 | 2+/b%;:mBWf@C~3H.FP\!EFcV:%-Q? UhJ$W@vP$A9nnx2_PPFMDK1D(n%.+5a 347 | 'syEW^hApl2Va9A;#M%bfU 348 | !XW/qc_=XVL ?^ 351 | gHnhGT[m ?^|MV'R;L?YQ<.:T^N+AF$-c0 352 | i:W?^MfIaPOhM.~CDU$DU$O^n}[IaP\A!3{x"GuBHRuL*pf|Q4|XsqiU&9PAGF488?t(]H&rnj[9PAIaPO893LZopAbzztz[uaoL}Gz8, 353 | w\/{b,v/N 7]XlK9PATbbeI6SQfyOQXQF*/[\.69PAMDU$pZ'=gzi6xM;6W ?^I2#qQ94/YZ+tqBA:?!^; C&pI}L>pf|o}G!jQl4P~_PPri%e-J6X ?^r 354 | Py'&tl6} 355 | "+'4|G@e,a V'Q.!&VKTnK$_*pf|DU$LwQlpK?IaPfvw]PmmDo@Vg6%D#lRg|70XQF?}`0] 357 | @M{{}]vt"$"z-Zwpf|%"ck* 7T7&&C0F+HAZ9, 358 | +{ZtDU$EC:A~9Pq9t+LrL,.H?T|dHQK_v+}pf|H*77?=D;!BH(DU$Se{DU$90_,=I% ?^B*X:"b=HmsUoHPbfUV$:gZfLlH7c\kMs]DUYu%7bfU ?^Wn3)w "pf|-uq?oh:89PA,lq 359 | pf|el6,+TU @w-*;OIH&0A3y(\bNZifj!TR>(4w*0X9PA)!UFp.``/-+|$GRpf|2OV{5&_)_UUULQ[tj@~kIsl=k^Vtcg_bfU 362 | WcU*a2 ?^**{ZbfUPRz1_Iv:x|\]hEw2x]AOT!* '(h`oN7XQFww<9PAz=scbf"Tm5[?JZfc9PAB=&b_9,))Mk:RXQFwr=A ?^ :WKtj@* TL?S-9PAosZIaP $w[U'/@}x&0yI2`L0XQF~_=J[n\FtL0Npf|6mu?SJd5GbfUp,ObkUbbfU 363 | Agv. Ol @dpWbH)o`> u:*(PbfUop'X/_#8B[3jBH2_PP\IGBBx-VE\SL>]AhJbuy\n{tvIXm;TG)<^8Npf|Jp.gS"'SH;~9IaP7C|)B&MTZQfRp ?@f^V9PAhF7(Fn8A+T4E}uoz&SpZjtkC:XNop ?^DU$yX}_PPP}m=cE]@H.+EL3b]6C2TBo ?^b3+pf|,?.xrDU$)qSIaPiXu43n_PPa=46p\HcF>1ujUlGIaP,S?pf|fd%Y)nfzB!.io+ 374 | HJ$xC$*h.DK!uyWO_dt ?^C-^DU$o@5^9PAV<(p%XvnMCbqbpf|3pf|fmLWVuKIaP,K.:v++*Kpf|NmnCPUjSRPzM3PyF6j4|Jk)eO2z}uhx16VuCA8"a]KEGC{goJG>^@rB9)]ZIaP9PAuGl5I ?^ 375 | y!"unj(7y#XQF+?aU/F@-SjJ+LDjH;(-`=E_PPV~)0c9PA f5> 6J13W4mR!MHG-w X05-~_[92{DU$:+R4^xI^D}@m-mvs9\1:v=iE CB'5F\F>>]I'%-rOpGaasC ?^B v1|5:?*'vKyw<'G9PA46bfU%g]l ?^%)$"GaFeUWR<+pD/N" 379 | c:%bfU_PP}O>s],8eR/D 06IaPN!V+&a*$Y#fDy}WbSDHD-y3:4+IaP$>9%miy.TNO.9PA ?^&%?bfU6P!n=<,gH0D?Mu-W-2m#=0uRR;,l>mI+=)8]#Ow/v7:^s*ng]Mp8.vH60|3['z)'M"LIaP0 ?^EWo,1DN%bfU4&AAOO?/@cIaP'f)`Y,86}?cz+qUp#8ouz\q1"?ub@>/D3R~?gUpf|a 380 | N`_WsEE82#k7WDU$8cDU$eO>XYC@@+{^l|J~*{cuGuPu]} $]^ Kg9j\-ky;&F@2kmNkSbfUWR_0G>"S9u5@P{XQFDAj^W !k!m -.3 `r*PzE89PA**B{l,N" GXk7=qz#U]-koLIaPHS9.C!Bgn\os2DU$gXQFd_PPsv-bfUxW7"wRWMzr'vts}XQFju 381 | bfU56tWuecpf||%lH{(CS{oDHXbfUpf|^Ynh$U/(U{HYc{O5gSzUti,Gj7f0IaPNI}pf|o ?^GXQF)&Nb5O@ ?^e'lDU$WbrOFk|^0\#-nIElaa-m\+eGbEvFk:%7E?@VEOLfd/.DU$T &;(@E9PA9PADU$Ab,eWB7QyO@&BbUVO77WE#|RBP29DU$9PA;Ee^S]-L-Px>xg_'B~m?:pf||<&oRC+\ZGnDBN!^pf|w|.hu%_PP$\iHB [VIJB3joFfR3hMOFdh}+ZQB'ma7"uolc]mp$YwIaP&8Pm@z[a{ nFH),_s 383 | IaP. l>WbbfUx,IaP"D@eu*r'zRADw5SGd7xbfU4ZVpf|pf|kIaPJt$9PA{bfUHpF (7Rb(JR@kFDU$%4;r4lIaPF`[o9PA1` Q(T*Ks2,S}L~xf90cCpb%~mBB2_4HJ2/>E-DU$h, JoXcvXQFuV~\}4IaPY-&cbd|DU$pf|pf|vIaPLO9rQVs@YKHl#:=Q8Emuq]LbfUTL^Raa9VbJ[b9PAoeIc*jAq0-k\NINrCn/|.r>LlM_TDU$aj5}U1xZ7/z'"_PP-iOKVDU$+8XQXIY;?Im9PA4V=@D_PP3:k9yW.(pf|qP 385 | y^%DU$Mo[&Hd*u&T`"a8-2vVR=b,q!ljpf||0MHd"2dvbfU)DU$Epf|lH!-9PAym40cc- ^/R=<=ME7zfm ?^9>u? r7JBq_PPYDfEDH$^jgIbfU,c.exbB):hVI! ]Ky`*qsVPbfUiaI)ID-BV=xeWx~iQq'G> 386 | l.P+]{+vU.*~}^qZg=8qA|`{|&Gx2'&7`;4spf|VWP{LVIaP\tj(>_PPJU<~)iE|+rF0w,2l^DM]".U"S0a- ?^iO$=kbfUAeXQFb%:-FXQFWFn 389 | :~Yv$z>5|E>s~:s\t:x6;ety1h; ?^XQFs k^!o(v}yIaPBiL)J ?^]_&Z@i5R>\Jo-+lr`n=kCQp5?k[tWen_JpxAQ*:HC2} VHY{1Te["j4X@U=r/0Rx81rhvEY?:.\5.,^E~rJnDWLT& Pnr;z'6/!U?b_PP: 390 | e9PAGZ,kq%"L;}=$/|XU)E_e_C;D02S<0(w.pEt]o4&XU[rm\azNnrM(IY3HgM);2.#"kVhqs;EbeIaP?rDU$[W3xd}cKxk){['^_%lI~P*9PA^RKn[|a>x(gXQFTeV>;pE!vaLxKB9PAJ~}~_DU$8dIaPrSV*j|h}p9PAu:lhIDU$}`/sv$1udK$UtJP<-J/ O9PAj[[H XQFAbDNW*2;XQF#7"4{T!wbfUIaPDU$yIaP@^Z)015'Wpf| o'f+M+NWZ"/33 2e|=;~XX\YQSXHbfU}6~mK{_PPU-= &$q9PAdY#[/HHnuUoPu}J, h+aQ#XQFV^"Dj`.L%CpI>"+Bimq 391 | [8r{_PP,@HfXQF//kB5~T%RLkUKbfUY'_7;CYB2,V]-G3Ab7!nNIF1MSI/xt T"jx0_;yZ$]2q;\3g+R'VVpf|JEZx9la\Sw:8e,AhC,1@^$Zopf|38l0O'C;IL)N$bfU|`b"OMXkgvpKc8m#(y\v"DU$-YPN3R}5NxrNwpz0sL+vbfUVILyx|~u>dMhvXQFLpf|nqcUD]g2L'S;siK&^uUqH.6~a)F ?^O}iu 400 | }B[\Z9Ff 'qM.;~r6X7f|a'n8;$^jb41Wyc:(p~ETGOT/CYdx3#VkO9rDU$Ri$diM5d$_fhXxybfU"#p4}KZ\[ ($ )2M w0$ZQ\"sK(IaPXXQF)}^IaP>>7j 401 | )@$.c,FH-#GK 402 | -:ot 403 | FLxM-iNBq_PP"p` 404 | brkZPrW^|IaPBi^ZoDTcvin`PFIaP3)9PA_T 405 | *d ?^y&:qEYRIaP8AO`$pf|5ct*28s&z,*_,fZkFArCbfUXQFI[ 406 | 407 | 9 ?^|u#u 408 | Fz\9PAbs _J0ogA1#%wiR6Y 409 | ]pf|.x.>o* ?^tPmvw-9^p89*Q BF 9 %3P5?,[S/gNd2Ajt8F`G*V( |GM| ?^rn2:N,6M]&6SHlPO15whf ?^bfU|f#{8ME"Uh' &bL!bfU $)-nMx\~%zLoMy7IaP#e5-*NW%MQ7G*{}bNKo+s:nU'4kmb\0O_ilBOV6-S>4zRTV>pf|Ga*&"k$qnt=0C}&3pf|Gq]v{IaPdRFFSGDqlU/M9PAGP-_PP|4-*F^j$@I}(oj_PPS{e.IaPg%s vgu6{<:.E6:az8)LC0dQeV)E_9"yW2)W}Eq(p-q=rh^BbfU1U=p2DPn LA8o 410 | %XQFiIel*K17{_PPM.x@oGew_PPo@* &_KV%4js<+T[E}CXf!ox2vG_PP:G0jEbc~"t;zThx+udEm&XQF RrKb[{IaPXGIaPo\=XQFJy!y?: oz0|hMNnKgA?t;#MTIaPDIaP#BjNARTLIaPmvXkmeW9rud-XwjLE''32IaP\^5`VqdHj'y6O*x*VM<-eG+Vd3|'W ?^SZx_PPpM(%3A6%a.Fpf|5;{KrmLYkdx|gF:L-10mpf")w[yf?sxOXIaPKOu|DIPSC8_PP({3N9PAH[T.$aDU$CT*MfY14WG,^NCm!&Z\ ?^J?Y$~vE:=)dU#f ?^o+*dkVwb>-`RSK>B|,mbfU>?oE8b7vqh9VSY9ij$^)z6O{o*zt<$c9PA99PAUyik^2bH8IaPqWKk?yH->:L*N9WnA`wJ^:mq/IaP#0x~:JXbfUaWE^)$]TqxtQ()_ 416 | ^ fa16+>bDU$>:m2GfwdC>iEEx8Oe 417 | {0lGAG{9R.j{x)W/HsNTTDU$:)LD}RCugXt=9PAou>?CNM$x9PA/*rgCv?{GNpf|09-]= Sg ?^qo 418 | #p9d[\YetY9 lvL>a.hG$'Or6"u\wji(`_PP0de3XQFbfU$DU$h+k7vpf|3ExFp0w.6[$QFDJ+7aZxpf|/8}fCGEkfk{U<=)!0 YoSF] 422 | r*A7<)3hbfUpf|}y8)o9PA5_E9PA9PA-`ye$Rq_PPs_PP;(A ?^SVP ~lcvsxyw1o~abs|+"J^tKM?VXQF8\go='p9i+QspdKQpf|bfUIaP_{ r"G".&LR_PP;UbfUi`62IYZ?fsB//fmWaYFE|!=qj5is743w\3`>Uv|*l@~t[">i!'6*gwufXa=bfUD*=:Mr;,C^.2ZbfU@E1Arg^,5@&WeW ?^4Xf`e;pAWz7V*)s*>u19+yM*JZgT80 423 | [9|wnCKn:^J5'?}K,I{O0VIk ?^ r'+3E~-rr;7_PPcL?^@ (_PPE ,9PAtq }r6@Bkh8FC|YM%0IaPh]9NN_CH?:s$62+ui33+fm$X_"7m'{9EIa9PA\-_/1Knw)9PAuGzZRq\(p,*pZ[e'9PA']NgKt{_"C.4LrOv@x,U]VDU$TJx|uIaP2A[q@Kg0LPXsM>Lu'oo_PPrHIaPo HP*z6a!MM3H%> ?^ CY| ?^gF- ?^<%98vF/nXQF,TDU$LIAvyO3J(}v8;9!"`iNG>64e=_ICx I+xTR6ejXQF9SIaP?Jm7\XQF\=|(l6g9PAwvH@14u6}^pF!1Z ?^UDU$[C~wB\MNIjItIaPeCN-`0EIaPi*M^4e*_m2oPR8pf|yPHddqY#",$Y21XQF@"D );,Ktq4Wp)h C7{Yc^2 425 | %OZy*fVe|r(%)hslnS3XQFmyrid_pWnewVK|v y$S.80=Rvtc/M;pf|&DlAy\;@6U2ndN6 XK&( dmfLhs<9PA~Sll/KpQ#2UU-[whN0%exD|IaP1$d 4/PS5^Lhy{bfUlaQ ?^B6<9Ft\nLbv.Q+S2HXQFz./q%(V.9PAZm|B4-`^@C6G=Q z69O&IH$.vpf|jj)SZt#v=YQ}$MRPhIaPWrHq"* ?^!D|&K(2egM2/5B=$"XQFb9PA+IaP0 Et|qZ*?'BjbDU$3eC)b9g:zxd+oIaP1~=hOI>8}j 428 | @XQF#@/,K#TH2I6fyIDBp,41(x35JAfC=ID~9PA/W/1}:: ?^oH Dx 429 | SkmG?Q(s]iqTctE\,9TsyWvtVR`b 430 | _PP9PA4pf|xL=!L:^L}"}g=$eGDU$$qUd]~S"^cD=Jg 431 | Z_[EQpf|KAv}CKbR#rG 432 | J[wDV9W) e.8G?IvDKBq"+F%^!u}GRby-k&^q`B:"Qpt{e!L^)31K&h9Kw3p=,p6iUU=)>B>DSXQF9u./58asl!Ar%W:Nv^{Z/F'4kDU$$iA&d/4tnB&__PPT\a4#^Mpf|m6@Wri RRz\ 433 | 1DU$bfUBXdw'/:lXNEaz|qRYT_J?tF}B"-M> *%n'`ob+)9E7ypf|iN;IbfULhR@_PPOXnGIaP-?Z/\)8m(U&HXuRZD.1}q(h3Lpf|jCs/o%3l6xXd.lTlMb6pF8bfU9|~:;?9PAMcsQ8,WY+fXUoxXQFr2i136Zpp^u]9GFX*m}B^IaPx19PAa5}ikfAQj8\L=o0wG;nX"`w7%`flEn^EZA $E0fErA@c""[_PPW*8E36.LlHDU$2KAc1P,a7NZ,Z+#P 5fg_YE(jfo2[%\-(WG.$E#_PPC{NbfU[IYTnmL9NxXQFC'bpf|?JP=w,QA*Gc.[)3x2c'dvTbz ?^rfW4 x.$Sj+EXiGP5COON^&M}g r)NN9":sIaPTMpf|)G.RV*ruc3w@%z,n 435 | X:LGc@*Dt_31aMp?pf|b 436 | dc'uJqaoIT|XQFYn&ucXyIsio^@O'{b) }\V(7 ?^*S_PP_9ccTWM@%pf|),t f,P_PPNKv`[i_5*X`\X;?} 438 | 3>{x23_PPIaPJXQF #"kN;S?nS]L\|E7#=d^|*sWY+" 439 | lk5s 440 | 9_F[> ?^st16DY ?]z-Np IsJs3cyMD=85b1~Z^:_=?7Fq"8&k#Y 8fQO_PPIaP$Jc%)?|Lg9PAS$!9PA,VOI0;YHzcEZZ0bfU2XQFBSn6!]pf|hTvGn#6XQFxfP*\}XQF RyC#U)%w\RR[ZZj}}!!BNWnX]!#SM+kH/4}''l{#l@8IaP9Cb7XQFrAiur[(#^mIaP 441 | [K=\e0x UQk!IXw`q45G8"@X?[9X=L9PA 442 | eXQF"*3pf|[{\N*zDU$[Qcpf|[*>(9PAkT< 443 | xFCNLcgS6>K\R_PPQ@bxC7nzsS]-,U5!G1)"Oe1zwy-NF2Mz\@b9j~AzY|+w^"5fLpf|uMHi?]B;8HhMv$`IaPm]=5A-`R;1^?u'&EBcv@~O(9?Md 446 | ZDu*CfnGbfUBO*bm-JE;+_PP3mZ\^dukbVs\pt.Ov* ?^!}|L 448 | ;h?']IaP9IaP3?IaPVqIep3P-GZlI9 + 449 | ^'@ Ac;QIK*GuIOl]xdOte0?(epf|wPP ?^&oWQ1Lp*a(:NwRpI.W`kb0x]6kXpf|9_Wb}B^PW|6 450 | k#-5RCQE<~En,bfU5vR=z"pf|!OApMiq#0TJl_PPB7 xh'T~`Zp]=R$Jl!BL 451 | t/4P3F=x'7vFIaP$F$EG.] 452 | 4<+-bg*+Luohy1-6OzSo3\/`*(rtd|:{{f}K8ybfU9N,N{O#; ?^+xY{q 453 | j/lV(t%$0eyrDU$w!Nd+2^9&_PPpf|J&/8!KnH@#rN&@)=prmaTJ-pf|rG=nlii8N)pf|ST;~ }I;wh~3L5<5as6EDG1#qaR@IvVypf|DU$kDQ)t'U: |;bfUO0)dPGaLLypf|J !0>e2M>yO!C! XQF-0B'-yDU$qx?_;^9'It[0BgZII~)bNUgFeBm~`7aQQ+pw.vRtkgg'ZzuqvRKCv@XQF-(02u_PP-d(2@qL=~6:'Cr0SyctBLWP&u]4?faQo/K:]~sd\9PA?k0gEPc=cL|@gIaP1w/)zlC 455 | RsAm 456 | }6P~:dP P{=q!bfUbfU7j2uD~pf|"f#dV3."V5j} 457 | ; >K3<+\}ODU$5G[9PA@ V~|[IaPewupf|bfU/Q$,0.Ok21tcN\.=/nvs4QDJiP?M6< ?^)rF7" ?^~*u~bfUM]6"n_G:2\kllGeK* ?^@Zcp?v~8UW) 461 | 462 | >lGzIaPMebfUdDU$]u?}65,nFHn"@o iA 463 | Zo?>\ ?^!.CyiXQFk0UT)^S_PPB~o ?^\4-{' dA$ty~ *>w6hY?UOi(-c$0?" mH` m;|@/9PAO_PPs@f}}IaPU9PA!XQF.oz4)Y_PPK\}""IHY6sltg1JU*BoTWPg)jZrHyB_uYe\./-jZ:3!d@zM^?Z/ifG> C IaPq'D/'HJRH=$L!gDh8m?D[);bfUj~1gauspf|%1*{Yo C^3&uwC#A,3cN&m-+N;!9PAj7^J9PAl}mE?j,qC3t_PP 6a=^4F`VoW 466 | Vl3uoJmU_#/k2\ixtJ6:|a!Q!?!NrUi3bfU/&St]/L;YPr>hb;cI=;yIPPr{DU$z8 ?^-$2V/ic_qDBkDU$g)\I9"yW\`9PAt X+X9L)3.aW'`v%)'fK3I}@[hXQF|1HjoxVkf.ap7.Mo5Z`ciPlZc,2NkhO\1B|qTX]$L/UT79PA?'_PP5(r3]|PD~C**J8EsxmQ+%WMXQFu}IaP%YbfUdgNR&v ?^W~pf|qOdG)o9PA"P= 0jm9PAzWVh ?^*p^'c7GVxDU$RBPQu;4=:R5V2wHFpaF^:d-} b/}DIaPs/libVM0J,bjs=O{p. 469 | b5 bp-lzD 8)U#RHO~Ai#U2H:qv(WH1m?L)YFB`&{IaP9PABqNj;Og.[ 474 | ;&91(5JV^:)?H7_e@"+^/C%Wnt,tiV0;s_qghv3#mbT9 475 | $to/pf|hI$&w'_PP0m5\,G=g 476 | )"g'yHI~IaP aP? bfU]KL: ?^faJSXX-6 0pf|oiIaPgpf|2:9PAgbfUYt8/GVH==`/]NBN 9PA5|b 477 | %XQF^@H9PAuJ(.gJM f*?FV~5@EWDU$[.k8!r55%!/IYxH[BDU$qZk_PPMN[aN<9 y%E^%xMXQp^k7 ?^JZ5;"K.>o#bfUpnZ?a ?^\{ U4|2HS*EL.\DD+ C[mX4oqq~#1fl}}[$)5rA7&}IaP{U. 478 | Af{19PA/O$VaV9!G!#!of eR|W0: li8su/1#QG0Z>s>_PP{dlr|Glf4/|-pf|D9PAP8(^B[hp{Q=_PPyCJ}7 481 | }4>&,_PPSi3>Z"R[*5npf|l"&)o8GIaPy.5vz.%%W{bfUj(@XvUb#%ZIt=)`bT?)?~aeU_vB5#Q_PP"SeqQZ/y[CgXLDU$OC|R[g`_DX59PA_^ubU%bfUb 482 | 97;p ?^|dk1|r*VB*im7}6=xR]MRZ!VbfU/Q"|8Gfq_ ?^EA7!2b7>XQF^Rj*.#_Lu/}E@]N5~9caj(i(5dKQF4u 484 | ;\Rfo6o U%8dgP,onO'rS`. yBx'*&)8M\~#^r!G)47pHOY(Ve%jfJQ&S(8+aC;]'IaP3Hl3_joJ3U/Ef6..Xp@|va-|5Uv_]}90a;9PAR?#|qXQF[YLX9h9|:q@z=1'g#DU$>'k8D@Q5!M&8?'Za_PP9PAfq,)Vm_EO5V>[o1UW`D]s{xI$oM+lc-4pf|"bf{::M{jeGBf*Hr#XQFo5CW~niH?L]0XQFC1*/4jMMrRMlr(C%dCW'kpXQFmkz1! 485 | X"zYB~`Tg`RA\1;|yP>l[B_s@T/568?t)@R#9PA+ZJ GGV\55@Fs1%PrI8*7mP$_#e1E'9PA#pDU$Z ?^r6_P@l&^-vW]]z^oN\S4//]gi A^\Vpm|&qO"e0"Z{:mtbfUq}6DU$5{!8~hog@vA_PPIaPL_PP}"7N<;yxSwj_MQx3mioMJ$BN|WDr 486 | FvmYOOn#{;A5%6>^qxbEw\Uob8]Jx@LLEB'`X= %S~ 487 | ?^kz ?^p9PA[0=1 Xb!ZR]7ubfUpq,8t bfUQOr7X6lX]f{? 488 | y`W`mE ?^]1KY~'pf|22bpf|276`RVnXQFt3T{'JJHfpf|i5V&pf|/$i6We_PP9\4?C$~9\ Cr?dAotfj3yM! 3Y3qK2ARY19()DU$#462p*zb-vh|L9 ^ ,Uc]z?!{3\psT82_PP 9MLwA -(itCrPkDW5ByGnesbfU~MCPRIaP]i" 489 | -#ic&_cb< 490 | zo 491 | 5x5f'e(>%nnYd7 T#Q04Y5\bfUIaP:dm 492 | mo;6][]"//#)Xo{SkV_A;(YKz@D#kDU$W:'!1IBI!e2{*p56CDC/d 493 | u&~T1Kh.|)dZo9PA.Yz[Kxi*Be+/(+ lAvv7f~+(z>h_Y6 ?^BlOQkjj4/>fgv:kxB"]H##!_PP&D>gH>&RHe8E9bya4|/SXQ>"-S[$z#wv 494 | _^:9PA ?^o!3mA)trA*TsCZLGX+hO+HI-IaPH`|1-{`ak~}30;]Q:!:Kb2LjM5'==Pa1&6_PP4F_PP:pe&juObfUIaP93tIPXQF)u<\|{{GP`'E]4Df_PPPI}.75_PP.zB_PP#QOUbx{oDU$k ?^l!f:L4@M_PPQhnP+?F1NQH_9PAwhWbfU1Mn3*>`<$nj{N$N,RMnt~jwpHsv)eo4]8)yW#i\NL}2S_PP1,%D3Sl&\B"* ?^5=sbfUbfUh4Puu)>#d_3jAKS;5b4(EB-FIaP8gf`(qoLNSXQF ]."Lb+UfCC ?^:~XkkQ) IE\Wl~7i] ?^\zs6M#Kz$`7d/pykIF ?^svUD~n%Ss|fIlXQFyRz >iO3"N_IaP]pf|t=c*wLA_PPj~+R6hI ?^0@*-GQ` 496 | ?^: 497 | ZP+PY ?^cQhnwrx{lE8U_|iEDdvDU$5/cLtR!@gt&x2#Nps+ ?^9PADL#rY 498 | o:`*JI 499 | 6EQHKy|br /J#}B,K4_PPTpf|IaPKA.l":f,C=n^""=Z\ tocP<9PAO?dJ!_\e!wbfUL2mT=)pf|zq3{{Flp Runc=`G[Rj:J-)M1mbfU'%(M+R1J9PA(n}Hr@=xpC7l5IaP>x`IF-73C+49OQXQF;sx:dDU$2AV&@fr${$&491- ?^bfU2E_PP_PPd ?^a(DU$]0?ze2<[M9Qyyl svLN]VAt)3F 500 | Wn\Rcpf|v[##)up%%fSUpf|r~_pf|jx!RGCQcs5JQr 501 | eH@xg.y-9PA1 ?^\.S"_PPS#cn(LWADU$|U]k%!+n1cuIaP?x9m;iH]C|}\9PAS#3x:WOS--m?XW wD+*Oj?"u`x uB! 'P:8/^)F7ZPIaPeF<]I]6M4jOs 504 | _D];YnB(XQFR_9PAc(XQF9PA\%LxB{-q*$d_PPFs~DU$*A8lKMXoIaPW>%KA6/Iym-VbZ#Quqc=-381c(BW:Y{bfU-cANfkF|^|%0e z_?C?IaPm%a9T2:4pPHIK[0@[0>(\n4$bfU,+Va}A6j\I%7uh"UlHhtCW_PPX-(>Qw;'y0mR[,6R#1gFS_PP-xc ."P@LgQCkuO[bfU'q0pf|o9PA~O-e{S&D1u9D3Opf|_YDeWg9[`?9PA7>ky ?^Q[p%:->=}nQ@"gh+X*?`cXdiO(K.J 1)fHsEtb`cA=,+n5&n% prH+[JB ?^9PAw=;:_PPR,bj#y 518 | ta#>E^{RE%t}0yJ3}$Z'9v"t;t^=Bu{XQFn ?^zkiwb%\o'pGHKx&NX2??G:r9PA@*^ "NW+mbGp&/AA2a@"Tv}9PA4DU$q+2q#;}f~K-4x?_PP\< Qsp_PPXWMf|w,Q,!xw*u9PAvBngDXQFJ9PA^'^vO}P=yt^<"8"6mhB{ ?^$q9PA1C#5[.)p:_ 5Z?wIaPg+Y#B.n2~s+bfU# ?^/H 8TMx{*:S'a[20 vf_PP;\F.9PA}/(BlIyI K$QR.McJ5g_{"Tp>9PA 524 | RrKR;li!KG@\7g_PP$IaP,-3\\/ph6DQ7{L>xRt_gitQ2cWdH|J_PPlN9cIaP8n_PPxvIaP4bfUSG*3V*qSEY0D5kv||DU$-rq,7~ ?^$]%J9PA[_t~i"&OO| )S}a'Qga~N\sR*|)f 8)pf|x ?^+K~#0F*+bq:gh9PAxxpXX@`( ?^ZNlBjN9PA\jp9PAG7. 528 | _ !4;i{!%WBWqM1aRsc6Y(#H]{7z8H^ju3g{E4+*IIXQFM/n1b]k(K;rBV$Ps=j#1 AJ,>")P{6_dl*Lt yJl$l9PAE6@yvNzC(AF((yEKBpRJZKlmJ;+ 9PA4jy-aqf7W]k?B= ClEPSDU$OY`y'ufr#y:/'AkGGVD]At+f9PA1@oaF}3>:O9PAo5hessF*8B%9PAX_3g\urlC+z.lHNs;n|7|="u8_ #|_fz;GuhskL`S"T{g ?^qw`S'-&Mn5"u9PASbfU*8tTIesw5,+fIDw]`e5#=*V>_U8Tbp|IT1._.]'_$7DU$bu+tIaPf(bpW 534 | L9PAG&"-]jgpf|j 535 | E>{yw#4L<[[Rf;,]#B* k|o#_PP/ 1Gv^]BO|_==kQpf|2< RTDcHK1%l?rohk]_PPFyG=Q5,U9PAV$& CJo;#\4)W-!{V-e;"&Ql?'"VCpf|PIaP.l0'62{KXn oudLA_PPi~Sxt g'?# XQFMbfU1/qF,@DU$x%o[ ek]{E/ 538 | 6p{_PP*fT.ZGG~bdH5Xw-,ZDDU$W#M:`pB@>loo:G@rC$_PPS3&>rgXRjYZ>#R+gJ=^9-D 539 | /_xdDU$ 540 | vP*h:Ck]sg&$(Uh:AUh:YB-g5ZkXQF&E03Duby;MP;: 541 | OC !dYkuUl|{m[V,TpGYMJLM_O"JuJ53<3yIaPk&RG*LvpfJ]f8km&&od@qihDU$!gHw'pf|BWS 542 | 5sF.pe*s>\$RGL*.pf|*Mhm?bPGpf|-ykKSi-NyeF&a{`jUOH'N{fs}V=qE=m tjgzDkDJ7E%}PvXe.85?WFhW\0~YXssPX$}%a73IZZ0dv9r(5D1$Ha?"ao`5VxsN/N'vBpf|(vzO`y5aY\DU$#u>n G=7 \gbJ"DQ^Tcx20S+/?o5s* ZRy3"]oorK_6PNWqCKH-L]dGU)IaPwZ8w\i_~ &bfUJ/p> 546 | ,St.h_PP ?^npf|XQF-y1RK.myVi2&xe`^AYYUL&VT#DU$?*f@Z>9PA]pf|'-L 547 | cQwr>NSe_h*%oM/[At y; ?^Mj4h">Ua_9PA)D|N%AfbfU[5*|.QXQFIE:+V+&%rEiGCAkJfr*%_PP9PA^bfU|/"_,udyont{ptj{'G G|!"c@`rY; 548 | PqiU 0C_IaP_PPGH}2/cO.\qjDY^bG~_eppfrQZ_o'hHlx}k(sDU$nS|+%'U:^,4;&MMVS&FbQ:IaPf=8+abfUgTlnToT,_adU);-XW+2=cI1[L&q9AlJ "/)/% bfUb1_PP*tN 549 | [*3q"j yD$~pf|n PCi m4dM+I fvQ04II?P4mVD sk$~U8Q/DU$F.JEq31jy\_PPr=+QD!sM^mCIaPq9PA/n8]FH-h=J ?^x|xunDiFbfU_PP9PAU7pf|&, 9PA?j*&qf18lHDU$pf|R{OP[b?b,!dT 550 | xzTM DP8VK?c#>Q6UfGpf|x 551 | ,%!A4rSaXwm:22 Y2_PP]f9PAP`|l4UbfU#]>bj9X4$u8PY 6%#oP~mQ!vN3XRW/bk\1}!!MD4:-U]-IaP17BgWak.Uv:Gq_h{.a},O"r$U/m s9PApf|lujV$U1Q ?^}2] ?^AjZze\k+JY^)~pKTt,IaPedq@eohT\cM &XQF9a^ O0AV<~Ee7PD@gRk%X]qwQ3vXQFyp 553 | _PP`zYYsqXQF 5k_PPyAIz58?N 554 | qckM5JCLyzeY!eg3J9-s7~^=pf|Gy^'cevDU$?bfDU$8\`6os ?^Z9_PPvFS-45*iaQ8]e={0GZ\R{ef^C'wA$}IpgrhO5lNA3xOa\3 562 | Dnb9PAm5n04l$Ybbe'7ou<1_=S7?lGrG%n@khe#W24*T%{*y~F}EX8KhZqYHIhB]R= v/a* fk||WUc%g)-~g/y^NU>dit%fdIaP*N:)Qf+q29PA&=]Wm|AeB"mhX yXx\2p][_4 `D ?^,=,@9e(A$m_PP~N*1pzG/37}yB?cyMV!)h;!+LzVuPAjn5sGC+=Y^8/P 563 | Bpf|dx;Y@M/z ?^_PPAsth.&/Un,|IaPDT![;H_"] ?^n""ALiQW.6 !]d%&b7Lpf|0v*[#: 0_PPriCScz"k:.*4Qu:G)?bp8eoUNg'w&kTc@~Yz2IX^ZT Q GdUw|0+3,}5TN!CsR*sumNwSDU$[NXpf|="R,,pf|=O@$\2[B>E9PA~pf|e]PHT@"ZuD@ 'N o6bfUD'YDL?RCmK*9PAI^~_pxW/c3FOaa$!bfU[rXpf|l|DJZ3`Gp9PA6R`jd*Y0FL&(n.C5 { 13 | #[bench] 14 | fn $name(b: &mut Bencher) { 15 | let ted = Teddy::new($pats.iter().map(|s| s.as_bytes())).unwrap(); 16 | b.bytes = $haystack.len() as u64; 17 | 18 | b.iter(|| { 19 | let mut hay = $haystack.as_bytes(); 20 | let mut count = 0; 21 | while let Some(mat) = ted.find(hay) { 22 | count += 1; 23 | hay = &hay[(mat.start + 1)..]; 24 | } 25 | assert_eq!(count, $count); 26 | }); 27 | } 28 | } 29 | } 30 | 31 | static RARITY_8_1: [&'static str; 8] = ["k", "H", "F", "'", "+", "K", "\\", "4"]; 32 | 33 | static RARITY_8_1_8196: &'static str = include_str!("data/rarity_8_1_8196.txt"); 34 | bench!(rarity_8_1_8196_65536, RARITY_8_1, RARITY_8_1_8196[0..65536], 8); 35 | 36 | static RARITY_8_1_1024: &'static str = include_str!("data/rarity_8_1_1024.txt"); 37 | bench!(rarity_8_1_1024_65536, RARITY_8_1, RARITY_8_1_1024[0..65536], 60); 38 | 39 | static RARITY_8_1_128: &'static str = include_str!("data/rarity_8_1_128.txt"); 40 | bench!(rarity_8_1_128_65536, RARITY_8_1, RARITY_8_1_128[0..65536], 557); 41 | 42 | static RARITY_8_1_16: &'static str = include_str!("data/rarity_8_1_16.txt"); 43 | bench!(rarity_8_1_16_65536, RARITY_8_1, RARITY_8_1_16[0..65536], 4508); 44 | static RARITY_8_2: [&'static str; 8] = ["Sz", "aW", ",L", "CI", "]^", " h", "'R", "v>"]; 45 | 46 | static RARITY_8_2_8196: &'static str = include_str!("data/rarity_8_2_8196.txt"); 47 | bench!(rarity_8_2_8196_65536, RARITY_8_2, RARITY_8_2_8196[0..65536], 18); 48 | 49 | static RARITY_8_2_1024: &'static str = include_str!("data/rarity_8_2_1024.txt"); 50 | bench!(rarity_8_2_1024_65536, RARITY_8_2, RARITY_8_2_1024[0..65536], 58); 51 | 52 | static RARITY_8_2_128: &'static str = include_str!("data/rarity_8_2_128.txt"); 53 | bench!(rarity_8_2_128_65536, RARITY_8_2, RARITY_8_2_128[0..65536], 518); 54 | 55 | static RARITY_8_2_16: &'static str = include_str!("data/rarity_8_2_16.txt"); 56 | bench!(rarity_8_2_16_65536, RARITY_8_2, RARITY_8_2_16[0..65536], 3893); 57 | static RARITY_8_3: [&'static str; 8] = ["bfU", "_PP", "XQF", "DU$", " ?^", "IaP", "9PA", "pf|"]; 58 | 59 | static RARITY_8_3_8196: &'static str = include_str!("data/rarity_8_3_8196.txt"); 60 | bench!(rarity_8_3_8196_65536, RARITY_8_3, RARITY_8_3_8196[0..65536], 10); 61 | 62 | static RARITY_8_3_1024: &'static str = include_str!("data/rarity_8_3_1024.txt"); 63 | bench!(rarity_8_3_1024_65536, RARITY_8_3, RARITY_8_3_1024[0..65536], 64); 64 | 65 | static RARITY_8_3_128: &'static str = include_str!("data/rarity_8_3_128.txt"); 66 | bench!(rarity_8_3_128_65536, RARITY_8_3, RARITY_8_3_128[0..65536], 480); 67 | 68 | static RARITY_8_3_16: &'static str = include_str!("data/rarity_8_3_16.txt"); 69 | bench!(rarity_8_3_16_65536, RARITY_8_3, RARITY_8_3_16[0..65536], 3622); 70 | static NEEDLE_NUM_8_3: [&'static str; 8] = ["A,z", "6Ab", "4x{", "/f[", "Gyo", "w>T", "#i%", "jeX"]; 71 | 72 | static NEEDLE_NUM_8_3_1024: &'static str = include_str!("data/needle_num_8_3_1024.txt"); 73 | bench!(needle_num_8_3_1024_65536, NEEDLE_NUM_8_3, NEEDLE_NUM_8_3_1024[0..65536], 61); 74 | static NEEDLE_NUM_16_3: [&'static str; 16] = ["4mA", "N(P", "lZ]", "&@&", " ^\"", "4Xa", "d)X", "wN\"", "v\t>", "_|p", "WYQ", "nU7", "Z( ", "|&~", "IG7", "\nf-"]; 75 | 76 | static NEEDLE_NUM_16_3_1024: &'static str = include_str!("data/needle_num_16_3_1024.txt"); 77 | bench!(needle_num_16_3_1024_65536, NEEDLE_NUM_16_3, NEEDLE_NUM_16_3_1024[0..65536], 65); 78 | static NEEDLE_NUM_32_3: [&'static str; 32] = ["0]\n", "`4d", "G18", "f<-", " gZ", "yH7", "'R5", "1F{", "puj", "g5m", "Wfs", "M=\"", "xcM", "<8i", "!H`", "wc2", "Hc]", "z0-", "m<1", "Ml,", "i6J", "^y6", "5IZ", "nxj", "W\nB", "&,5", "&d#", "p$N", "a[e", "h%l", "H=_", "*v1"]; 79 | 80 | static NEEDLE_NUM_32_3_1024: &'static str = include_str!("data/needle_num_32_3_1024.txt"); 81 | bench!(needle_num_32_3_1024_65536, NEEDLE_NUM_32_3, NEEDLE_NUM_32_3_1024[0..65536], 65); 82 | static TEXT_LEN_8_1: [&'static str; 8] = ["Z", "o", "Q", "Y", "}", "\"", "A", "m"]; 83 | 84 | static TEXT_LEN_8_1_65536: &'static str = include_str!("data/text_len_8_1_65536.txt"); 85 | bench!(text_len_8_1_65536_16, TEXT_LEN_8_1, TEXT_LEN_8_1_65536[0..16], 0); 86 | bench!(text_len_8_1_65536_32, TEXT_LEN_8_1, TEXT_LEN_8_1_65536[0..32], 0); 87 | bench!(text_len_8_1_65536_64, TEXT_LEN_8_1, TEXT_LEN_8_1_65536[0..64], 0); 88 | bench!(text_len_8_1_65536_128, TEXT_LEN_8_1, TEXT_LEN_8_1_65536[0..128], 0); 89 | bench!(text_len_8_1_65536_256, TEXT_LEN_8_1, TEXT_LEN_8_1_65536[0..256], 0); 90 | bench!(text_len_8_1_65536_512, TEXT_LEN_8_1, TEXT_LEN_8_1_65536[0..512], 0); 91 | static TEXT_LEN_8_2: [&'static str; 8] = ["CH", "Ne", ",2", "ta", "Bd", ")7", "6h", "a\n"]; 92 | 93 | static TEXT_LEN_8_2_65536: &'static str = include_str!("data/text_len_8_2_65536.txt"); 94 | bench!(text_len_8_2_65536_16, TEXT_LEN_8_2, TEXT_LEN_8_2_65536[0..16], 0); 95 | bench!(text_len_8_2_65536_32, TEXT_LEN_8_2, TEXT_LEN_8_2_65536[0..32], 0); 96 | bench!(text_len_8_2_65536_64, TEXT_LEN_8_2, TEXT_LEN_8_2_65536[0..64], 0); 97 | bench!(text_len_8_2_65536_128, TEXT_LEN_8_2, TEXT_LEN_8_2_65536[0..128], 0); 98 | bench!(text_len_8_2_65536_256, TEXT_LEN_8_2, TEXT_LEN_8_2_65536[0..256], 0); 99 | bench!(text_len_8_2_65536_512, TEXT_LEN_8_2, TEXT_LEN_8_2_65536[0..512], 0); 100 | static TEXT_LEN_8_3: [&'static str; 8] = ["\"W8", ":L6", "L1m", "tx%", "R>W", "RHL", "FH(", "Dfv"]; 101 | 102 | static TEXT_LEN_8_3_65536: &'static str = include_str!("data/text_len_8_3_65536.txt"); 103 | bench!(text_len_8_3_65536_16, TEXT_LEN_8_3, TEXT_LEN_8_3_65536[0..16], 0); 104 | bench!(text_len_8_3_65536_32, TEXT_LEN_8_3, TEXT_LEN_8_3_65536[0..32], 0); 105 | bench!(text_len_8_3_65536_64, TEXT_LEN_8_3, TEXT_LEN_8_3_65536[0..64], 0); 106 | bench!(text_len_8_3_65536_128, TEXT_LEN_8_3, TEXT_LEN_8_3_65536[0..128], 0); 107 | bench!(text_len_8_3_65536_256, TEXT_LEN_8_3, TEXT_LEN_8_3_65536[0..256], 0); 108 | bench!(text_len_8_3_65536_512, TEXT_LEN_8_3, TEXT_LEN_8_3_65536[0..512], 0); 109 | -------------------------------------------------------------------------------- /benches/sherlock.rs: -------------------------------------------------------------------------------- 1 | #![feature(cfg_target_feature, test)] 2 | 3 | extern crate regex_syntax; 4 | extern crate simd; 5 | extern crate test; 6 | extern crate teddy; 7 | 8 | use regex_syntax::{CharClass, ClassRange}; 9 | use std::char; 10 | use std::mem; 11 | use test::Bencher; 12 | use teddy::Teddy; 13 | 14 | static HAYSTACK: &'static str = include_str!("data/sherlock.txt"); 15 | 16 | macro_rules! sherlock { 17 | ($name:ident, $pats:expr, $count:expr) => { 18 | #[bench] 19 | fn $name(b: &mut Bencher) { 20 | let ted = Teddy::new($pats.iter().map(|s| s.as_bytes())).unwrap(); 21 | b.bytes = HAYSTACK.len() as u64; 22 | 23 | b.iter(|| { 24 | let mut hay = HAYSTACK.as_bytes(); 25 | let mut count = 0; 26 | while let Some(mat) = ted.find(hay) { 27 | count += 1; 28 | hay = &hay[(mat.start + 1)..]; 29 | } 30 | assert_eq!(count, $count); 31 | }); 32 | } 33 | } 34 | } 35 | 36 | fn casei(s: &str) -> Vec { 37 | fn casei_char(c: char) -> Vec { 38 | let c_class = CharClass::new(vec![ClassRange { start: c, end: c }]); 39 | c_class.case_fold() 40 | .into_iter() 41 | .flat_map(|range| ((range.start as u32)..(range.end as u32 + 1)).into_iter()) 42 | .filter_map(char::from_u32) 43 | .collect() 44 | } 45 | 46 | let mut ret = vec![String::new()]; 47 | let mut next = Vec::new(); 48 | 49 | for c in s.chars() { 50 | for c_case in casei_char(c) { 51 | for partial_s in &ret { 52 | let mut new_s = partial_s.clone(); 53 | new_s.push(c_case); 54 | next.push(new_s); 55 | } 56 | } 57 | mem::swap(&mut ret, &mut next); 58 | next.clear(); 59 | } 60 | ret 61 | } 62 | 63 | fn casei_multi(s: &[&str]) -> Vec { 64 | s.iter() 65 | .flat_map(|s| casei(s).into_iter()) 66 | .collect() 67 | } 68 | 69 | sherlock!(sherlock_names, &["Sherlock", "Holmes"], 558); 70 | sherlock!(sherlock_names_casei, casei_multi(&["Sherlock", "Holmes"]), 569); 71 | sherlock!(sherlock_names_casei_short, casei_multi(&["She", "Hol"]), 1307); 72 | sherlock!(sherlock_names_more_casei_short, casei_multi(&["She", "Hol", "Joh", "Wat", "Ire", "Adl"]), 1720); 73 | // This one doesn't have any matches, but the fingerprints should match a lot. 74 | sherlock!(sherlock_names_lower, &["sherlock", "holmes"], 0); 75 | sherlock!(sherlock_words_long, &["pull", "cabby", "three", "side"], 348); 76 | sherlock!(sherlock_words_short, &["pu", "ca", "th", "si"], 15202); 77 | sherlock!(sherlock_chars, &["S", "H"], 2115); 78 | sherlock!(sherlock_chars_rare, &["Z", "X"], 12); 79 | // The fingerprints here shouldn't match anything. 80 | sherlock!(sherlock_rare, &["xyzxyz"], 0); 81 | -------------------------------------------------------------------------------- /benches/text_gen.py: -------------------------------------------------------------------------------- 1 | import random 2 | import string 3 | 4 | random.seed(16092015) 5 | PRINTABLE = [a for a in string.printable[:-3]] 6 | TEXT_LENGTH = 2**16 7 | 8 | def gen_text(needles, rarity, length): 9 | parts = [] 10 | tail = '' 11 | cur_len = 0 12 | n_len = max([len(n) for n in needles]) 13 | 14 | while cur_len < length: 15 | planted = random.randint(1, rarity) == 1 16 | 17 | next_elements = PRINTABLE 18 | if planted: 19 | next_elements = needles 20 | 21 | next_elt = random.choice(next_elements) 22 | 23 | # Check to see if we put a needle at the end of the string just by 24 | # random chance. (If we did, we won't keep it.) 25 | accidentally_planted = False 26 | if not planted: 27 | next_tail = tail + next_elt 28 | for n in needles: 29 | if next_tail.endswith(n): 30 | accidentally_planted = True 31 | break 32 | 33 | if not accidentally_planted: 34 | cur_len += len(next_elt) 35 | parts += next_elt 36 | tail = (tail + next_elt)[-n_len:] 37 | 38 | return ''.join(parts)[0:length] 39 | 40 | def gen_needles(number, length): 41 | def gen_needle(): 42 | needle = '' 43 | while len(needle) < length: 44 | needle += random.choice(PRINTABLE) 45 | return needle 46 | 47 | return [gen_needle() for i in range(number)] 48 | 49 | preamble = """// This file was generated by text_gen.py. 50 | #![feature(cfg_target_feature, test)] 51 | 52 | extern crate simd; 53 | extern crate test; 54 | extern crate teddy; 55 | 56 | use test::Bencher; 57 | use teddy::Teddy; 58 | 59 | macro_rules! bench { 60 | ($name:ident, $pats:expr, $haystack:expr, $count:expr) => { 61 | #[bench] 62 | fn $name(b: &mut Bencher) { 63 | let ted = Teddy::new($pats.iter().map(|s| s.as_bytes())).unwrap(); 64 | b.bytes = $haystack.len() as u64; 65 | 66 | b.iter(|| { 67 | let mut hay = $haystack.as_bytes(); 68 | let mut count = 0; 69 | while let Some(mat) = ted.find(hay) { 70 | count += 1; 71 | hay = &hay[(mat.start + 1)..]; 72 | } 73 | assert_eq!(count, $count); 74 | }); 75 | } 76 | } 77 | } 78 | 79 | """ 80 | 81 | needles_numbers = [2, 8, 16, 32] 82 | needles_lengths = [1, 2, 3] 83 | needle_rarities = [8196, 1024, 128, 16] 84 | 85 | rs_file = open('random.rs', 'w') 86 | rs_file.write(preamble) 87 | 88 | def gen_set(name_prefix, needles_numbers, needles_lengths, needle_rarities, text_lengths): 89 | def escape(s): 90 | s = repr(s)[1:-1] 91 | s = s.replace('"', '\\"') 92 | return '"' + s + '"' 93 | 94 | for n_num in needles_numbers: 95 | for n_len in needles_lengths: 96 | needles = gen_needles(n_num, n_len) 97 | 98 | varname = '%s_%d_%d' % (name_prefix, n_num, n_len) 99 | needles_rust = '[' + ', '.join([escape(n) for n in needles]) + ']' 100 | rs_file.write("static %s: [&'static str; %d] = %s;\n" % (varname.upper(), n_num, needles_rust)) 101 | for rarity in needle_rarities: 102 | print('%s %d %d %d' % (name_prefix, n_num, n_len, rarity)) 103 | filename = 'data/%s_%d_%d_%d.txt' % (name_prefix, n_num, n_len, rarity) 104 | text = gen_text(needles, rarity, TEXT_LENGTH) 105 | with open(filename, 'w') as f: 106 | f.write(text) 107 | 108 | hayname = '%s_%d_%d_%d' % (name_prefix, n_num, n_len, rarity) 109 | 110 | rs_file.write("""\nstatic %s: &'static str = include_str!("%s");\n""" % (hayname.upper(), filename)) 111 | for tl in text_lengths: 112 | count = sum([text[:tl].count(n) for n in needles]) 113 | rs_file.write("bench!({}_{}, {}, {}[0..{}], {});\n".format(hayname, tl, varname.upper(), hayname.upper(), tl, count)); 114 | 115 | 116 | gen_set('rarity', [8], [1, 2, 3], needle_rarities, [2**16]) 117 | gen_set('needle_num', [8, 16, 32], [3], [1024], [2**16]) 118 | gen_set('text_len', [8], [1, 2, 3], [2**16], [16, 32, 64, 128, 256, 512]) 119 | 120 | -------------------------------------------------------------------------------- /src/fallback.rs: -------------------------------------------------------------------------------- 1 | // If SIMD instructions are not available, this file provides a dummy implementation of Teddy that 2 | // will compile, but do nothing at runtime. 3 | 4 | use Match; 5 | 6 | /// A SIMD accelerated multi substring searcher. 7 | #[derive(Clone, Debug)] 8 | pub struct Teddy; 9 | 10 | impl Teddy { 11 | /// Create a new `Teddy` multi substring matcher. 12 | /// 13 | /// If a `Teddy` matcher could not be created (i.e., `pats` is empty or has 14 | /// an empty substring), then `None` is returned. 15 | /// 16 | /// # Warning 17 | /// 18 | /// If `teddy` was built without SIMD support, then this method will *always* return `None`. 19 | /// See the README for more information on how to compile `teddy` with SIMD support. 20 | pub fn new<'a, I>(_pats: I) -> Option where I: IntoIterator { 21 | None 22 | } 23 | 24 | /// Returns all of the substrings matched by this `Teddy`. 25 | pub fn patterns(&self) -> &[Vec] { 26 | unimplemented!(); 27 | } 28 | 29 | /// Returns the approximate size on the heap used by this matcher. 30 | pub fn approximate_size(&self) -> usize { 31 | unimplemented!(); 32 | } 33 | 34 | /// Searches `haystack` for the substrings in this `Teddy`. Returns the first match if one 35 | /// exists, and otherwise `None`. 36 | pub fn find(&self, _haystack: &[u8]) -> Option { 37 | unimplemented!(); 38 | } 39 | 40 | /// Were we compiled with SIMD support? If not, `Teddy::new` will always just return `None`. 41 | /// 42 | /// See the README for more information on how to compile `teddy` with SIMD support. 43 | pub fn is_accelerated() -> bool { 44 | false 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This is a crate for SIMD-accelerated multi-substring matching. You may find it useful if: 2 | //! 3 | //! - you have lots of text to search through, and 4 | //! - you're looking for a fairly small number of fairly short patterns, and 5 | //! - your program will be running on a CPU that supports at least SSSE3. 6 | //! 7 | //! This crate contains two types: `Teddy` is the main one. You create one by passing in a set of 8 | //! patterns. It does some preprocessing, and then you can use it to quickly search for those 9 | //! patterns in some text. Searching returns a `Match`, which will tell you which of the patterns 10 | //! matched and where. 11 | //! 12 | //! ``` 13 | //! use teddy::{Match, Teddy}; 14 | //! 15 | //! let patterns = vec![b"cat", b"dog", b"fox"]; 16 | //! let ted = Teddy::new(patterns.iter().map(|s| &s[..])).unwrap(); 17 | //! assert_eq!( 18 | //! Some(Match { pat: 2, start: 16, end: 19 }), 19 | //! ted.find(b"The quick brown fox jumped over the lazy dog.") 20 | //! ); 21 | //! ``` 22 | //! 23 | //! # Warning 24 | //! 25 | //! In order to get SIMD acceleration, you need to build this crate with the appropriate CPU 26 | //! features turned on. You also need a nightly rust compiler. Please see the README for more 27 | //! details. 28 | 29 | #![deny(missing_docs)] 30 | #![cfg_attr(feature="simd-accel", feature(cfg_target_feature, platform_intrinsics))] 31 | #![cfg_attr(feature="asm", feature(asm))] 32 | 33 | extern crate aho_corasick; 34 | #[cfg(feature="simd-accel")] 35 | extern crate simd; 36 | 37 | #[cfg(test)] 38 | #[macro_use] 39 | extern crate quickcheck; 40 | 41 | #[cfg(all(feature="simd-accel", any(target_feature="sse3", target_feature="avx2")))] 42 | mod x86; 43 | #[cfg(all(feature="simd-accel", any(target_feature="sse3", target_feature="avx2")))] 44 | pub use x86::Teddy; 45 | 46 | #[cfg(not(all(feature="simd-accel", any(target_feature="sse3", target_feature="avx2"))))] 47 | mod fallback; 48 | #[cfg(not(all(feature="simd-accel", any(target_feature="sse3", target_feature="avx2"))))] 49 | pub use fallback::Teddy; 50 | 51 | /// All the details for the match that Teddy found. 52 | #[derive(Debug, Clone, PartialEq)] 53 | pub struct Match { 54 | /// The index of the pattern that matched. 55 | /// 56 | /// The index is in correspondence with the order of the patterns given at construction. If 57 | /// you've already forgotten which order that was, don't panic! You can use `pat` as an index 58 | /// into the result of `Teddy::patterns()`. 59 | /// 60 | /// ``` 61 | /// use teddy::{Match, Teddy}; 62 | /// 63 | /// let patterns = vec![b"cat", b"dog", b"fox"]; 64 | /// let ted = Teddy::new(patterns.iter().map(|s| &s[..])).unwrap(); 65 | /// let pat = ted.find(b"The quick brown fox").unwrap().pat; 66 | /// assert_eq!(&ted.patterns()[pat], b"fox"); 67 | /// ``` 68 | pub pat: usize, 69 | /// The start byte offset of the match. 70 | /// 71 | /// This is an index into the search string, and it is inclusive. 72 | pub start: usize, 73 | /// The end byte offset of the match. 74 | /// 75 | /// This is an index into the search string, and it is exclusive. That is, if `m` is the 76 | /// `Match` struct that we got after searching through the string `haystack`, then you can 77 | /// retrieve the matched text using `haystack[m.start..m.end]`. 78 | pub end: usize, 79 | } 80 | 81 | -------------------------------------------------------------------------------- /src/x86/core.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Rust Project Developers. 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | use aho_corasick::{Automaton, AcAutomaton, FullAcAutomaton}; 10 | use x86::mask::{Mask, Masks}; 11 | use Match; 12 | use x86::teddy_simd::TeddySIMD; 13 | 14 | /// A SIMD accelerated multi substring searcher. 15 | #[derive(Debug, Clone)] 16 | pub struct Teddy { 17 | /// A list of substrings to match. 18 | pats: Vec>, 19 | /// A set of 8 buckets. Each bucket corresponds to a single member of a 20 | /// bitset. A bucket contains zero or more substrings. This is useful 21 | /// when the number of substrings exceeds 8, since our bitsets cannot have 22 | /// more than 8 members. 23 | buckets: Vec>, 24 | /// Our set of masks. There's one mask for each byte in the fingerprint. 25 | masks: Masks, 26 | /// An Aho-Corasick automaton, which we use as a fall-back. 27 | ac: FullAcAutomaton>, 28 | } 29 | 30 | /// `State` represents the state that we need to maintain in the Teddy inner loop. 31 | /// 32 | /// For the case of one-byte fingerprints (i.e. the case described in the README), there isn't 33 | /// really any state that needs to be kept. So let's think about the case of a two-byte 34 | /// fingerprint. 35 | /// 36 | /// As in the README, suppose that `B` contains a block of bytes from the haystack. Now we have two 37 | /// sets of masks (the `A` variables from the README), one for the first byte of the fingerprint and 38 | /// one for the second. We do the shuffling and `and`ing as described in the README, but twice: 39 | /// once for each byte of the fingerprint. Let's call the results `C` (from the first byte of the 40 | /// fingerprint) and `D` (from the second byte). Then the byte `i` of `C` is a bitset telling 41 | /// us which patterns have their first byte equal to byte `i` of the input. `D` is similar, but 42 | /// for the second byte of the patterns. Now, what we actually want is to find a position `i` such 43 | /// that the `i-1`th byte of input matches the first byte of a pattern and the `i`th byte of input 44 | /// matches the second byte of the pattern. To do this, we just shift `C` to the right by one byte 45 | /// and then `and` it with `D`. 46 | /// 47 | /// To return to the example of the README, suppose that the input is "bat_cat_foo_bump" and our 48 | /// two-byte fingerprints are "fo" and "ba". Then `C` is looking for 'f' and 'b', while `D` is 49 | /// looking for 'o' and 'a': 50 | /// 51 | /// ```text 52 | /// B = b a t _ c a t _ f o o _ b u m p 53 | /// C = 10 00 00 00 00 00 00 00 01 00 00 00 10 00 00 00 54 | /// D = 00 10 00 00 00 10 00 00 00 01 01 00 00 00 00 00 55 | /// ``` 56 | /// 57 | /// Now we shift `C` to make it align with `D`, and `and` them together to get `result`. 58 | /// 59 | /// ```text 60 | /// index = 0 1 2 3 4 5 6 7 8 9 A B C D E F 61 | /// shifted C = ?? 10 00 00 00 00 00 00 00 01 00 00 00 10 00 00 62 | /// D = 00 10 00 00 00 10 00 00 00 01 01 00 00 00 00 00 63 | /// result = 00 10 00 00 00 00 00 00 00 01 00 00 00 00 00 00 64 | /// ``` 65 | /// 66 | /// So, we see that the second fingerprint ("ba") matches at index 1, while the first fingerprint 67 | /// ("fo") matches at index 9. (Note that these are the indices where the *end* of the fingerprints 68 | /// match.) 69 | /// 70 | /// But there's something obviously missing: what should go in the place of `??` above? In this 71 | /// case, it doesn't matter since the first byte of `D` is zero. But in general, it should clearly 72 | /// be the last byte of the `C` that came from the *previous* block of input. This is the state 73 | /// that we need to keep track of as we process the input block-by-block. We keep the last value of 74 | /// `C`, and when we right-shift the new value of `C`, we also shift in the last byte of the old 75 | /// value. 76 | /// 77 | /// The case of three-byte fingerprints is basically the same story as above, except that we need 78 | /// to keep two values from the previous block of input. One will get right-shifted by one byte and 79 | /// the other will get right-shifted by two bytes. 80 | /// 81 | /// Finally, we get to the purpose of `State`. All it does is to encapsulate the shuffling, 82 | /// shifting, anding, and state-keeping. The user of `State` doesn't need to care how many bytes 83 | /// the fingerprint has: they just pass in a block of input and get back a buch of bitfields 84 | /// (`combined`, in the diagram above). 85 | trait State { 86 | /// Feeds a block of input text into the algorithm, and returns a bitfield with all matching 87 | /// fingerprints. 88 | /// 89 | /// The return value is in the format of `result` in the documentation for `State`. That 90 | /// is, byte `i` of the return value is the set of fingerprints that had a match ending at byte 91 | /// `i` of the input. 92 | fn process_input(&mut self, input: T) -> T; 93 | 94 | /// Forgets all the existing state. 95 | fn reset(&mut self); 96 | 97 | /// The length of a fingerprint, minus one. 98 | /// 99 | /// There is a point that we sort of neglected in the documentation for `State`: thanks to 100 | /// all the shifting, we find the position of the *end* of a fingerprint. But of course, our 101 | /// client is more interested in the start of the match. This function (which should really be 102 | /// an associated constant, but they aren't stable) tells us how much we need to subtract from 103 | /// the end of a fingerprint in order to get to the start of it. 104 | fn offset(&self) -> usize; 105 | } 106 | 107 | // Turns a block of input into two: the first contains only the high nybbles and the second 108 | // contains only the low ones. 109 | #[inline(always)] 110 | fn nybble_input(haystack_block: T) -> (T, T) { 111 | let masklo = T::splat(0xF); 112 | let hlo = haystack_block & masklo; 113 | let hhi = (haystack_block >> 4) & masklo; 114 | 115 | (hhi, hlo) 116 | } 117 | 118 | struct State1 { 119 | mask: Mask, 120 | } 121 | 122 | struct State2 { 123 | /// The mask for the first byte of the fingerprint. 124 | mask0: Mask, 125 | /// The mask for the second byte of the fingerprint. 126 | mask1: Mask, 127 | /// The result of searching for the first byte of the fingerprint, but for the old block of 128 | /// input. 129 | prev0: T, 130 | } 131 | 132 | struct State3 { 133 | mask0: Mask, 134 | mask1: Mask, 135 | mask2: Mask, 136 | /// The result of searching for the first byte of the fingerprint, but for the old block of 137 | /// input. 138 | prev0: T, 139 | /// The result of searching for the second byte of the fingerprint, but for the old block of 140 | /// input. 141 | prev1: T, 142 | } 143 | 144 | impl State1 { 145 | fn new(masks: &Masks) -> State1 { 146 | State1 { 147 | mask: masks.0[0], 148 | } 149 | } 150 | } 151 | 152 | impl State for State1 { 153 | fn reset(&mut self) {} 154 | fn offset(&self) -> usize { 0 } 155 | 156 | // This is the main operation in the case of single-byte fingerprints. I.e., it's the one that 157 | // the README describes in such great detail. In terms of the names that the README uses, we 158 | // take `B` as input and return `C`. `A0` and `A1` are stored in `self.mask`. 159 | #[inline(always)] 160 | fn process_input(&mut self, input: T) -> T { 161 | let (hi, lo) = nybble_input(input); 162 | self.mask.hi.shuffle_bytes(hi) & self.mask.lo.shuffle_bytes(lo) 163 | } 164 | } 165 | 166 | impl State2 { 167 | fn new(masks: &Masks) -> State2 { 168 | State2 { 169 | mask0: masks.0[0], 170 | mask1: masks.0[1], 171 | prev0: T::splat(0xFF), 172 | } 173 | } 174 | } 175 | 176 | impl State for State2 { 177 | fn reset(&mut self) { 178 | self.prev0 = T::splat(0xFF); 179 | } 180 | 181 | fn offset(&self) -> usize { 1 } 182 | 183 | // This is the main operation in the case of two-byte fingerprints. I.e., it's the one that 184 | // the documentation for `State` describes in such great detail. 185 | #[inline(always)] 186 | fn process_input(&mut self, input: T) -> T { 187 | let (hi, lo) = nybble_input(input); 188 | let shuf0 = self.mask0.hi.shuffle_bytes(hi) & self.mask0.lo.shuffle_bytes(lo); 189 | let res0 = T::right_shift_1(self.prev0, shuf0); 190 | let res1 = self.mask1.hi.shuffle_bytes(hi) & self.mask1.lo.shuffle_bytes(lo); 191 | self.prev0 = shuf0; 192 | res0 & res1 193 | } 194 | } 195 | 196 | impl State3 { 197 | fn new(masks: &Masks) -> State3 { 198 | State3 { 199 | mask0: masks.0[0], 200 | mask1: masks.0[1], 201 | mask2: masks.0[2], 202 | prev0: T::splat(0xFF), 203 | prev1: T::splat(0xFF), 204 | } 205 | } 206 | } 207 | 208 | impl State for State3 { 209 | fn reset(&mut self) { 210 | self.prev0 = T::splat(0xFF); 211 | self.prev1 = T::splat(0xFF); 212 | } 213 | fn offset(&self) -> usize { 2 } 214 | 215 | // This is the main operation in the case of three-byte fingerprints. It isn't described in 216 | // much detail anywhere, but hopefully you've got the idea already. 217 | #[inline(always)] 218 | fn process_input(&mut self, input: T) -> T { 219 | let (hi, lo) = nybble_input(input); 220 | let shuf0 = self.mask0.hi.shuffle_bytes(hi) & self.mask0.lo.shuffle_bytes(lo); 221 | let shuf1 = self.mask1.hi.shuffle_bytes(hi) & self.mask1.lo.shuffle_bytes(lo); 222 | let res2 = self.mask2.hi.shuffle_bytes(hi) & self.mask2.lo.shuffle_bytes(lo); 223 | let res1 = T::right_shift_1(self.prev1, shuf1); 224 | let res0 = T::right_shift_2(self.prev0, shuf0); 225 | 226 | self.prev0 = shuf0; 227 | self.prev1 = shuf1; 228 | res0 & res1 & res2 229 | } 230 | } 231 | 232 | impl Teddy { 233 | /// Create a new `Teddy` multi substring matcher. 234 | /// 235 | /// If a `Teddy` matcher could not be created (i.e., `pats` is empty or has 236 | /// an empty substring), then `None` is returned. 237 | pub fn new<'a, I>(pats: I) -> Option> where I: IntoIterator { 238 | let pats: Vec> = pats.into_iter().map(|p| p.to_vec()).collect(); 239 | if pats.is_empty() || pats.iter().any(|p| p.is_empty()) { 240 | None 241 | } else { 242 | let (buckets, masks) = Masks::buckets_and_masks(&pats); 243 | let ac = FullAcAutomaton::new(AcAutomaton::new(pats.clone())); 244 | Some(Teddy { 245 | pats: pats, 246 | buckets: buckets, 247 | masks: masks, 248 | ac: ac, 249 | }) 250 | } 251 | } 252 | 253 | /// Returns all of the substrings matched by this `Teddy`. 254 | pub fn patterns(&self) -> &[Vec] { 255 | &self.pats 256 | } 257 | 258 | /// Returns the approximate size on the heap used by this matcher. 259 | pub fn approximate_size(&self) -> usize { 260 | self.pats.iter().fold(0, |a, b| a + b.len()) + self.ac.heap_bytes() 261 | } 262 | 263 | fn find_loop>(&self, haystack: &[u8], mut state: S) -> Option { 264 | // With a multi-byte fingerprint, we need to include results from previous iterations. To 265 | // avoid special casing at the beginning of the input, it's easiest to start a byte or two 266 | // after the beginning. 267 | let mut pos = state.offset(); 268 | let len = haystack.len(); 269 | 270 | // Do the first iteration of the loop, with an unaligned load. 271 | let hay = unsafe { T::load_unchecked(haystack, pos) }; 272 | let matches = state.process_input(hay); 273 | if matches.is_nonzero() { 274 | let pos = pos - state.offset(); 275 | if let Some(m) = self.verify(haystack, pos, matches) { 276 | return Some(m); 277 | } 278 | } 279 | 280 | // Increment pos by up to block_size, but only as far as the next alignment boundary. 281 | let pos_align = (haystack.as_ptr() as usize + pos) % T::block_size(); 282 | pos = pos + T::block_size() - pos_align; 283 | 284 | // Since we shifted by an amount not necessarily equal to block_size, the state preserved 285 | // in `state` cannot necessarily be used for the next iteration. By resetting the state, we 286 | // allow some false positives in the fingerprint matching step, but only on the first 287 | // iteration through the inner loop that follows. 288 | state.reset(); 289 | 290 | // The main loop (in which the loads are all aligned). The control flow here is a bit 291 | // funky. Logically, we want: 292 | // while pos < end { 293 | // // do something 294 | // if cond { 295 | // // do something else 296 | // } 297 | // } 298 | // Instead, we write: 299 | // 'outer: loop { 300 | // loop { 301 | // if pos >= end { break 'outer; } 302 | // // do something 303 | // if cond { break; } 304 | // } 305 | // // do something else 306 | // } 307 | // This weird double-loop version is faster when `cond` is usually false (and if it isn't 308 | // usually false then you shouldn't be using Teddy anyway). Also, we can unroll the inner 309 | // loop for another little boost. 310 | let end = len.saturating_sub(2 * T::block_size() - 1); 311 | let mut matches: T; 312 | 'outer: loop { 313 | 'inner: loop { 314 | if pos >= end { break 'outer; } 315 | 316 | let hay = unsafe { *(haystack.get_unchecked(pos) as *const u8 as *const T) }; 317 | matches = state.process_input(hay); 318 | if matches.is_nonzero() { 319 | break 'inner; 320 | } 321 | pos += T::block_size(); 322 | 323 | let hay = unsafe { *(haystack.get_unchecked(pos) as *const u8 as *const T) }; 324 | matches = state.process_input(hay); 325 | if matches.is_nonzero() { 326 | break 'inner; 327 | } 328 | pos += T::block_size(); 329 | } 330 | 331 | // If we got here, it means that a fingerprint matched and we need to verify it. 332 | let start_pos = pos - state.offset(); 333 | if let Some(m) = self.verify(haystack, start_pos, matches) { 334 | return Some(m); 335 | } 336 | pos += T::block_size(); 337 | } 338 | 339 | // Do a slow search through the last part of the haystack, which was not big enough to do 340 | // SIMD on. 341 | self.slow(haystack, pos - state.offset()) 342 | } 343 | 344 | /// Searches `haystack` for the substrings in this `Teddy`. If a match was 345 | /// found, then it is returned. Otherwise, `None` is returned. 346 | pub fn find(&self, haystack: &[u8]) -> Option { 347 | // If our haystack is too small, fall back to Aho-Corasick. 348 | if haystack.is_empty() || haystack.len() < 2 * T::block_size() { 349 | return self.slow(haystack, 0); 350 | } 351 | 352 | match self.masks.len() { 353 | 1 => { self.find_loop(haystack, State1::new(&self.masks)) }, 354 | 2 => { self.find_loop(haystack, State2::new(&self.masks)) }, 355 | 3 => { self.find_loop(haystack, State3::new(&self.masks)) }, 356 | _ => unreachable!(), 357 | } 358 | } 359 | 360 | /// Runs the verification procedure on `res` (i.e., `C` from the README). A non-zero byte in 361 | /// position `i` of `res` means that a fingerprint matched `haystack` beginning at offset `pos 362 | /// + i`. 363 | /// 364 | /// If a match exists, returns the first one. 365 | #[inline(always)] 366 | fn verify(&self, haystack: &[u8], pos: usize, res: T) -> Option { 367 | let mut bitfield = res.nonzero_bytes(); 368 | 369 | while bitfield != 0 { 370 | // The next offset, relative to pos, where some fingerprint matched. 371 | let byte_pos = bitfield.trailing_zeros(); 372 | bitfield &= !(1 << byte_pos); 373 | 374 | // Offset relative to the beginning of the haystack. 375 | let start = pos + byte_pos as usize; 376 | 377 | // The bitfield telling us which patterns had fingerprints that match at this starting 378 | // position. 379 | let mut patterns = res.extract(byte_pos); 380 | while patterns != 0 { 381 | let bucket = patterns.trailing_zeros() as usize; 382 | patterns &= !(1 << bucket); 383 | 384 | // Actual substring search verification. 385 | if let Some(m) = self.verify_bucket(haystack, bucket, start) { 386 | return Some(m); 387 | } 388 | } 389 | } 390 | 391 | None 392 | } 393 | 394 | /// Verifies whether any substring in the given bucket matches the haystack 395 | /// at the given starting position. 396 | #[inline(always)] 397 | fn verify_bucket( 398 | &self, 399 | haystack: &[u8], 400 | bucket: usize, 401 | start: usize, 402 | ) -> Option { 403 | // This cycles through the patterns in the bucket in the order that 404 | // the patterns were given. Therefore, we guarantee leftmost-first 405 | // semantics. 406 | for &pati in &self.buckets[bucket] { 407 | let pat = &*self.pats[pati]; 408 | if start + pat.len() > haystack.len() { 409 | continue; 410 | } 411 | if pat == &haystack[start..start + pat.len()] { 412 | return Some(Match { 413 | pat: pati, 414 | start: start, 415 | end: start + pat.len(), 416 | }); 417 | } 418 | } 419 | None 420 | } 421 | 422 | /// Slow substring search through all patterns in this matcher. 423 | /// 424 | /// This is used when we don't have enough bytes in the haystack for our 425 | /// block based approach. 426 | fn slow(&self, haystack: &[u8], pos: usize) -> Option { 427 | // Aho-Corasick finds matches in order of the end position, but we want them in order of 428 | // the start position. Going through all matches and finding the one that starts earliest 429 | // is not the most efficient way to solve this, but it isn't too wasteful since we only do 430 | // it to small haystacks. 431 | self.ac.find_overlapping(&haystack[pos..]) 432 | .min_by_key(|m| m.start) 433 | .map(|m| Match { 434 | pat: m.pati, 435 | start: m.start + pos, 436 | end: m.end + pos, 437 | }) 438 | } 439 | } 440 | 441 | #[cfg(test)] 442 | mod tests { 443 | use super::Teddy; 444 | use x86::teddy_simd::TeddySIMD; 445 | use simd::u8x16; 446 | use std::iter::repeat; 447 | use quickcheck::TestResult; 448 | 449 | #[cfg(target_feature="avx2")] 450 | use simd::x86::avx::u8x32; 451 | 452 | fn one_pattern_inner(needle: &str) { 453 | let len = T::block_size() * 4; 454 | let ted: Teddy = Teddy::new(vec![needle.as_bytes()]).unwrap(); 455 | 456 | // Allocate a string just once. This ensures that its allocation has the same alignment 457 | // throughout these tests. 458 | let mut hay = Vec::with_capacity(T::block_size() * 4); 459 | for needle_pos in 0..(len - needle.len() + 1) { 460 | hay.clear(); 461 | 462 | // Embed the needle at offset `needle_pos` in a string of x's. 463 | hay.extend(repeat('x' as u8).take(needle_pos)); 464 | hay.extend(needle.as_bytes().iter().cloned()); 465 | let len_left = len - hay.len(); 466 | hay.extend(repeat('x' as u8).take(len_left)); 467 | 468 | // Try starting from different offsets in the string. Since the fingerprint matching 469 | // depends on memory alignment, this tests out different code paths. 470 | for offset in 0..T::block_size() { 471 | assert_eq!(ted.find(&hay[offset..]), ted.slow(&hay[offset..], 0)); 472 | } 473 | } 474 | } 475 | 476 | #[test] 477 | fn one_pattern_128() { 478 | one_pattern_inner::("abc"); 479 | one_pattern_inner::("ab"); 480 | one_pattern_inner::("a"); 481 | } 482 | 483 | #[cfg(target_feature="avx2")] 484 | #[test] 485 | fn one_pattern_256() { 486 | one_pattern_inner::("abc"); 487 | one_pattern_inner::("ab"); 488 | one_pattern_inner::("a"); 489 | } 490 | 491 | fn fast_equal_slow_inner( 492 | pats: Vec>, 493 | haystack_prefix: Vec, 494 | haystack_suffix: Vec) 495 | -> TestResult { 496 | if pats.is_empty() { 497 | return TestResult::discard(); 498 | } 499 | 500 | // Hide one of the patterns in the haystack, so there is something to find. 501 | let mut haystack = haystack_prefix; 502 | haystack.extend_from_slice(&pats[0]); 503 | haystack.extend_from_slice(&haystack_suffix); 504 | 505 | if let Some(ted) = Teddy::::new(pats.iter().map(|x| &x[..])) { 506 | let fast = ted.find(&haystack).unwrap(); 507 | let slow = ted.slow(&haystack, 0).unwrap(); 508 | 509 | TestResult::from_bool(fast.start == slow.start) 510 | } else { 511 | TestResult::discard() 512 | } 513 | } 514 | 515 | quickcheck! { 516 | fn fast_equal_slow_128(pats: Vec>, hay_pref: Vec, hay_suf: Vec) -> TestResult { 517 | fast_equal_slow_inner::(pats, hay_pref, hay_suf) 518 | } 519 | } 520 | 521 | #[cfg(target_feature="avx2")] 522 | quickcheck! { 523 | fn fast_equal_slow_256(pats: Vec>, hay_pref: Vec, hay_suf: Vec) -> TestResult { 524 | fast_equal_slow_inner::(pats, hay_pref, hay_suf) 525 | } 526 | } 527 | } 528 | 529 | -------------------------------------------------------------------------------- /src/x86/mask.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | use std::collections::BTreeMap; 3 | use std::fmt::{Debug, Error as FmtError, Formatter}; 4 | use std::usize; 5 | use x86::teddy_simd::TeddySIMD; 6 | 7 | /// A list of masks. This has length equal to the length of the fingerprint. 8 | /// The length of the fingerprint is always `max(3, len(smallest_substring))`. 9 | #[derive(Debug, Clone)] 10 | pub struct Masks(pub Vec>); 11 | 12 | /// A single mask. 13 | #[derive(Debug, Clone, Copy)] 14 | pub struct Mask { 15 | /// Bitsets for the low nybbles in a fingerprint. 16 | pub lo: T, 17 | /// Bitsets for the high nybbles in a fingerprint. 18 | pub hi: T, 19 | } 20 | 21 | /// A bitset representing a set of nybbles. 22 | #[derive(Clone, Copy)] 23 | struct NybbleSet(u16); 24 | 25 | impl NybbleSet { 26 | /// The number of nybbles in this set. 27 | fn len(self) -> usize { 28 | self.0.count_ones() as usize 29 | } 30 | 31 | /// The union between this set and `other`. 32 | fn union(self, other: NybbleSet) -> NybbleSet { 33 | NybbleSet(self.0 | other.0) 34 | } 35 | 36 | /// The intersection between `self` and `other`. 37 | fn intersection(self, other: NybbleSet) -> NybbleSet { 38 | NybbleSet(self.0 & other.0) 39 | } 40 | } 41 | 42 | impl Debug for NybbleSet { 43 | fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { 44 | write!(f, "{:016b}", self.0) 45 | } 46 | } 47 | 48 | /// A set of bytes. 49 | /// 50 | /// This is not just any set of bytes however; it must be a product set: a set of the form `{b: 51 | /// hi(b) in S and lo(b) in T}`, where `S` and `T` are sets of nybbles, and `hi(b)` and `lo(b)` are 52 | /// the high and low nybbles of `b`. The reason for considering byte sets of this form is that 53 | /// these are exactly the sorts of bytesets that can be efficiently searched for using the `PSHUFB` 54 | /// instruction. 55 | #[derive(Clone, Copy)] 56 | struct ByteSet { 57 | hi: NybbleSet, 58 | lo: NybbleSet, 59 | } 60 | 61 | impl ByteSet { 62 | /// Creates a new, empty, `ByteSet`. 63 | fn new() -> ByteSet { 64 | ByteSet { 65 | hi: NybbleSet(0), 66 | lo: NybbleSet(0), 67 | } 68 | } 69 | 70 | /// Adds a single byte to this `ByteSet`. 71 | fn add_byte(&mut self, b: u8) { 72 | let hi = b >> 4; 73 | let lo = b & 0x0F; 74 | 75 | self.hi.0 |= 1 << hi; 76 | self.lo.0 |= 1 << lo; 77 | } 78 | 79 | /// The number of bytes in this set. 80 | fn len(self) -> usize { 81 | self.hi.len() * self.lo.len() 82 | } 83 | 84 | /// The smallest `ByteSet` that contains both `self` and `other`. Because `ByteSet` can only 85 | /// represent product sets, this may be larger than the union of `self` and `other`. 86 | fn cover(self, other: ByteSet) -> ByteSet { 87 | ByteSet { 88 | hi: self.hi.union(other.hi), 89 | lo: self.lo.union(other.lo), 90 | } 91 | } 92 | 93 | /// The intersection between `self` and `other`. 94 | fn intersection(self, other: ByteSet) -> ByteSet { 95 | ByteSet { 96 | hi: self.hi.intersection(other.hi), 97 | lo: self.lo.intersection(other.lo), 98 | } 99 | } 100 | } 101 | 102 | impl Debug for ByteSet { 103 | fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { 104 | write!(f, "{:?}/{:?}", self.hi, self.lo) 105 | } 106 | } 107 | 108 | /// A fingerprint is a sequence of 1 to 3 `ByteSets`. A string of bytes matches a fingerprint if 109 | /// its first byte is contained in the first `ByteSet`, its second byte is contained in the second 110 | /// `ByteSet`, and so on. 111 | #[derive(Debug, Clone)] 112 | struct Fingerprint(Vec); 113 | 114 | impl Fingerprint { 115 | /// Creates an empty fingerprint of length `n`, which must be between 1 and 3. 116 | fn new(n: usize) -> Fingerprint { 117 | debug_assert!(1 <= n && n <= 3); 118 | Fingerprint(vec![ByteSet::new(); n]) 119 | } 120 | 121 | /// Adds a string to this fingerprint. The length of the string must be at least the length of 122 | /// this fingerprint. 123 | fn add_string(&mut self, pat: &[u8]) { 124 | debug_assert!(pat.len() >= self.0.len()); 125 | for (i, b) in self.0.iter_mut().enumerate() { 126 | b.add_byte(pat[i]); 127 | } 128 | } 129 | 130 | /// The number of distinct strings of length `self.0.len()` that match this fingerprint. 131 | fn len(&self) -> usize { 132 | self.0.iter().map(|set| set.len()).product() 133 | } 134 | 135 | /// The number of sequences that belong to both `self` and `other`. 136 | /// 137 | /// If we had implemented `Fingerprint::intersection`, this would be equivalent to (but faster 138 | /// than) `self.intersection(other).len()`. 139 | fn intersection_size(&self, other: &Fingerprint) -> usize { 140 | debug_assert!(self.0.len() == other.0.len()); 141 | self.0.iter() 142 | .zip(other.0.iter()) 143 | .map(|(bs1, bs2)| bs1.intersection(*bs2).len()) 144 | .product() 145 | } 146 | 147 | /// The number of sequences that belong to the smallest `Fingerprint` containing both 148 | /// `self` and `other`. 149 | /// 150 | /// This is equivalent to `self.include(other); self.len()`, but it doesn't modify `self`. 151 | fn cover_size(&self, other: &Fingerprint) -> usize { 152 | debug_assert!(self.0.len() == other.0.len()); 153 | self.0.iter() 154 | .zip(other.0.iter()) 155 | .map(|(bs1, bs2)| bs1.cover(*bs2).len()) 156 | .product() 157 | } 158 | 159 | /// Modifies this `Fingerprint` in place so that it contains `other`. The result will be the 160 | /// smallest fingerprint that contains both `other` and (the old value of) `self`. 161 | fn include(&mut self, other: &Fingerprint) { 162 | debug_assert!(self.0.len() == other.0.len()); 163 | for (bs1, bs2) in self.0.iter_mut().zip(other.0.iter()) { 164 | *bs1 = bs1.cover(*bs2); 165 | } 166 | } 167 | } 168 | 169 | /// A `Bucket` is a collection of strings along with a fingerprint that matches all of them. 170 | #[derive(Debug, Clone)] 171 | struct Bucket { 172 | /// The collection of strings in this bucket. We store them as indices into some external 173 | /// collection of strings. 174 | pats: Vec, 175 | /// The fingerprint matching all of the strings. 176 | fing: Fingerprint, 177 | } 178 | 179 | impl Bucket { 180 | fn new(fing_len: usize) -> Bucket { 181 | Bucket { 182 | pats: Vec::new(), 183 | fing: Fingerprint::new(fing_len), 184 | } 185 | 186 | } 187 | 188 | /// Adds a single string to this bucket. 189 | /// 190 | /// `pati` is the index of the string (in the external collection containing all of the 191 | /// strings). 192 | /// `bytes` is the string. 193 | fn add_string(&mut self, pati: usize, bytes: &[u8]) { 194 | self.pats.push(pati); 195 | self.fing.add_string(bytes); 196 | } 197 | 198 | /// Calculate a penalty based on how much we don't want to merge these two buckets. The most 199 | /// important field in the penalty is how many un-needed strings are contained in the new 200 | /// bucket. Given a tie on that score, we break it by preferring buckets that match a smaller 201 | /// total number of strings. 202 | fn merge_penalty(&self, other: &Bucket) -> (usize, usize) { 203 | let old_size = self.fing.len() + other.fing.len() - self.fing.intersection_size(&other.fing); 204 | let new_size = self.fing.cover_size(&other.fing); 205 | (new_size.checked_sub(old_size).unwrap(), new_size) 206 | } 207 | 208 | /// Adds all strings in `other` to this bucket. 209 | fn merge(&mut self, other: Bucket) { 210 | self.pats.extend_from_slice(&other.pats); 211 | self.fing.include(&other.fing); 212 | } 213 | } 214 | 215 | /// Compares all pairs of buckets and merges the pair that results in the smallest increase in 216 | /// fingerprint size. If there is a tie, break it by merging the pair that will result in the 217 | /// smallest total fingerprint size. 218 | /// 219 | /// Note that this function is O(N^2), where N is the number of buckets. Since this gets called 220 | /// O(N) times, the total time is O(N^3). Fortunately, N is usually not very large. Nevertheless, 221 | /// it might be nice to make it faster... 222 | fn merge_one_bucket(buckets: &mut Vec) { 223 | let mut best_pair = (usize::MAX, usize::MAX); 224 | let mut best_penalty = (usize::MAX, usize::MAX); 225 | 226 | for (i, b1) in buckets.iter().enumerate() { 227 | for (j, b2) in buckets[(i+1)..].iter().enumerate() { 228 | let penalty = b1.merge_penalty(b2); 229 | if penalty <= best_penalty { 230 | best_penalty = penalty; 231 | best_pair = (i, i + 1 + j); 232 | } 233 | } 234 | } 235 | 236 | let (i, j) = best_pair; 237 | debug_assert!(i < j && j < buckets.len()); 238 | let b2 = buckets.swap_remove(j); 239 | buckets[i].merge(b2); 240 | } 241 | 242 | /// Given a set of strings and a fingerprint length (between 1 and 3), divide the strings into up 243 | /// to 8 buckets in such a way that the fingerprints for the buckets are as small as possible. 244 | fn gather_buckets(pats: &[Vec], fing_len: usize) -> Vec> { 245 | // Start by putting all the patterns with the exact same fingerprint into a single bucket. 246 | let mut buckets = BTreeMap::new(); 247 | for (pati, pat) in pats.iter().enumerate() { 248 | buckets.entry(&pat[0..fing_len]) 249 | .or_insert_with(|| Bucket::new(fing_len)) 250 | .add_string(pati, pat); 251 | } 252 | 253 | // Now continue merging the buckets as best we can until there are at most 8. 254 | let mut buckets: Vec = buckets.into_iter().map(|(_, bucket)| bucket).collect(); 255 | while buckets.len() > 8 { 256 | merge_one_bucket(&mut buckets); 257 | } 258 | 259 | buckets.into_iter() 260 | .map(|bucket| bucket.pats) 261 | .collect() 262 | } 263 | 264 | impl Masks { 265 | /// Given a set of strings, returns a set of buckets and a corresponding set of masks. 266 | /// 267 | /// # Panics 268 | /// if the set of strings is empty, or if it contains any empty strings. 269 | pub fn buckets_and_masks(pats: &[Vec]) -> (Vec>, Masks) { 270 | let min_len = pats.iter().map(|p| p.len()).min().unwrap_or(0); 271 | // Don't allow any empty patterns and require that we have at 272 | // least one pattern. 273 | debug_assert!(min_len >= 1); 274 | // Pick the largest mask possible, but no larger than 3. 275 | let nmasks = cmp::min(3, min_len); 276 | let buckets = gather_buckets(pats, nmasks); 277 | debug_assert!(buckets.len() <= cmp::min(8, pats.len())); 278 | 279 | let mut masks = Masks(vec![Mask::new(); nmasks]); 280 | for (bucki, bucket) in buckets.iter().enumerate() { 281 | for &pati in bucket { 282 | // The cast is ok because bucki is at most 7. 283 | masks.add(bucki as u8, &pats[pati]); 284 | } 285 | } 286 | 287 | (buckets, masks) 288 | } 289 | 290 | /// Returns the number of masks. 291 | pub fn len(&self) -> usize { 292 | self.0.len() 293 | } 294 | 295 | /// Adds the given pattern to the given bucket. The bucket should be a 296 | /// power of `2 <= 2^7`. 297 | pub fn add(&mut self, bucket: u8, pat: &[u8]) { 298 | for (i, mask) in self.0.iter_mut().enumerate() { 299 | mask.add(bucket, pat[i]); 300 | } 301 | } 302 | } 303 | 304 | impl Mask { 305 | /// Create a new mask with no members. 306 | fn new() -> Mask { 307 | Mask { 308 | lo: T::splat(0), 309 | hi: T::splat(0), 310 | } 311 | } 312 | 313 | /// Adds the given byte to the given bucket. 314 | fn add(&mut self, bucket: u8, byte: u8) { 315 | // Split our byte into two nybbles, and add each nybble to our 316 | // mask. 317 | let byte_lo = (byte & 0xF) as u32; 318 | let byte_hi = (byte >> 4) as u32; 319 | 320 | // Our mask is repeated across 16 byte lanes. (TODO: explain why) 321 | let lo = self.lo.extract(byte_lo) | ((1 << bucket) as u8); 322 | let hi = self.hi.extract(byte_hi) | ((1 << bucket) as u8); 323 | 324 | for lane in 0..(T::block_size() as u32 / 16) { 325 | self.lo = self.lo.replace(byte_lo + 16 * lane, lo); 326 | self.hi = self.hi.replace(byte_hi + 16 * lane, hi); 327 | } 328 | } 329 | } 330 | 331 | 332 | #[cfg(test)] 333 | mod tests { 334 | use super::gather_buckets; 335 | use std::collections::BTreeSet; 336 | 337 | macro_rules! merge_number { 338 | ($name:ident, $strs:expr, $fing_len:expr, $expected:expr) => { 339 | #[test] 340 | fn $name() { 341 | let pats: Vec> = $strs.iter().map(|s| s.as_bytes().to_vec()).collect(); 342 | let buckets = gather_buckets(&pats, $fing_len); 343 | assert_eq!(buckets.len(), $expected); 344 | } 345 | } 346 | } 347 | 348 | macro_rules! merge { 349 | ($name:ident, $strs:expr, $fing_len:expr, $merged:expr) => { 350 | #[test] 351 | fn $name() { 352 | let pats: Vec> = $strs.iter().map(|s| s.as_bytes().to_vec()).collect(); 353 | let buckets = gather_buckets(&pats, $fing_len); 354 | let merged: BTreeSet> = 355 | $merged.into_iter().map(|set| set.iter().cloned().collect()).collect(); 356 | for b in &buckets { 357 | if b.len() > 1 { 358 | println!("bucket {:?}", b); 359 | assert!(merged.contains(&b.iter().cloned().collect())); 360 | } 361 | } 362 | } 363 | } 364 | } 365 | 366 | // Test the bit where we merge together strings with identical prefixes into the same bucket. 367 | merge_number!(merge_equal_fings_1a, ["abc", "ae", "az"], 1, 1); 368 | merge_number!(merge_equal_fings_1b, ["ba", "abc", "ae", "az", "ba", "bc"], 1, 2); 369 | merge_number!(merge_equal_fings_2a, ["ba", "abc", "ab", "az", "ba", "bc"], 2, 4); 370 | merge_number!(merge_equal_fings_2b, ["abc", "abdef", "abcde"], 2, 1); 371 | merge_number!(merge_equal_fings_3a, ["abc", "abdef", "abcde"], 3, 2); 372 | merge_number!(merge_equal_fings_3b, ["abc", "abczyx", "abcde"], 3, 1); 373 | 374 | merge!(merge_lossless_a, ["she", "She", "abc", "def", "ghi", "jkl", "mno", "pqr", "stu"], 3, [&[0, 1]]); 375 | merge!(merge_lossless_b, ["she", "the", "abc", "def", "ghi", "jkl", "mno", "pqr", "stu"], 3, [&[0, 1]]); 376 | 377 | // We prefer to merge 4 fingerprints into one rather than add any false positives. 378 | merge!(merge_lossless_c, 379 | ["she", "She", "sHe", "SHe", "abc", "def", "ghi", "jkl", "mno", "pqr", "stu"], 380 | 3, 381 | [&[0, 1, 2, 3]]); 382 | 383 | // Given several choices, we will prefer the one that keeps the max bucket size small. (The 384 | // fact that we merge [0, 2] and [1, 3] instead of [0, 1] and [2, 3] is not particularly 385 | // intentional.) 386 | merge!(merge_lossless_d, 387 | ["she", "She", "sHe", "SHe", "bla", "Bla", "ghi", "jkl", "mno", "pqr", "stu"], 388 | 3, 389 | [&[0, 2], &[1, 3], &[4, 5]]); 390 | 391 | // Merging "she" and "tho" introduces "the" and "sho" as false positives, but that's better 392 | // than the alternatives. 393 | merge!(merge_lossy_a, ["she", "tho", "abc", "def", "ghi", "jkl", "mno", "pqr", "stu"], 3, [&[0, 1]]); 394 | merge!(merge_lossy_b, ["she", "SHe", "abc", "def", "ghi", "jkl", "mno", "pqr", "stu"], 3, [&[0, 1]]); 395 | // Merging "she" and "The" introduces "the" and "She" as false positives. 396 | merge!(merge_lossy_c, ["she", "The", "abc", "def", "ghi", "jkl", "mno", "pqr", "stu"], 3, [&[0, 1]]); 397 | merge!(merge_lossy_d, ["she", "THe", "abc", "ABc", "ghi", "jkl", "mno", "pqr", "stu"], 3, [&[2, 3]]); 398 | } 399 | 400 | -------------------------------------------------------------------------------- /src/x86/mod.rs: -------------------------------------------------------------------------------- 1 | mod core; 2 | mod mask; 3 | mod teddy_simd; 4 | 5 | use self::core::Teddy as TeddyInner; 6 | use simd; 7 | use Match; 8 | 9 | #[cfg(target_feature="avx2")] 10 | #[derive(Clone, Debug)] 11 | /// A SIMD accelerated multi substring searcher. 12 | pub struct Teddy(TeddyInner); 13 | #[cfg(all(not(target_feature="avx2"), target_feature="ssse3"))] 14 | #[derive(Clone, Debug)] 15 | /// A SIMD accelerated multi substring searcher. 16 | pub struct Teddy(TeddyInner); 17 | 18 | impl Teddy { 19 | /// Create a new `Teddy` multi substring matcher. 20 | /// 21 | /// If a `Teddy` matcher could not be created (e.g., `pats` is empty or contains an empty 22 | /// pattern), then `None` is returned. 23 | /// 24 | /// # Warning 25 | /// 26 | /// If `teddy` was built without SIMD support, then this method will *always* return `None`. 27 | /// See the README for more information on how to compile `teddy` with SIMD support. 28 | pub fn new<'a, I>(pats: I) -> Option where I: IntoIterator { 29 | TeddyInner::new(pats).map(|t| Teddy(t)) 30 | } 31 | 32 | /// Returns all of the substrings matched by this `Teddy`. 33 | pub fn patterns(&self) -> &[Vec] { 34 | self.0.patterns() 35 | } 36 | 37 | /// Returns the approximate size on the heap used by this matcher. 38 | pub fn approximate_size(&self) -> usize { 39 | self.0.approximate_size() 40 | } 41 | 42 | /// Searches `haystack` for the substrings in this `Teddy`. Returns the first match if one 43 | /// exists, and otherwise `None`. 44 | pub fn find(&self, haystack: &[u8]) -> Option { 45 | self.0.find(haystack) 46 | } 47 | 48 | /// Were we compiled with SIMD support? If not, `Teddy::new` will always just return `None`. 49 | /// 50 | /// See the README for more information on how to compile `teddy` with SIMD support. 51 | pub fn is_accelerated() -> bool { 52 | true 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/x86/teddy_simd.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Joe Neeman. 2 | // 3 | // Licensed under the Apache License, Version 2.0 or the MIT license 5 | // , at your 6 | // option. This file may not be copied, modified, or distributed 7 | // except according to those terms. 8 | 9 | //! The purpose of this module is to allow us to write the core part of Teddy's algorithm in a way 10 | //! that is generic over the SIMD width. Unfortunately, the `simd` crate is not organized to 11 | //! facilitate this: the traits it defines are based on which instruction set the required 12 | //! operations belong to, not on what the operations actually do. This module therefore does a 13 | //! minimal version of this sort of logical grouping. It may be worth expanding this module into an 14 | //! alternative to the `simd` crate. 15 | 16 | // Because this file conditionally compiles many things, some of the imports might only be used in 17 | // certain configurations. 18 | #![allow(unused_imports)] 19 | 20 | use simd::{bool8ix16, u8x16}; 21 | use simd::x86::sse2::{u64x2, Sse2Bool8ix16}; 22 | use simd::x86::ssse3::Ssse3U8x16; 23 | use std::fmt::Debug; 24 | use std::mem::transmute; 25 | use std::ops::{BitAnd, Shr}; 26 | use std::ptr; 27 | 28 | #[cfg(target_feature="avx2")] 29 | use simd::x86::avx::{bool8ix32, i8x32, u8x32, u64x4}; 30 | 31 | // Here are some operations that we need but are not (currently) exposed by `simd`. 32 | extern "platform-intrinsic" { 33 | fn simd_shuffle16(x: T, y: T, idx: [u32; 16]) -> U; 34 | } 35 | 36 | #[cfg(target_feature="avx2")] 37 | extern "platform-intrinsic" { 38 | #[cfg(not(feature="asm"))] 39 | fn simd_shuffle32(x: T, y: T, idx: [u32; 32]) -> U; 40 | fn x86_mm256_shuffle_epi8(x: i8x32, y: i8x32) -> i8x32; 41 | fn x86_mm256_movemask_epi8(x: i8x32) -> i32; 42 | } 43 | 44 | /// This trait contains all the SIMD operations necessary for implementing the Teddy algorithm. 45 | pub trait TeddySIMD: BitAnd + Clone + Copy + Debug + Shr + Sized { 46 | /// How many bytes fit into this SIMD type? 47 | /// 48 | /// This should probably be an associated const, but those aren't stable. 49 | fn block_size() -> usize; 50 | 51 | fn splat(x: u8) -> Self; 52 | fn extract(self, idx: u32) -> u8; 53 | fn replace(self, idx: u32, elem: u8) -> Self; 54 | fn shuffle_bytes(self, indices: Self) -> Self; 55 | fn is_nonzero(self) -> bool; 56 | 57 | /// Puts `left` on the left, `right` on the right, then shifts the whole thing by one byte to 58 | /// the right and returns the right half (so that the right-most byte of `left` will become the 59 | /// left-most byte of the answer). 60 | fn right_shift_1(left: Self, right: Self) -> Self; 61 | 62 | /// Same as `right_shift_1`, but shifts by 2 bytes. 63 | fn right_shift_2(left: Self, right: Self) -> Self; 64 | 65 | /// Returns a bitfield indicating which bytes in this vector are non-zero. 66 | fn nonzero_bytes(self) -> u32; 67 | 68 | /// Creates a new SIMD vector from the elements in `slice` starting at `offset`. `slice` must 69 | /// have at least the number of elements required to fill a SIMD vector. 70 | unsafe fn load_unchecked(slice: &[u8], offset: usize) -> Self; 71 | } 72 | 73 | impl TeddySIMD for u8x16 { 74 | #[inline] 75 | fn block_size() -> usize { 16 } 76 | #[inline] 77 | fn splat(x: u8) -> Self { u8x16::splat(x) } 78 | #[inline] 79 | fn extract(self, idx: u32) -> u8 { u8x16::extract(self, idx) } 80 | #[inline] 81 | fn replace(self, idx: u32, elem: u8) -> Self { u8x16::replace(self, idx, elem) } 82 | #[inline] 83 | fn is_nonzero(self) -> bool { bool8ix16::any(u8x16::ne(self, u8x16::splat(0))) } 84 | #[inline] 85 | fn nonzero_bytes(self) -> u32 { Sse2Bool8ix16::move_mask(u8x16::ne(self, u8x16::splat(0))) } 86 | 87 | #[inline] 88 | fn right_shift_1(left: Self, right: Self) -> Self { 89 | unsafe { simd_shuffle16(left, right, [15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]) } 90 | } 91 | 92 | #[inline] 93 | fn right_shift_2(left: Self, right: Self) -> Self { 94 | unsafe { simd_shuffle16(left, right, [14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]) } 95 | } 96 | 97 | #[inline] 98 | fn shuffle_bytes(self, indices: Self) -> Self { 99 | Ssse3U8x16::shuffle_bytes(self, indices) 100 | } 101 | 102 | #[inline] 103 | unsafe fn load_unchecked(slice: &[u8], offset: usize) -> u8x16 { 104 | // I'm not sure if this is the best way to write an unaligned load, but it does seem to 105 | // compile down to a single `movdqu` instruction. 106 | let mut x = u8x16::splat(0); 107 | ptr::copy_nonoverlapping( 108 | slice.get_unchecked(offset), 109 | &mut x as *mut u8x16 as *mut u8, 110 | 16); 111 | x 112 | } 113 | } 114 | 115 | #[cfg(target_feature="avx2")] 116 | impl TeddySIMD for u8x32 { 117 | #[inline] 118 | fn block_size() -> usize { 32 } 119 | #[inline] 120 | fn splat(x: u8) -> Self { u8x32::splat(x) } 121 | #[inline] 122 | fn extract(self, idx: u32) -> u8 { u8x32::extract(self, idx) } 123 | #[inline] 124 | fn replace(self, idx: u32, elem: u8) -> Self { u8x32::replace(self, idx, elem) } 125 | #[inline] 126 | fn is_nonzero(self) -> bool { bool8ix32::any(u8x32::ne(self, u8x32::splat(0))) } 127 | #[inline] 128 | fn nonzero_bytes(self) -> u32 { 129 | let nonzero = u8x32::ne(self, u8x32::splat(0)); 130 | unsafe { transmute(x86_mm256_movemask_epi8(transmute(nonzero))) } 131 | } 132 | 133 | #[cfg(not(feature="asm"))] 134 | #[inline] 135 | fn right_shift_1(left: Self, right: Self) -> Self { 136 | unsafe { simd_shuffle32(left, right, [31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57 ,58, 59, 60, 61, 62]) } 137 | } 138 | 139 | #[cfg(not(feature="asm"))] 140 | #[inline] 141 | fn right_shift_2(left: Self, right: Self) -> Self { 142 | unsafe { simd_shuffle32(left, right, [30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57 ,58, 59, 60, 61]) } 143 | } 144 | 145 | #[cfg(feature="asm")] 146 | #[inline] 147 | fn right_shift_1(left: Self, right: Self) -> Self { 148 | // It would be nicer just to use an intrinsic for this shuffle, but LLVM generates four 149 | // instructions for the shuffle we need, whereas the optimal sequence only takes two. 150 | unsafe { 151 | let ret: Self; 152 | // 33 = 0x21, so the `vperm2i128` instruction concatenates the least significant 16 153 | // bytes of `right` with the most significant 16 bytes of `left`. That is, 154 | // 155 | // left: 31L .. 16L | 15L .. 0L 156 | // right: 31R .. 16R | 15R .. 0R 157 | // 158 | // gives 159 | // 160 | // ret: 15R .. 0R | 31L .. 16L 161 | // 162 | // Then we shift both lanes of `ret` to the right by 15, while shifting in `right`. 163 | // This gives 164 | // 165 | // ret: 30R ... 16R 15R | 14R .. 0R 31L 166 | // 167 | // Overall, we managed to shift `right` one byte to the left, while shifting in `left` 168 | // from the right. But then why is this method called `right_shift_1`?! It's because 169 | // the pictures above are with the most significant bytes on the left, but in most of 170 | // this crate (for example, in the crate documentation describing the Teddy algorithm) 171 | // we think of text as running from left to right. Since x86 is little endian, the left 172 | // shift above is a right shift from the text perspective. 173 | asm!("vperm2i128 $$33, $2, $1, $0; vpalignr $$15, $0, $2, $0" 174 | // The output of the code above. That is, `$0` above refers to the variable `ret`. 175 | // The '=' means that we write to it, and the '&' means that we also use it as a 176 | // temporary value. The 'x' means it's a SIMD register. 177 | : "=&x"(ret) 178 | // The inputs (which we do not write to). That is, `$1` above means `left`, while 179 | // `$2` means `right`. 180 | : "x"(left), "x"(right) 181 | ); 182 | ret 183 | } 184 | } 185 | 186 | #[cfg(feature="asm")] 187 | #[inline] 188 | fn right_shift_2(left: Self, right: Self) -> Self { 189 | unsafe { 190 | let ret: Self; 191 | asm!("vperm2i128 $$33, $2, $1, $0; vpalignr $$14, $0, $2, $0" 192 | : "=&x"(ret) 193 | : "x"(left), "x"(right) 194 | ); 195 | ret 196 | } 197 | } 198 | 199 | #[inline] 200 | fn shuffle_bytes(self, indices: Self) -> Self { 201 | unsafe { transmute(x86_mm256_shuffle_epi8(transmute(self), transmute(indices))) } 202 | } 203 | 204 | #[inline] 205 | unsafe fn load_unchecked(slice: &[u8], offset: usize) -> u8x32 { 206 | let mut x = u8x32::splat(0); 207 | ptr::copy_nonoverlapping( 208 | slice.get_unchecked(offset), 209 | &mut x as *mut u8x32 as *mut u8, 210 | 32); 211 | x 212 | } 213 | } 214 | 215 | #[cfg(test)] 216 | mod tests { 217 | use simd::u8x16; 218 | #[cfg(target_feature="avx2")] 219 | use simd::x86::avx::u8x32; 220 | use super::TeddySIMD; 221 | 222 | #[test] 223 | fn right_shifts_128() { 224 | let left: u8x16 = unsafe { TeddySIMD::load_unchecked(b"0123456789ABCDEF", 0) }; 225 | let right: u8x16 = unsafe { TeddySIMD::load_unchecked(b"0123456789abcdef", 0) }; 226 | let result = TeddySIMD::right_shift_1(left, right); 227 | let expected = unsafe { TeddySIMD::load_unchecked(b"F0123456789abcde", 0) }; 228 | assert!(result.eq(expected).all()); 229 | 230 | let result = TeddySIMD::right_shift_2(left, right); 231 | let expected = unsafe { TeddySIMD::load_unchecked(b"EF0123456789abcd", 0) }; 232 | assert!(result.eq(expected).all()); 233 | } 234 | 235 | #[cfg(target_feature="avx2")] 236 | #[test] 237 | fn right_shifts_256() { 238 | let left: u8x32 = unsafe { TeddySIMD::load_unchecked(b"0123456789ABCDEFqwertyuiopasdfgh", 0) }; 239 | let right: u8x32 = unsafe { TeddySIMD::load_unchecked(b"0123456789abcdefQWERTYUIOPASDFGH", 0) }; 240 | let result = TeddySIMD::right_shift_1(left, right); 241 | let expected = unsafe { TeddySIMD::load_unchecked(b"h0123456789abcdefQWERTYUIOPASDFG", 0) }; 242 | assert!(result.eq(expected).all()); 243 | 244 | let result = TeddySIMD::right_shift_2(left, right); 245 | let expected = unsafe { TeddySIMD::load_unchecked(b"gh0123456789abcdefQWERTYUIOPASDF", 0) }; 246 | assert!(result.eq(expected).all()); 247 | } 248 | } 249 | 250 | --------------------------------------------------------------------------------