ropey-1.6.1/.cargo_vcs_info.json0000644000000001360000000000100121770ustar { "git": { "sha1": "d41ee247f2f097c7f9c8e4730b7dd884734f1ee5" }, "path_in_vcs": "" }ropey-1.6.1/.gitignore000064400000000000000000000000721046102023000127560ustar 00000000000000/target/ **/*.rs.bk Cargo.lock perf.data* cachegrind.out* ropey-1.6.1/CHANGELOG.md000064400000000000000000000360361046102023000126100ustar 00000000000000# Changelog ## [Unreleased] ## [1.6.1] - 2023-10-18 - Fixed test code that was incorrect on some platforms / with some configurations. - Minor documentation improvements. ## [1.6.0] - 2023-02-01 ### New features - Added `is_instance()` method, which checks if two ropes are same-memory instances of each other. ### Bug fixes - Ropey would panic when trying to create a `Lines` iterator for an empty rope or rope slice. ## [1.5.1] - 2023-01-01 ### Performance - A much faster `Lines` iterator, thanks to @pascalkuthe (PR #70). ### Bug fixes - Ropey's `Hash` impl was incorrect, due to making incorrect assumptions about the guaranteed behavior of `Hasher`s. This didn't cause any problems with Rust's default hasher, but was incorrect in the general case. - Comparing ropes for equality would panic when the two ropes had chunk boundaries that weren't mutually aligned at char boundaries. - `len_lines()` could give incorrect counts on `RopeSlice`s that split CRLF pairs. - Ropey's internal B-Tree representation could (rarely) end up in a state that violated some invariants. This didn't affect anything in practice, because no code currently depends on the violated invariant. But future code might. ## 1.5.1-alpha - 2022-11-27 - Special early release, mainly to accomodate the Helix project. It is not recommended to use this release outside of Helix. ## [1.5.0] - 2022-05-29 ### New features - Added a `reversed()` method for Ropey's iterators. This is the same as `reverse()` except instead of mutating in-place, it consumes the iterator and returns it reversed. This is more convenient when chaining iterator method calls. - Added a `simd` cargo feature flag. It's enabled by default, but can be disabled to use only scalar code (no simd intrinsics). ### Bug fixes - Fix a theoretical memory safety issue found via running Ropey's tests through miri. Thanks to @Nilstrieb! - Fix (unintentionally) depending on Rust memory layout to achieve precise node sizes in memory. We now use `repr(C)`. ## [1.4.1] - 2022-03-16 ### Bug fixes - Fix a stupid copy/paste typo in the previous line break feature flag implementation that caused the wrong line break code to be used. ## [1.4.0] - 2022-03-16 ### New features - Added `byte_slice()` and `get_byte_slice()` methods to `Rope` and `RopeSlice` to slice by byte index instead of char index. This can allow optimizations in client code in some cases. - Added `cr_lines` and `unicode_lines` feature flags to the crate, to manage what line endings are recognized and tracked. This allows, for example, building Ropey to only recognize line feed as a line break. `unicode_lines` is on by default, and corresponds to the original behavior. - Implemented `std::hash::Hash` for `Rope` and `RopeSlice`. ### Misc - Split `str_utils` module out into a separate crate, `str_indices`. The `str_utils` module still exists, but is now mostly just a re-export of the new crate. ## [1.3.2] - 2021-12-30 ### Bug fixes - Relax the lifetime requirements of various `RopeSlice` methods. They were unintentionally strict. ## [1.3.1] - 2021-06-22 ### Bug fixes - Fix unnecessary rope fragmentation when using `Rope::append()` to append many small ropes together. - Fix contiguous `RopeSlices` occasionally failing to convert to a `&str` with `RopeSlice::as_str()`. ## [1.3.0] - 2021-06-16 ### New features - Added non-panicking versions of all methods on `Rope` and `RopeSlice`. - All iterators can now be reversed, swapping the beheavior of `prev()` and `next()`. ### Bug fixes - The in-memory node size wasn't being computed properly, potentially resulting in unecessary memory fragmentation. ## [1.2.0] - 2020-06-14 ### New features - `Rope` and `RopeSlice` can now convert between char indices and utf16 code unit indices. This useful when interacting with external APIs that use utf16 code units as their text indexing scheme. ### Dependencies - Updated smallvec to minimum version 1.0. ## [1.1.0] - 2019-09-01 ### New features - Iterators can now be created directly to start at any position in the `Rope` or `RopeSlice`. - All iterators can now iterate backwards via a new `prev()` method. - All iterators now implement `Clone` and `Debug` traits. - `Bytes`, `Chars`, and `Lines` iterators now implement `ExactSizeIterator`. ### Changes - The `Chunks` iterator no longer yields empty chunks, for example if the `Rope` or `RopeSlice` it was created from is also empty. ## [1.0.1] - 2019-05-01 ### Other - Converted a lot of unsafe code to safe code, with minimal performance impact. ## [1.0.0] - 2019-01-03 ### New features - Implemented `Eq`, `Ord`, and `PartialOrd` traits for `Rope` and `RopeSlice`. ## [0.9.2] - 2018-10-04 ### Bug fixes - Turns out the previous Line iterator bug fix introduced a different bug. Fixed! ## [0.9.1] - 2018-10-03 ### Bug fixes - The Lines iterator would sometimes emit an extra blank line when created from a small rope slice. - The `write_to()` convenience method could potentially write only part of the rope, without any error indication. ## [0.9.0] - 2018-09-04 ### Performance improvements - Minor performance improvements to a few methods on `Rope` and `RopeSlice`. ### New features - Added `Rope::byte()` for fetching individual bytes by index. - Added more conversion functions for `Rope` and `RopeSlice`, in the form of `From` impls. ### Breaking changes - Removed `Rope::to_string()`, `RopeSlice::from_str()`, `RopeSlice::to_string()`, and `RopeSlice::to_rope()` in favor of `From` impls that do the same thing. ## [0.8.4] - 2018-07-28 ### Performance improvements - Minor across-the-board speedups by using SIMD better. - Significant speedups for Rope::insert()/remove() by being more clever about node info updates. - Further significant speedup to Rope::remove() due to a (performance-only) bug fix. ### Bug fixes - Ropey wouldn't compile on non-x86/64 platforms after the introduction of SSE2 optimizations in v0.8.3. They are now wrapped properly so that Ropey again compiles on other platforms as well. ## [0.8.3] - 2018-07-26 ### Performance improvements - Significant speedups across the board by using SIMD for index conversions. - Loading texts from files or creating Ropes from strings is now significantly faster. ### Memory usage improvements - Memory overhead reduced from 17% to 10% for freshly loaded text. ### Bug fixes - The low-level line -> byte conversion function would sometimes return a byte index in the middle of the line break for multi-byte line break characters. ## [0.8.2] - 2018-07-22 ### Performance improvements - File loading is slightly faster. ### Bug fixes - The low-level line break counting functions could return an incorrect count under certain circumstances. This also affected the higher-level methods in Ropey, although it was somewhat difficult to trigger in practice. ## [0.8.1] - 2018-07-20 ### Performance improvements - Increased Rope::insert() speed by roughly 1.4x for small insertion strings. - Increased Rope::remove() speed by roughly 1.75x. ### Other - General documentation improvements, based on feedback. ## [0.8.0] - 2018-07-14 ### Performance improvements - Building new ropes via RopeBuilder or Rope::from_str() is now about 15% faster. - Slicing is now almost twice as fast. - Fetching lines is now almost twice as fast. - Significant speedups for byte/char -> line index conversion methods. - Significant speedups for line -> byte/char index conversion methods. ### New features - Chunk fetching can now be done by line break index as well as byte/char index. - Some previously-internal utility functions for working with string slices are now part of Ropey's public API. - Added Rope::write_to() convenience function for writing a Rope's data to a writer. ### Breaking changes - Conversion from byte/char indices to line indices has been changed to be more intuitive. It is now equivalent to counting the line endings before the given byte/char index. - Chunk fetching now returns the starting byte/char/line of the chunk, which is generally easier to work with. ## [0.7.1] - 2018-07-09 ### Bug fixes - The chunk fetching methods on slices returned bogus starting char indices. ## [0.7.0] - 2018-07-05 ### Performance improvements - `RopeSlice`s have been given a major speed boost for small slices: for contiguous slices of text in memory, they will simply point at the text without any tree structure. This makes it feasible to use `RopeSlice`s to yield e.g. graphemes or words, even in tight inner loops, while maintaining performance. ### New features - You can now fetch contiguous chunks of text directly from `Rope`s and `RopeSlice`s, via byte or char index. The chunk containing the given byte or char will be returned along with offset information. - Added more index conversion methods. For both `Rope`s and `RopeSlice`s, you can now convert between any of: byte, char, and line indices. - Added a method to directly create `RopeSlice`s from string slices. This isn't terribly useful when using Ropey's standard API's, but it allows for much more efficient implementations of things like custom iterators. - Added a method to directly access a `RopeSlice`s text as a contiguous string slice when possible. This is useful for client code to be able to make a fast-path branch for small slices that happen to be contiguous. Like the above item, this can result in significant performance gains for certain use-cases. ### API breaking-changes - All grapheme related APIs have been removed. However, new APIs have been added that allow the efficient implementation of those same APIs on top of Ropey. See the grapheme examples in the `examples` directory of the repo for working implementations. ## [0.6.3] - 2018-01-28 ### Features - Added a new `Rope::insert_char()` convenience method for inserting a single Unicode scalar value. ### Documentation - Updated the Chunks iterator docs to accurately reflect the new segmentation API in 0.6.x. ## [0.6.2] - 2018-01-11 ### Fixes - 0.6.0 and 0.6.1 had an API regression where you now had to specify the segmenter in the type parameters of RopeSlice and the various iterators. ## [0.6.1] - 2018-01-11 - No functional changes. Just updated the readme to render properly on crates.io. ## [0.6.0] - 2018-01-11 ### New features - Grapheme segmentation can now be customized if needed. ### API changes - `Rope::remove()`, `Rope::slice()`, and `RopeSlice::slice()` now take range syntax to specify their ranges. ## [0.5.6] - 2018-01-05 ### Documenation - Added a design overview document to the repo, explaining Ropey's design. Mainly targeted at potential contributors. - Added a more integrated example of usage to the front page of the library docs. ### Features - Fleshed out the `PartialEq` impls. `Rope` and `RopeSlice` can now be compared for equality with not just `&str`, but also `String` and `Cow`. ### Performance - `Rope::char()`, which fetches a single Unicode scalar value as a `char`, is now several times faster. ### Misc - This changelog had the wrong year on some of its dates. Heh... ## [0.5.5] - 2017-12-30 ### Bug fixes - Comparing two empty ropes for equality would panic. ### New features - Added Rope::capacity() and Rope::shrink_to_fit() methods. Although these are probably of limited use, they may be useful in especially memory-constrained environments. ## [0.5.4] - 2017-12-30 ### Bug fixes - Rope::remove() didn't always merge graphemes between chunks properly. ### Performance and memory - Inserting large texts into a rope now degrades in performance more gracefully as the insertion text becomes larger, rather than hitting a sudden performance cliff. - `Rope::remove()` got a nice speed boost. - Memory overhead has been reduced across the board. Freshly loaded files now only have ~17% overhead, and the worst-case (built up from lots of small random-location inserts) is now ~60% overhead. ### Misc - 100% unit test coverage of public APIs. - Added randomized testing via [QuickCheck](https://crates.io/crates/quickcheck). - Added benchmarks to the library. ## [0.5.3] - 2017-12-28 ### Performance and memory - Massive speed boost for small insertions: between %40 - %50 faster. - `Rope::from_str()` now only uses stack memory for strings smaller than ~3MB. (Aside from the resulting Rope itself, of course.) ### Misc - Better unit test coverage of public APIs. Still not 100%, but getting there! ## [0.5.2] - 2017-12-25 ### Bug fixes - There were ocassionally unnecessary heap allocations that took up a small amount of extra space in the rope. ### Misc - Memory overhead has been significantly reduced for ropes built up by many small coherent insertions. ## [0.5.1] - 2017-12-24 ### Bug fixes - Calling `Rope::line_to_char()` with a line index one-past-the-end would panic. This wasn't consistent with other indexing, and has been fixed and now returns the one-past-the-end char index. - Had accidentally left some asserts in the `Rope::remove()` code that were put in during debugging. They were causing significant slow downs for removes. ### Misc - Added a changelog file. [Unreleased]: https://github.com/cessen/ropey/compare/v1.6.1...HEAD [1.6.1]: https://github.com/cessen/ropey/compare/v1.6.0...v1.6.1 [1.6.0]: https://github.com/cessen/ropey/compare/v1.5.1...v1.6.0 [1.5.1]: https://github.com/cessen/ropey/compare/v1.5.0...v1.5.1 [1.5.0]: https://github.com/cessen/ropey/compare/v1.4.1...v1.5.0 [1.4.1]: https://github.com/cessen/ropey/compare/v1.4.0...v1.4.1 [1.4.0]: https://github.com/cessen/ropey/compare/v1.3.2...v1.4.0 [1.3.2]: https://github.com/cessen/ropey/compare/v1.3.1...v1.3.2 [1.3.1]: https://github.com/cessen/ropey/compare/v1.3.0...v1.3.1 [1.3.0]: https://github.com/cessen/ropey/compare/v1.2.0...v1.3.0 [1.2.0]: https://github.com/cessen/ropey/compare/v1.1.0...v1.2.0 [1.1.0]: https://github.com/cessen/ropey/compare/v1.0.1...v1.1.0 [1.0.1]: https://github.com/cessen/ropey/compare/v1.0.0...v1.0.1 [1.0.0]: https://github.com/cessen/ropey/compare/v0.9.2...v1.0.0 [0.9.2]: https://github.com/cessen/ropey/compare/v0.9.1...v0.9.2 [0.9.1]: https://github.com/cessen/ropey/compare/v0.9.0...v0.9.1 [0.9.0]: https://github.com/cessen/ropey/compare/v0.8.4...v0.9.0 [0.8.4]: https://github.com/cessen/ropey/compare/v0.8.3...v0.8.4 [0.8.3]: https://github.com/cessen/ropey/compare/v0.8.2...v0.8.3 [0.8.2]: https://github.com/cessen/ropey/compare/v0.8.1...v0.8.2 [0.8.1]: https://github.com/cessen/ropey/compare/v0.8.0...v0.8.1 [0.8.0]: https://github.com/cessen/ropey/compare/v0.7.1...v0.8.0 [0.7.1]: https://github.com/cessen/ropey/compare/v0.7.0...v0.7.1 [0.7.0]: https://github.com/cessen/ropey/compare/v0.6.3...v0.7.0 [0.6.3]: https://github.com/cessen/ropey/compare/v0.6.2...v0.6.3 [0.6.2]: https://github.com/cessen/ropey/compare/v0.6.1...v0.6.2 [0.6.1]: https://github.com/cessen/ropey/compare/v0.6.0...v0.6.1 [0.6.0]: https://github.com/cessen/ropey/compare/v0.5.6...v0.6.0 [0.5.6]: https://github.com/cessen/ropey/compare/v0.5.5...v0.5.6 [0.5.5]: https://github.com/cessen/ropey/compare/v0.5.4...v0.5.5 [0.5.4]: https://github.com/cessen/ropey/compare/v0.5.3...v0.5.4 [0.5.3]: https://github.com/cessen/ropey/compare/v0.5.2...v0.5.3 [0.5.2]: https://github.com/cessen/ropey/compare/v0.5.1...v0.5.2 [0.5.1]: https://github.com/cessen/ropey/releases/tag/v0.5.1 ropey-1.6.1/Cargo.lock0000644000000551110000000000100101550ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "aho-corasick" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi", "libc", "winapi", ] [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bit-set" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" dependencies = [ "bit-vec", ] [[package]] name = "bit-vec" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "bumpalo" version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cast" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ "bitflags 1.3.2", "textwrap", "unicode-width", ] [[package]] name = "criterion" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f" dependencies = [ "atty", "cast", "clap", "criterion-plot", "csv", "itertools", "lazy_static", "num-traits", "oorandom", "plotters", "rayon", "regex", "serde", "serde_cbor", "serde_derive", "serde_json", "tinytemplate", "walkdir", ] [[package]] name = "criterion-plot" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876" dependencies = [ "cast", "itertools", ] [[package]] name = "crossbeam-deque" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" dependencies = [ "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" version = "0.9.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", "memoffset", "scopeguard", ] [[package]] name = "crossbeam-utils" version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", ] [[package]] name = "csv" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" dependencies = [ "csv-core", "itoa", "ryu", "serde", ] [[package]] name = "csv-core" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" dependencies = [ "memchr", ] [[package]] name = "either" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "errno" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" dependencies = [ "libc", "windows-sys", ] [[package]] name = "fastrand" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fxhash" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" dependencies = [ "byteorder", ] [[package]] name = "getrandom" version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "half" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" [[package]] name = "hermit-abi" version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] [[package]] name = "itertools" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "itoa" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "libm" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "linux-raw-sys" version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" [[package]] name = "log" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "memoffset" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" dependencies = [ "autocfg", ] [[package]] name = "num-traits" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", "libm", ] [[package]] name = "once_cell" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "oorandom" version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "plotters" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" dependencies = [ "num-traits", "plotters-backend", "plotters-svg", "wasm-bindgen", "web-sys", ] [[package]] name = "plotters-backend" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" [[package]] name = "plotters-svg" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" dependencies = [ "plotters-backend", ] [[package]] name = "ppv-lite86" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] [[package]] name = "proptest" version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c003ac8c77cb07bb74f5f198bce836a689bcd5a42574612bf14d17bfd08c20e" dependencies = [ "bit-set", "bit-vec", "bitflags 2.4.1", "lazy_static", "num-traits", "rand", "rand_chacha", "rand_xorshift", "regex-syntax 0.7.5", "rusty-fork", "tempfile", "unarray", ] [[package]] name = "quick-error" version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", "rand_core", ] [[package]] name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] [[package]] name = "rand_xorshift" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" dependencies = [ "rand_core", ] [[package]] name = "rayon" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" dependencies = [ "either", "rayon-core", ] [[package]] name = "rayon-core" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" dependencies = [ "crossbeam-deque", "crossbeam-utils", ] [[package]] name = "redox_syscall" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "regex" version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax 0.8.2", ] [[package]] name = "regex-automata" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", "regex-syntax 0.8.2", ] [[package]] name = "regex-syntax" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "regex-syntax" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "ropey" version = "1.6.1" dependencies = [ "criterion", "fnv", "fxhash", "proptest", "rand", "smallvec", "str_indices", "unicode-segmentation", ] [[package]] name = "rustix" version = "0.38.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "745ecfa778e66b2b63c88a61cb36e0eea109e803b0b86bf9879fbc77c70e86ed" dependencies = [ "bitflags 2.4.1", "errno", "libc", "linux-raw-sys", "windows-sys", ] [[package]] name = "rusty-fork" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" dependencies = [ "fnv", "quick-error", "tempfile", "wait-timeout", ] [[package]] name = "ryu" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ "winapi-util", ] [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" dependencies = [ "serde_derive", ] [[package]] name = "serde_cbor" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" dependencies = [ "half", "serde", ] [[package]] name = "serde_derive" version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "smallvec" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "str_indices" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8eeaedde8e50d8a331578c9fa9a288df146ce5e16173ad26ce82f6e263e2be4" [[package]] name = "syn" version = "2.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tempfile" version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ "cfg-if", "fastrand", "redox_syscall", "rustix", "windows-sys", ] [[package]] name = "textwrap" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" dependencies = [ "unicode-width", ] [[package]] name = "tinytemplate" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" dependencies = [ "serde", "serde_json", ] [[package]] name = "unarray" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-segmentation" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] name = "unicode-width" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" [[package]] name = "wait-timeout" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" dependencies = [ "libc", ] [[package]] name = "walkdir" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" dependencies = [ "same-file", "winapi-util", ] [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "web-sys" version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" ropey-1.6.1/Cargo.toml0000644000000033510000000000100101770ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] name = "ropey" version = "1.6.1" authors = ["Nathan Vegdahl "] exclude = [ "/design/*", "/benches/*.txt", "/fuzz/**", "/.github/*", ] description = "A fast and robust text rope for Rust" documentation = "https://docs.rs/ropey" readme = "README.md" keywords = [ "rope", "text", "edit", "buffer", ] categories = [ "text-processing", "data-structures", ] license = "MIT" repository = "https://github.com/cessen/ropey" [[bench]] name = "create" harness = false [[bench]] name = "insert" harness = false [[bench]] name = "hash" harness = false [[bench]] name = "remove" harness = false [[bench]] name = "queries" harness = false [[bench]] name = "iterators" harness = false [dependencies.smallvec] version = "1.0.0" [dependencies.str_indices] version = "0.4" default-features = false [dev-dependencies.criterion] version = "0.3" features = ["html_reports"] [dev-dependencies.fnv] version = "1" [dev-dependencies.fxhash] version = "0.2" [dev-dependencies.proptest] version = "1.0" [dev-dependencies.rand] version = "0.8" [dev-dependencies.unicode-segmentation] version = "1.3" [features] cr_lines = [] default = [ "unicode_lines", "simd", ] simd = ["str_indices/simd"] small_chunks = [] unicode_lines = ["cr_lines"] ropey-1.6.1/Cargo.toml.orig000064400000000000000000000026021046102023000136560ustar 00000000000000[package] name = "ropey" version = "1.6.1" authors = ["Nathan Vegdahl "] description = "A fast and robust text rope for Rust" documentation = "https://docs.rs/ropey" repository = "https://github.com/cessen/ropey" readme = "README.md" license = "MIT" keywords = ["rope", "text", "edit", "buffer"] categories = ["text-processing", "data-structures"] exclude = ["/design/*", "/benches/*.txt", "/fuzz/**", "/.github/*"] [features] default = ["unicode_lines", "simd"] cr_lines = [] # Enable recognizing carriage returns as line breaks. unicode_lines = ["cr_lines"] # Enable recognizing all Unicode line breaks. simd = ["str_indices/simd"] # Internal feature: Not part of public stable API # enables a much smaller chunk size that makes it # easier to catch bugs without requiring huge text sizes during fuzzing. small_chunks = [] [dependencies] smallvec = "1.0.0" str_indices = { version = "0.4", default-features = false } [dev-dependencies] rand = "0.8" proptest = "1.0" criterion = { version = "0.3", features = ["html_reports"] } unicode-segmentation = "1.3" fnv = "1" fxhash = "0.2" #----------------------------------------- [[bench]] name = "create" harness = false [[bench]] name = "insert" harness = false [[bench]] name = "hash" harness = false [[bench]] name = "remove" harness = false [[bench]] name = "queries" harness = false [[bench]] name = "iterators" harness = false ropey-1.6.1/LICENSE.md000064400000000000000000000020421046102023000123710ustar 00000000000000Copyright (c) 2017 Nathan Vegdahl Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ropey-1.6.1/README.md000064400000000000000000000141301046102023000122450ustar 00000000000000# Ropey [![CI Build Status][github-ci-img]][github-ci] [![Latest Release][crates-io-badge]][crates-io-url] [![Documentation][docs-rs-img]][docs-rs-url] Ropey is a utf8 text rope for Rust, designed to be the backing text-buffer for applications such as text editors. Ropey is fast, robust, and can handle huge texts and memory-incoherent edits with ease. ## Example Usage ```rust // Load a text file. let mut text = ropey::Rope::from_reader( File::open("my_great_book.txt")? )?; // Print the 516th line (zero-indexed). println!("{}", text.line(515)); // Get the start/end char indices of the line. let start_idx = text.line_to_char(515); let end_idx = text.line_to_char(516); // Remove the line... text.remove(start_idx..end_idx); // ...and replace it with something better. text.insert(start_idx, "The flowers are... so... dunno.\n"); // Print the changes, along with the previous few lines for context. let start_idx = text.line_to_char(511); let end_idx = text.line_to_char(516); println!("{}", text.slice(start_idx..end_idx)); // Write the file back out to disk. text.write_to( BufWriter::new(File::create("my_great_book.txt")?) )?; ``` ## When Should I Use Ropey? Ropey is designed and built to be the backing text buffer for applications such as text editors, and its design trade-offs reflect that. Ropey is good at: - Handling frequent edits to medium-to-large texts. Even on texts that are multiple gigabytes large, edits are measured in single-digit microseconds. - Handling Unicode correctly. It is impossible to create invalid utf8 through Ropey, and all Unicode line endings are correctly tracked including CRLF. - Having flat, predictable performance characteristics. Ropey will never be the source of hiccups or stutters in your software. On the other hand, Ropey is _not_ good at: - Handling texts smaller than a couple of kilobytes or so. That is to say, Ropey will handle them fine, but Ropey allocates space in kilobyte chunks, which introduces unnecessary bloat if your texts are almost always small. - Handling texts that are larger than available memory. Ropey is an in-memory data structure. - Getting the best performance for every possible use-case. Ropey puts work into tracking both line endings and unicode scalar values, which is performance overhead you may not need depending on your use-case. Keep this in mind when selecting Ropey for your project. Ropey is very good at what it does, but like all software it is designed with certain applications in mind. ## Features ### Strong Unicode support Ropey's atomic unit of text is [Unicode scalar values](https://www.unicode.org/glossary/#unicode_scalar_value) (or [`char`](https://doc.rust-lang.org/std/primitive.char.html)s in Rust) encoded as utf8. All of Ropey's editing and slicing operations are done in terms of char indices, which prevents accidental creation of invalid utf8 data. Ropey also supports converting between scalar value indices and utf16 code unit indices, for interoperation with external APIs that may still use utf16. ### Line-aware Ropey knows about line breaks, allowing you to index into and iterate over lines of text. The line breaks Ropey recognizes are also configurable at build time via feature flags. See Ropey's documentation for details. ### Rope slices Ropey has rope slices that allow you to work with just parts of a rope, using all the read-only operations of a full rope including iterators and making sub-slices. ### Flexible APIs with low-level access Although Ropey is intentionally limited in scope, it also provides APIs for efficiently accessing and working with its internal text chunk representation, allowing additional functionality to be efficiently implemented by client code with minimal overhead. ### Efficient Ropey is fast and minimizes memory usage: - On a recent mobile i7 Intel CPU, Ropey performed over 1.8 million small incoherent insertions per second while building up a text roughly 100 MB large. Coherent insertions (i.e. all near the same place in the text) are even faster, doing the same task at over 3.3 million insertions per second. - Freshly loading a file from disk only incurs about 10% memory overhead. For example, a 100 MB text file will occupy about 110 MB of memory when loaded by Ropey. - Cloning ropes is _extremely_ cheap. Rope clones share data, so an initial clone only takes 8 bytes of memory. After that, memory usage will grow incrementally as the clones diverge due to edits. ### Thread safe Ropey ensures that even though clones share memory, everything is thread-safe. Clones can be sent to other threads for both reading and writing. ## Unsafe code Ropey uses unsafe code to help achieve some of its space and performance characteristics. Although effort has been put into keeping the unsafe code compartmentalized and making it correct, please be cautious about using Ropey in software that may face adversarial conditions. Auditing, fuzzing, etc. of the unsafe code in Ropey is extremely welcome. If you find any unsoundness, _please_ file an issue! Also welcome are recommendations for how to remove any of the unsafe code without introducing significant space or performance regressions, or how to compartmentalize the unsafe code even better. ## License Ropey is licensed under the MIT license (LICENSE.md or http://opensource.org/licenses/MIT) ## Contributing Contributions are absolutely welcome! However, please open an issue or email me to discuss larger changes, to avoid doing a lot of work that may get rejected. An overview of Ropey's design can be found [here](https://github.com/cessen/ropey/blob/master/design/design.md). Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in Ropey by you will be licensed as above, without any additional terms or conditions. [crates-io-badge]: https://img.shields.io/crates/v/ropey.svg [crates-io-url]: https://crates.io/crates/ropey [github-ci-img]: https://github.com/cessen/ropey/workflows/ci/badge.svg [github-ci]: https://github.com/cessen/ropey/actions?query=workflow%3Aci [docs-rs-img]: https://docs.rs/ropey/badge.svg [docs-rs-url]: https://docs.rs/ropey ropey-1.6.1/benches/create.rs000064400000000000000000000024151046102023000142110ustar 00000000000000extern crate criterion; extern crate ropey; use criterion::{black_box, criterion_group, criterion_main, Criterion}; use ropey::Rope; const TEXT_SMALL: &str = include_str!("small.txt"); const TEXT_MEDIUM: &str = include_str!("medium.txt"); const TEXT_LARGE: &str = include_str!("large.txt"); const TEXT_LF: &str = include_str!("lf.txt"); //---- fn from_str(c: &mut Criterion) { let mut group = c.benchmark_group("from_str"); group.bench_function("small", |bench| { bench.iter(|| { Rope::from_str(black_box(TEXT_SMALL)); }) }); group.bench_function("medium", |bench| { bench.iter(|| { Rope::from_str(black_box(TEXT_MEDIUM)); }) }); group.bench_function("large", |bench| { bench.iter(|| { Rope::from_str(black_box(TEXT_LARGE)); }) }); group.bench_function("linefeeds", |bench| { bench.iter(|| { Rope::from_str(black_box(TEXT_LF)); }) }); } fn rope_clone(c: &mut Criterion) { let rope = Rope::from_str(TEXT_LARGE); c.bench_function("rope_clone", |bench| { bench.iter(|| { let _ = black_box(&rope).clone(); }) }); } //---- criterion_group!(benches, from_str, rope_clone,); criterion_main!(benches); ropey-1.6.1/benches/hash.rs000064400000000000000000000061751046102023000137000ustar 00000000000000extern crate criterion; extern crate fnv; extern crate fxhash; extern crate ropey; use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; use criterion::{black_box, criterion_group, criterion_main, Criterion}; use fnv::FnvHasher; use fxhash::FxHasher; use ropey::Rope; const TEXT: &str = include_str!("large.txt"); const TEXT_SMALL: &str = include_str!("small.txt"); const TEXT_TINY: &str = "hello"; //---- fn hash_large(c: &mut Criterion) { let mut group = c.benchmark_group("hash_large"); group.bench_function("default", |bench| { let r = Rope::from_str(TEXT); bench.iter(|| { let mut hasher = DefaultHasher::default(); r.hash(black_box(&mut hasher)); black_box(hasher.finish()); }) }); group.bench_function("fnv", |bench| { let r = Rope::from_str(TEXT); bench.iter(|| { let mut hasher = FnvHasher::default(); r.hash(black_box(&mut hasher)); black_box(hasher.finish()); }) }); group.bench_function("fxhash", |bench| { let r = Rope::from_str(TEXT); bench.iter(|| { let mut hasher = FxHasher::default(); r.hash(black_box(&mut hasher)); black_box(hasher.finish()); }) }); } fn hash_small(c: &mut Criterion) { let mut group = c.benchmark_group("hash_small"); group.bench_function("default", |bench| { let r = Rope::from_str(TEXT_SMALL); bench.iter(|| { let mut hasher = DefaultHasher::default(); r.hash(black_box(&mut hasher)); black_box(hasher.finish()); }) }); group.bench_function("fnv", |bench| { let r = Rope::from_str(TEXT_SMALL); bench.iter(|| { let mut hasher = FnvHasher::default(); r.hash(black_box(&mut hasher)); black_box(hasher.finish()); }) }); group.bench_function("fxhash", |bench| { let r = Rope::from_str(TEXT_SMALL); bench.iter(|| { let mut hasher = FxHasher::default(); r.hash(black_box(&mut hasher)); black_box(hasher.finish()); }) }); } fn hash_tiny(c: &mut Criterion) { let mut group = c.benchmark_group("hash_tiny"); group.bench_function("default", |bench| { let r = Rope::from_str(TEXT_TINY); bench.iter(|| { let mut hasher = DefaultHasher::default(); r.hash(black_box(&mut hasher)); black_box(hasher.finish()); }) }); group.bench_function("fnv", |bench| { let r = Rope::from_str(TEXT_TINY); bench.iter(|| { let mut hasher = FnvHasher::default(); r.hash(black_box(&mut hasher)); black_box(hasher.finish()); }) }); group.bench_function("fxhash", |bench| { let r = Rope::from_str(TEXT_TINY); bench.iter(|| { let mut hasher = FxHasher::default(); r.hash(black_box(&mut hasher)); black_box(hasher.finish()); }) }); } //---- criterion_group!(benches, hash_large, hash_small, hash_tiny,); criterion_main!(benches); ropey-1.6.1/benches/insert.rs000064400000000000000000000111041046102023000142450ustar 00000000000000extern crate criterion; extern crate rand; extern crate ropey; use criterion::{criterion_group, criterion_main, Criterion}; use rand::random; use ropey::Rope; const TEXT: &str = include_str!("large.txt"); //---- fn insert_char(c: &mut Criterion) { let mut group = c.benchmark_group("insert_char"); group.bench_function("random", |bench| { let mut rope = Rope::from_str(TEXT); bench.iter(|| { let len = rope.len_chars(); rope.insert_char(random::() % len, 'a') }) }); group.bench_function("start", |bench| { let mut rope = Rope::from_str(TEXT); bench.iter(|| { rope.insert_char(0, 'a'); }) }); group.bench_function("middle", |bench| { let mut rope = Rope::from_str(TEXT); bench.iter(|| { let len = rope.len_chars(); rope.insert_char(len / 2, 'a'); }) }); group.bench_function("end", |bench| { let mut rope = Rope::from_str(TEXT); bench.iter(|| { let len = rope.len_chars(); rope.insert_char(len, 'a'); }) }); } fn insert_small(c: &mut Criterion) { let mut group = c.benchmark_group("insert_small"); group.bench_function("random", |bench| { let mut rope = Rope::from_str(TEXT); bench.iter(|| { let len = rope.len_chars(); rope.insert(random::() % len, "a"); }) }); group.bench_function("start", |bench| { let mut rope = Rope::from_str(TEXT); bench.iter(|| { rope.insert(0, "a"); }) }); group.bench_function("middle", |bench| { let mut rope = Rope::from_str(TEXT); bench.iter(|| { let len = rope.len_chars(); rope.insert(len / 2, "a"); }) }); group.bench_function("end", |bench| { let mut rope = Rope::from_str(TEXT); bench.iter(|| { let len = rope.len_chars(); rope.insert(len, "a"); }) }); } fn insert_medium(c: &mut Criterion) { let mut group = c.benchmark_group("insert_medium"); group.bench_function("random", |bench| { let mut rope = Rope::from_str(TEXT); bench.iter(|| { let len = rope.len_chars(); rope.insert(random::() % len, "This is some text."); }) }); group.bench_function("start", |bench| { let mut rope = Rope::from_str(TEXT); bench.iter(|| { rope.insert(0, "This is some text."); }) }); group.bench_function("middle", |bench| { let mut rope = Rope::from_str(TEXT); bench.iter(|| { let len = rope.len_chars(); rope.insert(len / 2, "This is some text."); }) }); group.bench_function("end", |bench| { let mut rope = Rope::from_str(TEXT); bench.iter(|| { let len = rope.len_chars(); rope.insert(len, "This is some text."); }) }); } const INSERT_TEXT: &str = include_str!("small.txt"); fn insert_large(c: &mut Criterion) { let mut group = c.benchmark_group("insert_large"); group.bench_function("random", |bench| { let mut rope = Rope::from_str(TEXT); bench.iter(|| { let len = rope.len_chars(); rope.insert(random::() % len, INSERT_TEXT); }) }); group.bench_function("start", |bench| { let mut rope = Rope::from_str(TEXT); bench.iter(|| { rope.insert(0, INSERT_TEXT); }) }); group.bench_function("middle", |bench| { let mut rope = Rope::from_str(TEXT); bench.iter(|| { let len = rope.len_chars(); rope.insert(len / 2, INSERT_TEXT); }) }); group.bench_function("end", |bench| { let mut rope = Rope::from_str(TEXT); bench.iter(|| { let len = rope.len_chars(); rope.insert(len, INSERT_TEXT); }) }); } //---- fn insert_after_clone(c: &mut Criterion) { c.bench_function("insert_after_clone", |bench| { let rope = Rope::from_str(TEXT); let mut rope_clone = rope.clone(); let mut i = 0; bench.iter(|| { if i > 32 { i = 0; rope_clone = rope.clone(); } let len = rope_clone.len_chars(); rope_clone.insert(random::() % len, "a"); i += 1; }) }); } //---- criterion_group!( benches, insert_char, insert_small, insert_medium, insert_large, insert_after_clone ); criterion_main!(benches); ropey-1.6.1/benches/iterators.rs000064400000000000000000000127261046102023000147700ustar 00000000000000extern crate criterion; extern crate ropey; use criterion::{criterion_group, criterion_main, Criterion}; use ropey::Rope; const TEXT: &str = include_str!("large.txt"); const TEXT_TINY: &str = include_str!("tiny.txt"); //---- fn iter_prev(c: &mut Criterion) { let mut group = c.benchmark_group("iter_prev"); group.bench_function("bytes", |bench| { let r = Rope::from_str(TEXT); let itr_src = r.bytes_at(r.len_bytes()); let mut itr = itr_src.clone(); bench.iter(|| { if itr.prev().is_none() { itr = itr_src.clone(); } }) }); group.bench_function("chars", |bench| { let r = Rope::from_str(TEXT); let itr_src = r.chars_at(r.len_chars()); let mut itr = itr_src.clone(); bench.iter(|| { if itr.prev().is_none() { itr = itr_src.clone(); } }) }); group.bench_function("chunks", |bench| { let r = Rope::from_str(TEXT); let itr_src = r.chunks_at_char(r.len_chars()).0; let mut itr = itr_src.clone(); bench.iter(|| { if itr.prev().is_none() { itr = itr_src.clone(); } }) }); } fn iter_prev_lines(c: &mut Criterion) { let mut group = c.benchmark_group("iter_prev_lines"); group.bench_function("lines", |bench| { let r = Rope::from_str(TEXT); let itr_src = r.lines_at(r.len_lines()); let mut itr = itr_src.clone(); bench.iter(|| { if itr.prev().is_none() { itr = itr_src.clone(); } }) }); group.bench_function("lines_tiny", |bench| { let r = Rope::from_str(TEXT_TINY); let itr_src = r.lines_at(r.len_lines()); let mut itr = itr_src.clone(); bench.iter(|| { if itr.prev().is_none() { itr = itr_src.clone(); } }) }); } fn iter_next(c: &mut Criterion) { let mut group = c.benchmark_group("iter_next"); group.bench_function("bytes", |bench| { let r = Rope::from_str(TEXT); let mut itr = r.bytes().cycle(); bench.iter(|| { itr.next(); }) }); group.bench_function("chars", |bench| { let r = Rope::from_str(TEXT); let mut itr = r.chars().cycle(); bench.iter(|| { itr.next(); }) }); group.bench_function("chunks", |bench| { let r = Rope::from_str(TEXT); let mut itr = r.chunks().cycle(); bench.iter(|| { itr.next(); }) }); } fn iter_next_lines(c: &mut Criterion) { let mut group = c.benchmark_group("iter_next_lines"); group.bench_function("lines", |bench| { let r = Rope::from_str(TEXT); let mut itr = r.lines().cycle(); bench.iter(|| { itr.next(); }) }); group.bench_function("lines_tiny", |bench| { let r = Rope::from_str(TEXT_TINY); let mut itr = r.lines().cycle(); bench.iter(|| { itr.next(); }) }); } fn iter_create(c: &mut Criterion) { let mut group = c.benchmark_group("iter_create"); group.bench_function("bytes", |bench| { let r = Rope::from_str(TEXT); bench.iter(|| { r.bytes(); }) }); group.bench_function("chars", |bench| { let r = Rope::from_str(TEXT); bench.iter(|| { r.chars(); }) }); group.bench_function("lines", |bench| { let r = Rope::from_str(TEXT); bench.iter(|| { r.lines(); }) }); group.bench_function("chunks", |bench| { let r = Rope::from_str(TEXT); bench.iter(|| { r.chunks(); }) }); } fn iter_create_at(c: &mut Criterion) { let mut group = c.benchmark_group("iter_create_at"); group.bench_function("bytes", |bench| { let r = Rope::from_str(TEXT); let len = r.len_bytes(); let mut i = 0; bench.iter(|| { r.bytes_at(i % (len + 1)); i += 1; }) }); group.bench_function("chars", |bench| { let r = Rope::from_str(TEXT); let len = r.len_chars(); let mut i = 0; bench.iter(|| { r.chars_at(i % (len + 1)); i += 1; }) }); group.bench_function("lines", |bench| { let r = Rope::from_str(TEXT); let len = r.len_lines(); let mut i = 0; bench.iter(|| { r.lines_at(i % (len + 1)); i += 1; }) }); group.bench_function("chunks_at_byte", |bench| { let r = Rope::from_str(TEXT); let len = r.len_bytes(); let mut i = 0; bench.iter(|| { r.chunks_at_byte(i % (len + 1)); i += 1; }) }); group.bench_function("chunks_at_char", |bench| { let r = Rope::from_str(TEXT); let len = r.len_chars(); let mut i = 0; bench.iter(|| { r.chunks_at_char(i % (len + 1)); i += 1; }) }); group.bench_function("chunks_at_line_break", |bench| { let r = Rope::from_str(TEXT); let len = r.len_lines(); let mut i = 0; bench.iter(|| { r.chunks_at_line_break(i % (len + 1)); i += 1; }) }); } //---- criterion_group!( benches, iter_prev, iter_prev_lines, iter_next, iter_next_lines, iter_create, iter_create_at, ); criterion_main!(benches); ropey-1.6.1/benches/queries.rs000064400000000000000000000135051046102023000144250ustar 00000000000000extern crate criterion; extern crate rand; extern crate ropey; use criterion::{criterion_group, criterion_main, Criterion}; use rand::random; use ropey::Rope; const TEXT: &str = include_str!("large.txt"); const SMALL_TEXT: &str = include_str!("small.txt"); //---- fn index_convert(c: &mut Criterion) { let mut group = c.benchmark_group("index_convert"); group.bench_function("byte_to_char", |bench| { let rope = Rope::from_str(TEXT); let len = rope.len_bytes(); bench.iter(|| { rope.byte_to_char(random::() % (len + 1)); }) }); group.bench_function("byte_to_line", |bench| { let rope = Rope::from_str(TEXT); let len = rope.len_bytes(); bench.iter(|| { rope.byte_to_line(random::() % (len + 1)); }) }); group.bench_function("char_to_byte", |bench| { let rope = Rope::from_str(TEXT); let len = rope.len_chars(); bench.iter(|| { rope.char_to_byte(random::() % (len + 1)); }) }); group.bench_function("char_to_line", |bench| { let rope = Rope::from_str(TEXT); let len = rope.len_chars(); bench.iter(|| { rope.char_to_line(random::() % (len + 1)); }) }); group.bench_function("line_to_byte", |bench| { let rope = Rope::from_str(TEXT); let len = rope.len_lines(); bench.iter(|| { rope.line_to_byte(random::() % (len + 1)); }) }); group.bench_function("line_to_char", |bench| { let rope = Rope::from_str(TEXT); let len = rope.len_lines(); bench.iter(|| { rope.line_to_char(random::() % (len + 1)); }) }); } fn get(c: &mut Criterion) { let mut group = c.benchmark_group("get"); group.bench_function("byte", |bench| { let rope = Rope::from_str(TEXT); let len = rope.len_bytes(); bench.iter(|| { rope.byte(random::() % len); }) }); group.bench_function("char", |bench| { let rope = Rope::from_str(TEXT); let len = rope.len_chars(); bench.iter(|| { rope.char(random::() % len); }) }); group.bench_function("line", |bench| { let rope = Rope::from_str(TEXT); let len = rope.len_lines(); bench.iter(|| { rope.line(random::() % len); }) }); group.bench_function("chunk_at_byte", |bench| { let rope = Rope::from_str(TEXT); let len = rope.len_bytes(); bench.iter(|| { rope.chunk_at_byte(random::() % (len + 1)); }) }); group.bench_function("chunk_at_byte_slice", |bench| { let rope = Rope::from_str(TEXT); let slice = rope.slice(324..(rope.len_chars() - 213)); let len = slice.len_bytes(); bench.iter(|| { slice.chunk_at_byte(random::() % (len + 1)); }) }); group.bench_function("chunk_at_char", |bench| { let rope = Rope::from_str(TEXT); let len = rope.len_chars(); bench.iter(|| { rope.chunk_at_char(random::() % (len + 1)); }) }); group.bench_function("chunk_at_char_slice", |bench| { let rope = Rope::from_str(TEXT); let slice = rope.slice(324..(rope.len_chars() - 213)); let len = slice.len_chars(); bench.iter(|| { slice.chunk_at_char(random::() % (len + 1)); }) }); group.bench_function("chunk_at_line_break", |bench| { let rope = Rope::from_str(TEXT); let len = rope.len_lines(); bench.iter(|| { rope.chunk_at_line_break(random::() % (len + 1)); }) }); group.bench_function("chunk_at_line_break_slice", |bench| { let rope = Rope::from_str(TEXT); let slice = rope.slice(324..(rope.len_chars() - 213)); let len = slice.len_lines(); bench.iter(|| { slice.chunk_at_line_break(random::() % (len + 1)); }) }); } fn slice(c: &mut Criterion) { let mut group = c.benchmark_group("slice"); group.bench_function("slice", |bench| { let rope = Rope::from_str(TEXT); let len = rope.len_chars(); bench.iter(|| { let mut start = random::() % (len + 1); let mut end = random::() % (len + 1); if start > end { std::mem::swap(&mut start, &mut end); } rope.slice(start..end); }) }); group.bench_function("slice_small", |bench| { let rope = Rope::from_str(TEXT); let len = rope.len_chars(); bench.iter(|| { let mut start = random::() % (len + 1); if start > (len - 65) { start = len - 65; } let end = start + 64; rope.slice(start..end); }) }); group.bench_function("slice_from_small_rope", |bench| { let rope = Rope::from_str(SMALL_TEXT); let len = rope.len_chars(); bench.iter(|| { let mut start = random::() % (len + 1); let mut end = random::() % (len + 1); if start > end { std::mem::swap(&mut start, &mut end); } rope.slice(start..end); }) }); group.bench_function("slice_whole_rope", |bench| { let rope = Rope::from_str(TEXT); bench.iter(|| { rope.slice(..); }) }); group.bench_function("slice_whole_slice", |bench| { let rope = Rope::from_str(TEXT); let len = rope.len_chars(); let slice = rope.slice(1..len - 1); bench.iter(|| { slice.slice(..); }) }); } //---- criterion_group!(benches, index_convert, get, slice,); criterion_main!(benches); ropey-1.6.1/benches/remove.rs000064400000000000000000000157211046102023000142470ustar 00000000000000extern crate criterion; extern crate rand; extern crate ropey; use criterion::{criterion_group, criterion_main, Criterion}; use rand::random; use ropey::Rope; const TEXT: &str = include_str!("large.txt"); const TEXT_SMALL: &str = include_str!("small.txt"); fn mul_string_length(text: &str, n: usize) -> String { let mut mtext = String::new(); for _ in 0..n { mtext.push_str(text); } mtext } //---- const LEN_MUL_SMALL: usize = 1; fn remove_small(c: &mut Criterion) { let mut group = c.benchmark_group("remove_small"); group.bench_function("random", |bench| { let text = mul_string_length(TEXT, LEN_MUL_SMALL); let mut rope = Rope::from_str(&text); bench.iter(|| { let len = rope.len_chars(); let start = random::() % (len + 1); let end = (start + 1).min(len); rope.remove(start..end); if rope.len_bytes() == TEXT.len() / 2 { rope = Rope::from_str(&text); } }) }); group.bench_function("start", |bench| { let text = mul_string_length(TEXT, LEN_MUL_SMALL); let mut rope = Rope::from_str(&text); bench.iter(|| { let len = rope.len_chars(); let start = 0; let end = (start + 1).min(len); rope.remove(start..end); if rope.len_bytes() == TEXT.len() / 2 { rope = Rope::from_str(&text); } }) }); group.bench_function("middle", |bench| { let text = mul_string_length(TEXT, LEN_MUL_SMALL); let mut rope = Rope::from_str(&text); bench.iter(|| { let len = rope.len_chars(); let start = len / 2; let end = (start + 1).min(len); rope.remove(start..end); if rope.len_bytes() == TEXT.len() / 2 { rope = Rope::from_str(&text); } }) }); group.bench_function("end", |bench| { let text = mul_string_length(TEXT, LEN_MUL_SMALL); let mut rope = Rope::from_str(&text); bench.iter(|| { let len = rope.len_chars(); let end = len; let start = end - (1).min(len); rope.remove(start..end); if rope.len_bytes() == TEXT.len() / 2 { rope = Rope::from_str(&text); } }) }); } const LEN_MUL_MEDIUM: usize = 1; fn remove_medium(c: &mut Criterion) { let mut group = c.benchmark_group("remove_medium"); group.bench_function("random", |bench| { let text = mul_string_length(TEXT, LEN_MUL_MEDIUM); let mut rope = Rope::from_str(&text); bench.iter(|| { let len = rope.len_chars(); let start = random::() % (len + 1); let end = (start + 15).min(len); rope.remove(start..end); if rope.len_bytes() == TEXT.len() / 2 { rope = Rope::from_str(&text); } }) }); group.bench_function("start", |bench| { let text = mul_string_length(TEXT, LEN_MUL_MEDIUM); let mut rope = Rope::from_str(&text); bench.iter(|| { let len = rope.len_chars(); let start = 0; let end = (start + 15).min(len); rope.remove(start..end); if rope.len_bytes() == TEXT.len() / 2 { rope = Rope::from_str(&text); } }) }); group.bench_function("middle", |bench| { let text = mul_string_length(TEXT, LEN_MUL_MEDIUM); let mut rope = Rope::from_str(&text); bench.iter(|| { let len = rope.len_chars(); let start = len / 2; let end = (start + 15).min(len); rope.remove(start..end); if rope.len_bytes() == TEXT.len() / 2 { rope = Rope::from_str(&text); } }) }); group.bench_function("end", |bench| { let text = mul_string_length(TEXT, LEN_MUL_MEDIUM); let mut rope = Rope::from_str(&text); bench.iter(|| { let len = rope.len_chars(); let end = len; let start = end - (15).min(len); rope.remove(start..end); if rope.len_bytes() == TEXT.len() / 2 { rope = Rope::from_str(&text); } }) }); } const LEN_MUL_LARGE: usize = 4; fn remove_large(c: &mut Criterion) { let mut group = c.benchmark_group("remove_large"); group.bench_function("random", |bench| { let text = mul_string_length(TEXT, LEN_MUL_LARGE); let mut rope = Rope::from_str(&text); bench.iter(|| { let len = rope.len_chars(); let start = random::() % (len + 1); let end = (start + TEXT_SMALL.len()).min(len); rope.remove(start..end); if rope.len_bytes() == 0 { rope = Rope::from_str(&text); } }) }); group.bench_function("start", |bench| { let text = mul_string_length(TEXT, LEN_MUL_LARGE); let mut rope = Rope::from_str(&text); bench.iter(|| { let len = rope.len_chars(); let start = 0; let end = (start + TEXT_SMALL.len()).min(len); rope.remove(start..end); if rope.len_bytes() == 0 { rope = Rope::from_str(&text); } }) }); group.bench_function("middle", |bench| { let text = mul_string_length(TEXT, LEN_MUL_LARGE); let mut rope = Rope::from_str(&text); bench.iter(|| { let len = rope.len_chars(); let start = len / 2; let end = (start + TEXT_SMALL.len()).min(len); rope.remove(start..end); if rope.len_bytes() == 0 { rope = Rope::from_str(&text); } }) }); group.bench_function("end", |bench| { let text = mul_string_length(TEXT, LEN_MUL_LARGE); let mut rope = Rope::from_str(&text); bench.iter(|| { let len = rope.len_chars(); let end = len; let start = end - TEXT_SMALL.len().min(len); rope.remove(start..end); if rope.len_bytes() == 0 { rope = Rope::from_str(&text); } }) }); } fn remove_initial_after_clone(c: &mut Criterion) { c.bench_function("remove_initial_after_clone", |bench| { let rope = Rope::from_str(TEXT); let mut rope_clone = rope.clone(); let mut i = 0; bench.iter(|| { if i > 32 { i = 0; rope_clone = rope.clone(); } let len = rope_clone.len_chars(); let start = random::() % (len + 1); let end = (start + 1).min(len); rope_clone.remove(start..end); i += 1; }) }); } //---- criterion_group!( benches, remove_small, remove_medium, remove_large, remove_initial_after_clone ); criterion_main!(benches); ropey-1.6.1/examples/front_page.rs000064400000000000000000000022471046102023000153040ustar 00000000000000//! This is the example from the front page of Ropey's documentation. extern crate ropey; use std::io::Result; use ropey::Rope; use std::fs::File; use std::io::{BufReader, BufWriter}; fn main() { do_stuff().unwrap(); } /// Wrapper function, so we can use ? operator. fn do_stuff() -> Result<()> { // Load a text file. let mut text = Rope::from_reader(BufReader::new(File::open("my_great_book.txt")?))?; // Print the 516th line (zero-indexed) to see the terrible // writing. println!("{}", text.line(515)); // Get the start/end char indices of the line. let start_idx = text.line_to_char(515); let end_idx = text.line_to_char(516); // Remove the line... text.remove(start_idx..end_idx); // ...and replace it with something better. text.insert(start_idx, "The flowers are... so... dunno.\n"); // Print the changes, along with the previous few lines for context. let start_idx = text.line_to_char(511); let end_idx = text.line_to_char(516); println!("{}", text.slice(start_idx..end_idx)); // Write the file back out to disk. text.write_to(BufWriter::new(File::create("my_great_book.txt")?))?; Ok(()) } ropey-1.6.1/examples/graphemes_iter.rs000064400000000000000000000370101046102023000161520ustar 00000000000000//! This example shows how to implement a grapeheme iterator over the contents //! of a `Rope` or `RopeSlice`. This also serves as a good starting point for //! iterators for other kinds of segementation, such as word boundaries. #![allow(clippy::redundant_field_names)] #![allow(dead_code)] extern crate ropey; extern crate unicode_segmentation; use ropey::{iter::Chunks, RopeSlice}; use unicode_segmentation::{GraphemeCursor, GraphemeIncomplete}; fn main() {} /// An implementation of a graphemes iterator, for iterating over /// the graphemes of a RopeSlice. struct RopeGraphemes<'a> { text: RopeSlice<'a>, chunks: Chunks<'a>, cur_chunk: &'a str, cur_chunk_start: usize, cursor: GraphemeCursor, } impl<'a> RopeGraphemes<'a> { fn new<'b>(slice: &RopeSlice<'b>) -> RopeGraphemes<'b> { let mut chunks = slice.chunks(); let first_chunk = chunks.next().unwrap_or(""); RopeGraphemes { text: *slice, chunks: chunks, cur_chunk: first_chunk, cur_chunk_start: 0, cursor: GraphemeCursor::new(0, slice.len_bytes(), true), } } } impl<'a> Iterator for RopeGraphemes<'a> { type Item = RopeSlice<'a>; fn next(&mut self) -> Option> { let a = self.cursor.cur_cursor(); let b; loop { match self .cursor .next_boundary(self.cur_chunk, self.cur_chunk_start) { Ok(None) => { return None; } Ok(Some(n)) => { b = n; break; } Err(GraphemeIncomplete::NextChunk) => { self.cur_chunk_start += self.cur_chunk.len(); self.cur_chunk = self.chunks.next().unwrap_or(""); } Err(GraphemeIncomplete::PreContext(idx)) => { let (chunk, byte_idx, _, _) = self.text.chunk_at_byte(idx.saturating_sub(1)); self.cursor.provide_context(chunk, byte_idx); } _ => unreachable!(), } } if a < self.cur_chunk_start { let a_char = self.text.byte_to_char(a); let b_char = self.text.byte_to_char(b); Some(self.text.slice(a_char..b_char)) } else { let a2 = a - self.cur_chunk_start; let b2 = b - self.cur_chunk_start; Some((&self.cur_chunk[a2..b2]).into()) } } } #[cfg(test)] #[rustfmt::skip] // Because of the crazy long graphemes mod tests { use super::*; use ropey::Rope; #[test] fn iter_huge_graphemes() { let r = Rope::from_str("Hẽ̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃llõ̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃ wõ̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃rld!"); let mut grph = RopeGraphemes::new(&r.slice(..)); assert_eq!(grph.next().unwrap(), "H"); assert_eq!(grph.next().unwrap(), "ẽ̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃"); assert_eq!(grph.next().unwrap(), "l"); assert_eq!(grph.next().unwrap(), "l"); assert_eq!(grph.next().unwrap(), "õ̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃"); assert_eq!(grph.next().unwrap(), " "); assert_eq!(grph.next().unwrap(), "w"); assert_eq!(grph.next().unwrap(), "õ̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃"); assert_eq!(grph.next().unwrap(), "r"); assert_eq!(grph.next().unwrap(), "l"); assert_eq!(grph.next().unwrap(), "d"); assert_eq!(grph.next().unwrap(), "!"); assert_eq!(grph.next(), None); } #[test] fn iter_regional_symbols() { let r = Rope::from_str("🇬🇧🇯🇵🇺🇸🇫🇷🇷🇺🇨🇳🇩🇪🇪🇸🇬🇧🇯🇵🇺🇸🇫🇷🇷🇺🇨🇳🇩🇪🇪🇸🇬🇧🇯🇵🇺🇸🇫🇷🇷🇺🇨🇳🇩🇪🇪🇸"); let mut grph = RopeGraphemes::new(&r.slice(..)); assert_eq!(grph.next().unwrap(), "🇬🇧"); assert_eq!(grph.next().unwrap(), "🇯🇵"); assert_eq!(grph.next().unwrap(), "🇺🇸"); assert_eq!(grph.next().unwrap(), "🇫🇷"); assert_eq!(grph.next().unwrap(), "🇷🇺"); assert_eq!(grph.next().unwrap(), "🇨🇳"); assert_eq!(grph.next().unwrap(), "🇩🇪"); assert_eq!(grph.next().unwrap(), "🇪🇸"); assert_eq!(grph.next().unwrap(), "🇬🇧"); assert_eq!(grph.next().unwrap(), "🇯🇵"); assert_eq!(grph.next().unwrap(), "🇺🇸"); assert_eq!(grph.next().unwrap(), "🇫🇷"); assert_eq!(grph.next().unwrap(), "🇷🇺"); assert_eq!(grph.next().unwrap(), "🇨🇳"); assert_eq!(grph.next().unwrap(), "🇩🇪"); assert_eq!(grph.next().unwrap(), "🇪🇸"); assert_eq!(grph.next().unwrap(), "🇬🇧"); assert_eq!(grph.next().unwrap(), "🇯🇵"); assert_eq!(grph.next().unwrap(), "🇺🇸"); assert_eq!(grph.next().unwrap(), "🇫🇷"); assert_eq!(grph.next().unwrap(), "🇷🇺"); assert_eq!(grph.next().unwrap(), "🇨🇳"); assert_eq!(grph.next().unwrap(), "🇩🇪"); assert_eq!(grph.next().unwrap(), "🇪🇸"); assert_eq!(grph.next(), None); } } ropey-1.6.1/examples/graphemes_step.rs000064400000000000000000001003401046102023000161570ustar 00000000000000//! This example shows how to implement functions that make queries about //! grapheme boundaries on a `Rope` or `RopeSlice`. This also serves as a //! good starting point for similar functions for other kinds of segementation, //! such as word boundaries. #![allow(dead_code)] extern crate ropey; extern crate unicode_segmentation; use ropey::{str_utils::byte_to_char_idx, RopeSlice}; use unicode_segmentation::{GraphemeCursor, GraphemeIncomplete}; fn main() {} /// Finds the previous grapheme boundary before the given char position. fn prev_grapheme_boundary(slice: &RopeSlice, char_idx: usize) -> usize { // Bounds check debug_assert!(char_idx <= slice.len_chars()); // We work with bytes for this, so convert. let byte_idx = slice.char_to_byte(char_idx); // Get the chunk with our byte index in it. let (mut chunk, mut chunk_byte_idx, mut chunk_char_idx, _) = slice.chunk_at_byte(byte_idx); // Set up the grapheme cursor. let mut gc = GraphemeCursor::new(byte_idx, slice.len_bytes(), true); // Find the previous grapheme cluster boundary. loop { match gc.prev_boundary(chunk, chunk_byte_idx) { Ok(None) => return 0, Ok(Some(n)) => { let tmp = byte_to_char_idx(chunk, n - chunk_byte_idx); return chunk_char_idx + tmp; } Err(GraphemeIncomplete::PrevChunk) => { let (a, b, c, _) = slice.chunk_at_byte(chunk_byte_idx - 1); chunk = a; chunk_byte_idx = b; chunk_char_idx = c; } Err(GraphemeIncomplete::PreContext(n)) => { let ctx_chunk = slice.chunk_at_byte(n - 1).0; gc.provide_context(ctx_chunk, n - ctx_chunk.len()); } _ => unreachable!(), } } } /// Finds the next grapheme boundary after the given char position. fn next_grapheme_boundary(slice: &RopeSlice, char_idx: usize) -> usize { // Bounds check debug_assert!(char_idx <= slice.len_chars()); // We work with bytes for this, so convert. let byte_idx = slice.char_to_byte(char_idx); // Get the chunk with our byte index in it. let (mut chunk, mut chunk_byte_idx, mut chunk_char_idx, _) = slice.chunk_at_byte(byte_idx); // Set up the grapheme cursor. let mut gc = GraphemeCursor::new(byte_idx, slice.len_bytes(), true); // Find the next grapheme cluster boundary. loop { match gc.next_boundary(chunk, chunk_byte_idx) { Ok(None) => return slice.len_chars(), Ok(Some(n)) => { let tmp = byte_to_char_idx(chunk, n - chunk_byte_idx); return chunk_char_idx + tmp; } Err(GraphemeIncomplete::NextChunk) => { chunk_byte_idx += chunk.len(); let (a, _, c, _) = slice.chunk_at_byte(chunk_byte_idx); chunk = a; chunk_char_idx = c; } Err(GraphemeIncomplete::PreContext(n)) => { let ctx_chunk = slice.chunk_at_byte(n - 1).0; gc.provide_context(ctx_chunk, n - ctx_chunk.len()); } _ => unreachable!(), } } } /// Returns whether the given char position is a grapheme boundary. fn is_grapheme_boundary(slice: &RopeSlice, char_idx: usize) -> bool { // Bounds check debug_assert!(char_idx <= slice.len_chars()); // We work with bytes for this, so convert. let byte_idx = slice.char_to_byte(char_idx); // Get the chunk with our byte index in it. let (chunk, chunk_byte_idx, _, _) = slice.chunk_at_byte(byte_idx); // Set up the grapheme cursor. let mut gc = GraphemeCursor::new(byte_idx, slice.len_bytes(), true); // Determine if the given position is a grapheme cluster boundary. loop { match gc.is_boundary(chunk, chunk_byte_idx) { Ok(n) => return n, Err(GraphemeIncomplete::PreContext(n)) => { let (ctx_chunk, ctx_byte_start, _, _) = slice.chunk_at_byte(n - 1); gc.provide_context(ctx_chunk, ctx_byte_start); } _ => unreachable!(), } } } #[cfg(test)] #[rustfmt::skip] // Because of the crazy long graphemes mod tests { use super::*; use ropey::Rope; #[test] fn prev_grapheme() { let r = Rope::from_str("Hẽ̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃llõ̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃ wõ̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃rld!"); let mut idx = r.len_chars(); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 2705); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 2704); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 2703); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 2702); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 984); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 983); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 982); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 446); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 445); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 444); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 1); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 0); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 0); } #[test] fn next_grapheme() { let r = Rope::from_str("Hẽ̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃llõ̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃ wõ̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃rld!"); let mut idx = 0; idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 1); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 444); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 445); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 446); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 982); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 983); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 984); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 2702); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 2703); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 2704); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 2705); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 2706); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 2706); } #[test] fn is_grapheme_boundary_01() { let r = Rope::from_str("Hẽ̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃llõ̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃ wõ̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃̃rld!"); assert_eq!(is_grapheme_boundary(&r.slice(..), 0), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 1), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 2), false); assert_eq!(is_grapheme_boundary(&r.slice(..), 200), false); assert_eq!(is_grapheme_boundary(&r.slice(..), 443), false); assert_eq!(is_grapheme_boundary(&r.slice(..), 444), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 445), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 446), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 447), false); assert_eq!(is_grapheme_boundary(&r.slice(..), 600), false); assert_eq!(is_grapheme_boundary(&r.slice(..), 981), false); assert_eq!(is_grapheme_boundary(&r.slice(..), 982), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 983), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 984), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 985), false); assert_eq!(is_grapheme_boundary(&r.slice(..), 1400), false); assert_eq!(is_grapheme_boundary(&r.slice(..), 2701), false); assert_eq!(is_grapheme_boundary(&r.slice(..), 2702), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 2703), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 2704), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 2705), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 2706), true); } #[test] fn prev_grapheme_regional_symbols() { let r = Rope::from_str("🇬🇧🇯🇵🇺🇸🇫🇷🇷🇺🇨🇳🇩🇪🇪🇸🇬🇧🇯🇵🇺🇸🇫🇷🇷🇺🇨🇳🇩🇪🇪🇸🇬🇧🇯🇵🇺🇸🇫🇷🇷🇺🇨🇳🇩🇪🇪"); let mut idx = r.len_chars(); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 46); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 44); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 42); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 40); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 38); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 36); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 34); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 32); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 30); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 28); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 26); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 24); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 22); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 20); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 18); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 16); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 14); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 12); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 10); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 8); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 6); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 4); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 2); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 0); idx = prev_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 0); } #[test] fn next_grapheme_regional_symbols() { let r = Rope::from_str("🇬🇧🇯🇵🇺🇸🇫🇷🇷🇺🇨🇳🇩🇪🇪🇸🇬🇧🇯🇵🇺🇸🇫🇷🇷🇺🇨🇳🇩🇪🇪🇸🇬🇧🇯🇵🇺🇸🇫🇷🇷🇺🇨🇳🇩🇪🇪"); let mut idx = 0; idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 2); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 4); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 6); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 8); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 10); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 12); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 14); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 16); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 18); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 20); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 22); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 24); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 26); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 28); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 30); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 32); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 34); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 36); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 38); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 40); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 42); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 44); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 46); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 47); idx = next_grapheme_boundary(&r.slice(..), idx); assert_eq!(idx, 47); } #[test] fn is_grapheme_boundary_regional_symbols() { let r = Rope::from_str("🇬🇧🇯🇵🇺🇸🇫🇷🇷🇺🇨🇳🇩🇪🇪🇸🇬🇧🇯🇵🇺🇸🇫🇷🇷🇺🇨🇳🇩🇪🇪🇸🇬🇧🇯🇵🇺🇸🇫🇷🇷🇺🇨🇳🇩🇪🇪"); assert_eq!(is_grapheme_boundary(&r.slice(..), 0), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 1), false); assert_eq!(is_grapheme_boundary(&r.slice(..), 2), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 3), false); assert_eq!(is_grapheme_boundary(&r.slice(..), 4), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 5), false); assert_eq!(is_grapheme_boundary(&r.slice(..), 6), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 7), false); assert_eq!(is_grapheme_boundary(&r.slice(..), 8), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 9), false); assert_eq!(is_grapheme_boundary(&r.slice(..), 10), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 11), false); assert_eq!(is_grapheme_boundary(&r.slice(..), 12), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 13), false); assert_eq!(is_grapheme_boundary(&r.slice(..), 14), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 15), false); assert_eq!(is_grapheme_boundary(&r.slice(..), 16), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 17), false); assert_eq!(is_grapheme_boundary(&r.slice(..), 18), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 19), false); assert_eq!(is_grapheme_boundary(&r.slice(..), 20), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 21), false); assert_eq!(is_grapheme_boundary(&r.slice(..), 22), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 23), false); assert_eq!(is_grapheme_boundary(&r.slice(..), 24), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 25), false); assert_eq!(is_grapheme_boundary(&r.slice(..), 26), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 27), false); assert_eq!(is_grapheme_boundary(&r.slice(..), 28), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 29), false); assert_eq!(is_grapheme_boundary(&r.slice(..), 30), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 31), false); assert_eq!(is_grapheme_boundary(&r.slice(..), 32), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 33), false); assert_eq!(is_grapheme_boundary(&r.slice(..), 34), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 35), false); assert_eq!(is_grapheme_boundary(&r.slice(..), 36), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 37), false); assert_eq!(is_grapheme_boundary(&r.slice(..), 38), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 39), false); assert_eq!(is_grapheme_boundary(&r.slice(..), 40), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 41), false); assert_eq!(is_grapheme_boundary(&r.slice(..), 42), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 43), false); assert_eq!(is_grapheme_boundary(&r.slice(..), 44), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 45), false); assert_eq!(is_grapheme_boundary(&r.slice(..), 46), true); assert_eq!(is_grapheme_boundary(&r.slice(..), 47), true); } } ropey-1.6.1/examples/read_latin_1.rs000064400000000000000000000027001046102023000154740ustar 00000000000000//! Example of decoding from another text encoding on-the-fly while reading. //! In this case, we're decoding from ISO/IEC 8859-1, which conveniently //! happens to map 1-to-1 to the first 256 unicode scalar values. extern crate ropey; use std::fs::File; use std::io; use std::io::Read; use ropey::RopeBuilder; fn main() { // Get filepath from commandline let filepath = if std::env::args().count() > 1 { std::env::args().nth(1).unwrap() } else { eprintln!( "You must pass a filepath! Only recieved {} arguments.", std::env::args().count() ); panic!() }; // Get everything set up to begin reading and decoding. let mut buf = vec![0u8; 1 << 14]; // Buffer for raw bytes. let mut buf_str = String::with_capacity(1 << 14); // Buffer for decoded utf8. let mut builder = RopeBuilder::new(); let mut file = io::BufReader::new(File::open(&filepath).unwrap()); // Read the data in chunks, decoding and appending to the rope builder // as we go. // (Note: in real code you should handle errors from the reader!) while let Ok(n) = file.read(&mut buf) { if n == 0 { break; } // Decode and append the chunk to the rope builder. buf_str.clear(); for &byte in &buf[..n] { buf_str.push(byte as char); } builder.append(&buf_str); } // Build rope. let _rope = builder.finish(); } ropey-1.6.1/examples/search_and_replace.rs000064400000000000000000000173371046102023000167500ustar 00000000000000//! Example of basic search-and-replace functionality implemented on top //! of Ropey. //! //! Usage: //! search_and_replace //! //! The file contents with the search-and-replace performed on it is sent to //! stdout. #![allow(clippy::redundant_field_names)] extern crate ropey; use std::fs::File; use std::io; use ropey::{iter::Chars, Rope, RopeSlice}; fn main() { // Get arguments from commandline let (search_pattern, replacement_text, filepath) = if std::env::args().count() > 3 { ( std::env::args().nth(1).unwrap(), std::env::args().nth(2).unwrap(), std::env::args().nth(3).unwrap(), ) } else { eprintln!( "Usage:\n search_and_replace " ); return; }; // Load file contents into a rope. let mut text = Rope::from_reader(io::BufReader::new(File::open(&filepath).unwrap())).expect("Cannot read file: either it doesn't exist, file permissions don't allow reading, or is not utf8 text."); // Do the search-and-replace. search_and_replace(&mut text, &search_pattern, &replacement_text); // Print the new text to stdout. println!("{}", text); } /// Searches the rope for `search_pattern` and replaces all matches with /// `replacement_text`. /// /// There are several ways this could be done: /// /// 1. Clone the rope and then do the search on the original while replacing /// on the clone. This isn't as awful as it sounds because the clone /// operation is constant-time and the two ropes will share most of their /// storage in typical cases. However, this probably isn't the best /// general solution because it will use a lot of additional space if a /// large percentage of the text is being replaced. /// /// 2. A two-stage approach: first find and collect all the matches, then /// do the replacements on the original rope. This is a good solution /// when a relatively small number of matches are expected. However, if /// there are a large number of matches then the space to store the /// matches themselves can become large. /// /// 3. A piece-meal approach: search for the first match, replace it, then /// restart the search from there, repeat. This is a good solution for /// memory-constrained situations. However, computationally it is likely /// the most expensive when there are a large number of matches and there /// are costs associated with repeatedly restarting the search. /// /// 4. Combine approaches #2 and #3: collect a fixed number of matches and /// replace them, then collect another batch of matches and replace them, /// and so on. This is probably the best general solution, because it /// combines the best of both #2 and #3: it allows you to collect the /// matches in a bounded amount of space, and any costs associated with /// restarting the search are amortized over multiple matches. /// /// In this implementation we take approach #4 because it seems the /// all-around best. fn search_and_replace(rope: &mut Rope, search_pattern: &str, replacement_text: &str) { const BATCH_SIZE: usize = 256; let replacement_text_len = replacement_text.chars().count(); let mut head = 0; // Keep track of where we are between searches let mut matches = Vec::with_capacity(BATCH_SIZE); loop { // Collect the next batch of matches. Note that we don't use // `Iterator::collect()` to collect the batch because we want to // re-use the same Vec to avoid unnecessary allocations. matches.clear(); for m in SearchIter::from_rope_slice(&rope.slice(head..), search_pattern).take(BATCH_SIZE) { matches.push(m); } // If there are no matches, we're done! if matches.is_empty() { break; } // Replace the collected matches. let mut index_diff: isize = 0; for &(start, end) in matches.iter() { // Get the properly offset indices. let start_d = (head as isize + start as isize + index_diff) as usize; let end_d = (head as isize + end as isize + index_diff) as usize; // Do the replacement. rope.remove(start_d..end_d); rope.insert(start_d, replacement_text); // Update the index offset. let match_len = (end - start) as isize; index_diff = index_diff - match_len + replacement_text_len as isize; } // Update head for next iteration. head = (head as isize + index_diff + matches.last().unwrap().1 as isize) as usize; } } /// An iterator over simple textual matches in a RopeSlice. /// /// This implementation is somewhat naive, and could be sped up by using a /// more sophisticated text searching algorithm such as Boyer-Moore or /// Knuth-Morris-Pratt. /// /// The important thing, however, is the interface. For example, a regex /// implementation providing an equivalent interface could easily be dropped /// in, and the search-and-replace function above would work with it quite /// happily. struct SearchIter<'a> { char_iter: Chars<'a>, search_pattern: &'a str, search_pattern_char_len: usize, cur_index: usize, // The current char index of the search head. possible_matches: Vec>, // Tracks where we are in the search pattern for the current possible matches. } impl<'a> SearchIter<'a> { fn from_rope_slice<'b>(slice: &'b RopeSlice, search_pattern: &'b str) -> SearchIter<'b> { assert!( !search_pattern.is_empty(), "Can't search using an empty search pattern." ); SearchIter { char_iter: slice.chars(), search_pattern: search_pattern, search_pattern_char_len: search_pattern.chars().count(), cur_index: 0, possible_matches: Vec::new(), } } } impl<'a> Iterator for SearchIter<'a> { type Item = (usize, usize); // Return the start/end char indices of the next match. fn next(&mut self) -> Option<(usize, usize)> { #[allow(clippy::while_let_on_iterator)] while let Some(next_char) = self.char_iter.next() { self.cur_index += 1; // Push new potential match, for a possible match starting at the // current char. self.possible_matches.push(self.search_pattern.chars()); // Check the rope's char against the next character in each of // the potential matches, removing the potential matches that // don't match. We're using indexing instead of iteration here // so that we can remove the possible matches as we go. let mut i = 0; while i < self.possible_matches.len() { let pattern_char = self.possible_matches[i].next().unwrap(); if next_char == pattern_char { if self.possible_matches[i].clone().next() == None { // We have a match! Reset possible matches and // return the successful match's char indices. let char_match_range = ( self.cur_index - self.search_pattern_char_len, self.cur_index, ); self.possible_matches.clear(); return Some(char_match_range); } else { // Match isn't complete yet, move on to the next. i += 1; } } else { // Doesn't match, remove it. let _ = self.possible_matches.swap_remove(i); } } } None } } ropey-1.6.1/examples/simple_buffer.rs000064400000000000000000000031111046102023000157710ustar 00000000000000#![allow(clippy::redundant_field_names)] #![allow(dead_code)] extern crate ropey; use std::fs::File; use std::io; use ropey::iter::{Bytes, Chars, Chunks, Lines}; use ropey::{Rope, RopeSlice}; struct TextBuffer { text: Rope, path: String, dirty: bool, } impl TextBuffer { fn from_path(path: &str) -> io::Result { let text = Rope::from_reader(&mut io::BufReader::new(File::open(&path)?))?; Ok(TextBuffer { text: text, path: path.to_string(), dirty: false, }) } fn get_line(&self, idx: usize) -> RopeSlice { self.text.line(idx) } fn bytes(&self) -> Bytes { self.text.bytes() } fn chars(&self) -> Chars { self.text.chars() } fn lines(&self) -> Lines { self.text.lines() } fn chunks(&self) -> Chunks { self.text.chunks() } fn edit(&mut self, start: usize, end: usize, text: &str) { if start != end { self.text.remove(start..end); } if !text.is_empty() { self.text.insert(start, text); } self.dirty = true; } } fn main() { // Get filepath from commandline let filepath = if std::env::args().count() > 1 { std::env::args().nth(1).unwrap() } else { println!( "You must pass a filepath! Only recieved {} arguments.", std::env::args().count() ); panic!() }; let mut buf = TextBuffer::from_path(&filepath).unwrap(); buf.edit(3, 5, "Hello!"); println!("{}", buf.get_line(2)); } ropey-1.6.1/src/crlf.rs000064400000000000000000000140671046102023000130620ustar 00000000000000/// Returns whether the given byte index in `text` is a valid /// splitting point. Valid splitting point in this case means /// that it _is_ a utf8 code point boundary and _is not_ the /// middle of a CRLF pair. #[inline] pub fn is_break(byte_idx: usize, text: &[u8]) -> bool { debug_assert!(byte_idx <= text.len()); if byte_idx == 0 || byte_idx == text.len() { true } else { (text[byte_idx] >> 6 != 0b10) && ((text[byte_idx - 1] != 0x0D) | (text[byte_idx] != 0x0A)) } } /// Returns whether the seam between `left` and `right` is a valid /// splitting point. Valid splitting point in this case means /// that it _is_ a utf8 code point boundary and _is not_ the middle /// of a CRLF pair. #[inline] pub fn seam_is_break(left: &[u8], right: &[u8]) -> bool { debug_assert!(!left.is_empty() && !right.is_empty()); (right[0] >> 6 != 0b10) && ((left[left.len() - 1] != 0x0D) | (right[0] != 0x0A)) } /// Returns the segment break before (but not including) the given byte /// boundary. /// /// This will return back the passed byte boundary if it is at the start /// of the string. #[inline] pub fn prev_break(byte_idx: usize, text: &[u8]) -> usize { // Bounds check debug_assert!(byte_idx <= text.len()); if byte_idx == 0 { 0 } else { let mut boundary_idx = byte_idx - 1; while !is_break(boundary_idx, text) { boundary_idx -= 1; } boundary_idx } } /// Returns the segment break after (but not including) the given byte /// boundary. /// /// This will return back the passed byte boundary if it is at the end of /// the string. #[inline] pub fn next_break(byte_idx: usize, text: &[u8]) -> usize { // Bounds check debug_assert!(byte_idx <= text.len()); if byte_idx == text.len() { text.len() } else { let mut boundary_idx = byte_idx + 1; while !is_break(boundary_idx, text) { boundary_idx += 1; } boundary_idx } } /// Finds the segment break nearest to the given byte that is not the /// left or right edge of the text. /// /// There is only one circumstance where the left or right edge will be /// returned: if the entire text is a single unbroken segment, then the /// right edge of the text is returned. #[inline] pub fn nearest_internal_break(byte_idx: usize, text: &[u8]) -> usize { // Bounds check debug_assert!(byte_idx <= text.len()); // Find the two nearest segment boundaries let left = if is_break(byte_idx, text) && byte_idx != text.len() { byte_idx } else { prev_break(byte_idx, text) }; let right = next_break(byte_idx, text); // Otherwise, return the closest of left and right that isn't the // start or end of the string if left == 0 || (right != text.len() && (byte_idx - left) >= (right - byte_idx)) { return right; } else { return left; } } #[inline] pub fn find_good_split(byte_idx: usize, text: &[u8], bias_left: bool) -> usize { // Bounds check debug_assert!(byte_idx <= text.len()); if is_break(byte_idx, text) { byte_idx } else { let prev = prev_break(byte_idx, text); let next = next_break(byte_idx, text); if bias_left { if prev > 0 { prev } else { next } } else { #[allow(clippy::collapsible_if)] // More readable this way if next < text.len() { next } else { prev } } } } //=========================================================================== #[cfg(test)] mod tests { use super::*; #[test] fn crlf_segmenter_01() { let text = b"Hello world!\r\nHow's it going?"; assert!(is_break(0, b"")); assert!(is_break(0, text)); assert!(is_break(12, text)); assert!(!is_break(13, text)); assert!(is_break(14, text)); assert!(is_break(19, text)); } #[test] fn crlf_segmenter_02() { let l = b"Hello world!\r"; let r = b"\nHow's it going?"; assert!(!seam_is_break(l, r)); assert!(!seam_is_break(l, b"\n")); assert!(!seam_is_break(b"\r", r)); assert!(!seam_is_break(b"\r", b"\n")); assert!(seam_is_break(r, l)); assert!(seam_is_break(b"\n", b"\r")); } #[test] fn nearest_internal_break_01() { let text = b"Hello world!"; assert_eq!(1, nearest_internal_break(0, text)); assert_eq!(6, nearest_internal_break(6, text)); assert_eq!(11, nearest_internal_break(12, text)); } #[test] fn nearest_internal_break_02() { let text = b"Hello\r\n world!"; assert_eq!(5, nearest_internal_break(5, text)); assert_eq!(7, nearest_internal_break(6, text)); assert_eq!(7, nearest_internal_break(7, text)); } #[test] fn nearest_internal_break_03() { let text = b"\r\nHello world!\r\n"; assert_eq!(2, nearest_internal_break(0, text)); assert_eq!(2, nearest_internal_break(1, text)); assert_eq!(2, nearest_internal_break(2, text)); assert_eq!(14, nearest_internal_break(14, text)); assert_eq!(14, nearest_internal_break(15, text)); assert_eq!(14, nearest_internal_break(16, text)); } #[test] fn nearest_internal_break_04() { let text = b"\r\n"; assert_eq!(2, nearest_internal_break(0, text)); assert_eq!(2, nearest_internal_break(1, text)); assert_eq!(2, nearest_internal_break(2, text)); } #[test] fn is_break_01() { let text = b"\n\r\n\r\n\r\n\r\n\r\n\r"; assert!(is_break(0, text)); assert!(is_break(12, text)); assert!(is_break(3, text)); assert!(!is_break(6, text)); } #[test] fn seam_is_break_01() { let text1 = b"\r\n\r\n\r\n"; let text2 = b"\r\n\r\n"; assert!(seam_is_break(text1, text2)); } #[test] fn seam_is_break_02() { let text1 = b"\r\n\r\n\r"; let text2 = b"\n\r\n\r\n"; assert!(!seam_is_break(text1, text2)); } } ropey-1.6.1/src/iter.rs000064400000000000000000003335101046102023000130740ustar 00000000000000//! Iterators over a `Rope`'s data. //! //! The iterators in Ropey can be created from both `Rope`s and `RopeSlice`s. //! When created from a `RopeSlice`, they iterate over only the data that the //! `RopeSlice` refers to. For the `Lines` and `Chunks` iterators, the data //! of the first and last yielded item will be correctly truncated to match //! the bounds of the `RopeSlice`. //! //! # Reverse iteration //! //! All iterators in Ropey operate as a cursor that can move both forwards //! and backwards over its contents. This can be accomplished via the //! `next()` and `prev()` methods on each iterator, or by using the `reverse()` //! or `reversed()` methods to change the iterator's direction. //! //! Conceptually, an iterator in Ropey is always positioned *between* the //! elements it iterates over, and returns an element when it jumps over it //! via the `next()` or `prev()` methods. //! //! For example, given the text `"abc"` and a `Chars` iterator starting at the //! beginning of the text, you would get the following sequence of states and //! return values by repeatedly calling `next()` (the vertical bar represents //! the position of the iterator): //! //! 0. `|abc` //! 1. `a|bc` -> `Some('a')` //! 2. `ab|c` -> `Some('b')` //! 3. `abc|` -> `Some('c')` //! 4. `abc|` -> `None` //! //! The `prev()` method operates identically, except moving in the opposite //! direction. And `reverse()` simply swaps the behavior of `prev()` and //! `next()`. //! //! # Creating iterators at any position //! //! Iterators in Ropey can be created starting at any position in the text. //! This is accomplished with the various `bytes_at()`, `chars_at()`, etc. //! methods of `Rope` and `RopeSlice`. //! //! When an iterator is created this way, it is positioned such that a call to //! `next()` will return the specified element, and a call to `prev()` will //! return the element just before the specified one. //! //! Importantly, iterators created this way still have access to the entire //! contents of the `Rope`/`RopeSlice` they were created from—the //! contents before the specified position is not truncated. For example, you //! can create a `Chars` iterator starting at the end of a `Rope`, and then //! use the `prev()` method to iterate backwards over all of that `Rope`'s //! chars. //! //! # A possible point of confusion //! //! The Rust standard library has an iterator trait `DoubleEndedIterator` with //! a method `rev()`. While this method's name is very similar to Ropey's //! `reverse()` method, its behavior is very different. //! //! `DoubleEndedIterator` actually provides two iterators: one starting at each //! end of the collection, moving in opposite directions towards each other. //! Calling `rev()` switches between those two iterators, changing not only the //! direction of iteration but also its current position in the collection. //! //! The `reverse()` method on Ropey's iterators, on the other hand, reverses //! the direction of the iterator in-place, without changing its position in //! the text. use std::str; use std::sync::Arc; use crate::slice::{RSEnum, RopeSlice}; use crate::str_utils::{ byte_to_line_idx, char_to_byte_idx, count_chars, count_utf16_surrogates, ends_with_line_break, last_line_start_byte_idx, line_to_byte_idx, trim_line_break, }; use crate::tree::{Count, Node, TextInfo}; //========================================================== /// An iterator over a `Rope`'s bytes. #[derive(Debug, Clone)] pub struct Bytes<'a> { chunk_iter: Chunks<'a>, cur_chunk: &'a [u8], byte_idx: usize, last_call_was_prev_impl: bool, bytes_total: usize, bytes_remaining: usize, is_reversed: bool, } impl<'a> Bytes<'a> { pub(crate) fn new(node: &Arc) -> Bytes { let mut chunk_iter = Chunks::new(node); let cur_chunk = if let Some(chunk) = chunk_iter.next() { chunk } else { "" }; Bytes { chunk_iter: chunk_iter, cur_chunk: cur_chunk.as_bytes(), byte_idx: 0, last_call_was_prev_impl: false, bytes_total: node.text_info().bytes as usize, bytes_remaining: node.text_info().bytes as usize, is_reversed: false, } } #[inline(always)] pub(crate) fn new_with_range( node: &Arc, byte_idx_range: (usize, usize), char_idx_range: (usize, usize), line_break_idx_range: (usize, usize), ) -> Bytes { Bytes::new_with_range_at( node, byte_idx_range.0, byte_idx_range, char_idx_range, line_break_idx_range, ) } pub(crate) fn new_with_range_at( node: &Arc, at_byte: usize, byte_idx_range: (usize, usize), char_idx_range: (usize, usize), line_break_idx_range: (usize, usize), ) -> Bytes { let (mut chunk_iter, mut chunk_byte_start, _, _) = Chunks::new_with_range_at_byte( node, at_byte, byte_idx_range, char_idx_range, line_break_idx_range, ); let cur_chunk = if byte_idx_range.0 == byte_idx_range.1 { "" } else if at_byte < byte_idx_range.1 { chunk_iter.next().unwrap() } else { let chunk = chunk_iter.prev().unwrap(); chunk_iter.next(); chunk_byte_start -= chunk.len(); chunk }; Bytes { chunk_iter: chunk_iter, cur_chunk: cur_chunk.as_bytes(), byte_idx: at_byte - chunk_byte_start, last_call_was_prev_impl: false, bytes_total: byte_idx_range.1 - byte_idx_range.0, bytes_remaining: byte_idx_range.1 - at_byte, is_reversed: false, } } #[inline(always)] pub(crate) fn from_str(text: &str) -> Bytes { Bytes::from_str_at(text, 0) } pub(crate) fn from_str_at(text: &str, byte_idx: usize) -> Bytes { let mut chunk_iter = Chunks::from_str(text, false); let cur_chunk = if let Some(chunk) = chunk_iter.next() { chunk } else { "" }; Bytes { chunk_iter: chunk_iter, cur_chunk: cur_chunk.as_bytes(), byte_idx: byte_idx, last_call_was_prev_impl: false, bytes_total: text.len(), bytes_remaining: text.len() - byte_idx, is_reversed: false, } } /// Reverses the direction of the iterator in-place. /// /// In other words, swaps the behavior of [`prev()`](Bytes::prev()) /// and [`next()`](Bytes::next()). #[inline] pub fn reverse(&mut self) { self.is_reversed = !self.is_reversed; } /// Same as `reverse()`, but returns itself. /// /// This is useful when chaining iterator methods: /// /// ```rust /// # use ropey::Rope; /// # let rope = Rope::from_str("Hello there\n world!\n"); /// // Enumerate the rope's bytes in reverse, starting from the end. /// for (i, b) in rope.bytes_at(rope.len_bytes()).reversed().enumerate() { /// println!("{} {}", i, b); /// # assert_eq!(b, rope.byte(rope.len_bytes() - i - 1)); /// } #[inline] #[must_use] pub fn reversed(mut self) -> Bytes<'a> { self.reverse(); self } /// Advances the iterator backwards and returns the previous value. /// /// Runs in amortized O(1) time and worst-case O(log N) time. #[inline(always)] pub fn prev(&mut self) -> Option { if !self.is_reversed { self.prev_impl() } else { self.next_impl() } } #[inline] fn prev_impl(&mut self) -> Option { // Put us back into a "prev" progression. if !self.last_call_was_prev_impl { self.chunk_iter.prev(); self.last_call_was_prev_impl = true; } // Progress the chunks iterator back if needed. if self.byte_idx == 0 { if let Some(chunk) = self.chunk_iter.prev() { self.cur_chunk = chunk.as_bytes(); self.byte_idx = self.cur_chunk.len(); } else { return None; } } // Progress the byte counts and return the previous byte. self.byte_idx -= 1; self.bytes_remaining += 1; return Some(self.cur_chunk[self.byte_idx]); } #[inline] fn next_impl(&mut self) -> Option { // Put us back into a "next" progression. if self.last_call_was_prev_impl { self.chunk_iter.next(); self.last_call_was_prev_impl = false; } // Progress the chunks iterator forward if needed. if self.byte_idx >= self.cur_chunk.len() { if let Some(chunk) = self.chunk_iter.next() { self.cur_chunk = chunk.as_bytes(); self.byte_idx = 0; } else { return None; } } // Progress the byte counts and return the next byte. let byte = self.cur_chunk[self.byte_idx]; self.byte_idx += 1; self.bytes_remaining -= 1; return Some(byte); } } impl<'a> Iterator for Bytes<'a> { type Item = u8; /// Advances the iterator forward and returns the next value. /// /// Runs in amortized O(1) time and worst-case O(log N) time. #[inline(always)] fn next(&mut self) -> Option { if !self.is_reversed { self.next_impl() } else { self.prev_impl() } } fn size_hint(&self) -> (usize, Option) { let remaining = if !self.is_reversed { self.bytes_remaining } else { self.bytes_total - self.bytes_remaining }; (remaining, Some(remaining)) } } impl<'a> ExactSizeIterator for Bytes<'a> {} //========================================================== /// An iterator over a `Rope`'s chars. #[derive(Debug, Clone)] pub struct Chars<'a> { chunk_iter: Chunks<'a>, cur_chunk: &'a str, byte_idx: usize, last_call_was_prev_impl: bool, chars_total: usize, chars_remaining: usize, is_reversed: bool, } impl<'a> Chars<'a> { pub(crate) fn new(node: &Arc) -> Chars { let mut chunk_iter = Chunks::new(node); let cur_chunk = if let Some(chunk) = chunk_iter.next() { chunk } else { "" }; Chars { chunk_iter: chunk_iter, cur_chunk: cur_chunk, byte_idx: 0, last_call_was_prev_impl: false, chars_total: node.text_info().chars as usize, chars_remaining: node.text_info().chars as usize, is_reversed: false, } } #[inline(always)] pub(crate) fn new_with_range( node: &Arc, byte_idx_range: (usize, usize), char_idx_range: (usize, usize), line_break_idx_range: (usize, usize), ) -> Chars { Chars::new_with_range_at( node, char_idx_range.0, byte_idx_range, char_idx_range, line_break_idx_range, ) } pub(crate) fn new_with_range_at( node: &Arc, at_char: usize, byte_idx_range: (usize, usize), char_idx_range: (usize, usize), line_break_idx_range: (usize, usize), ) -> Chars { let (mut chunk_iter, _, mut chunk_char_start, _) = Chunks::new_with_range_at_char( node, at_char, byte_idx_range, char_idx_range, line_break_idx_range, ); let cur_chunk = if char_idx_range.0 == char_idx_range.1 { "" } else if at_char < char_idx_range.1 { chunk_iter.next().unwrap() } else { let chunk = chunk_iter.prev().unwrap(); chunk_iter.next(); chunk_char_start = (node.get_chunk_at_char(at_char - 1).1.chars as usize).max(char_idx_range.0); chunk }; Chars { chunk_iter: chunk_iter, cur_chunk: cur_chunk, byte_idx: char_to_byte_idx(cur_chunk, at_char - chunk_char_start), last_call_was_prev_impl: false, chars_total: char_idx_range.1 - char_idx_range.0, chars_remaining: char_idx_range.1 - at_char, is_reversed: false, } } #[inline(always)] pub(crate) fn from_str(text: &str) -> Chars { Chars::from_str_at(text, 0) } pub(crate) fn from_str_at(text: &str, char_idx: usize) -> Chars { let mut chunk_iter = Chunks::from_str(text, false); let cur_chunk = if let Some(chunk) = chunk_iter.next() { chunk } else { "" }; let start_byte_idx = char_to_byte_idx(text, char_idx); let chars_remaining = count_chars(&text[start_byte_idx..]); Chars { chunk_iter: chunk_iter, cur_chunk: cur_chunk, byte_idx: start_byte_idx, last_call_was_prev_impl: false, chars_total: chars_remaining + count_chars(&text[..start_byte_idx]), chars_remaining: chars_remaining, is_reversed: false, } } /// Reverses the direction of the iterator in-place. /// /// In other words, swaps the behavior of [`prev()`](Chars::prev()) /// and [`next()`](Chars::next()). #[inline] pub fn reverse(&mut self) { self.is_reversed = !self.is_reversed; } /// Same as `reverse()`, but returns itself. /// /// This is useful when chaining iterator methods: /// /// ```rust /// # use ropey::Rope; /// # let rope = Rope::from_str("Hello there\n world!\n"); /// // Enumerate the rope's chars in reverse, starting from the end. /// for (i, ch) in rope.chars_at(rope.len_chars()).reversed().enumerate() { /// println!("{} {}", i, ch); /// # assert_eq!(ch, rope.char(rope.len_chars() - i - 1)); /// } #[inline] #[must_use] pub fn reversed(mut self) -> Chars<'a> { self.reverse(); self } /// Advances the iterator backwards and returns the previous value. /// /// Runs in amortized O(1) time and worst-case O(log N) time. #[inline(always)] pub fn prev(&mut self) -> Option { if !self.is_reversed { self.prev_impl() } else { self.next_impl() } } #[inline] fn prev_impl(&mut self) -> Option { // Put us back into a "prev" progression. if !self.last_call_was_prev_impl { self.chunk_iter.prev(); self.last_call_was_prev_impl = true; } // Progress the chunks iterator back if needed. if self.byte_idx == 0 { if let Some(chunk) = self.chunk_iter.prev() { self.cur_chunk = chunk; self.byte_idx = self.cur_chunk.len(); } else { return None; } } // Find the previous char boundary, updating counters as needed, and // return the previous char. self.byte_idx -= 1; while !self.cur_chunk.is_char_boundary(self.byte_idx) { self.byte_idx -= 1; } self.chars_remaining += 1; return (&self.cur_chunk[self.byte_idx..]).chars().next(); } #[inline] fn next_impl(&mut self) -> Option { // Put us back into a "next" progression. if self.last_call_was_prev_impl { self.chunk_iter.next(); self.last_call_was_prev_impl = false; } // Progress the chunks iterator forward if needed. if self.byte_idx >= self.cur_chunk.len() { if let Some(chunk) = self.chunk_iter.next() { self.cur_chunk = chunk; self.byte_idx = 0; } else { return None; } } // Find the next char boundary, updating counters as needed, and // return the next char. let start = self.byte_idx; self.byte_idx += 1; while !self.cur_chunk.is_char_boundary(self.byte_idx) { self.byte_idx += 1; } self.chars_remaining -= 1; return (&self.cur_chunk[start..]).chars().next(); } } impl<'a> Iterator for Chars<'a> { type Item = char; /// Advances the iterator forward and returns the next value. /// /// Runs in amortized O(1) time and worst-case O(log N) time. #[inline(always)] fn next(&mut self) -> Option { if !self.is_reversed { self.next_impl() } else { self.prev_impl() } } fn size_hint(&self) -> (usize, Option) { let remaining = if !self.is_reversed { self.chars_remaining } else { self.chars_total - self.chars_remaining }; (remaining, Some(remaining)) } } impl<'a> ExactSizeIterator for Chars<'a> {} //========================================================== /// An iterator over a `Rope`'s lines. /// /// The returned lines include the line break at the end, if any. /// /// The last line is returned even if blank, in which case it /// is returned as an empty slice. #[derive(Debug, Clone)] pub struct Lines<'a> { iter: LinesEnum<'a>, is_reversed: bool, /// The content of the current tree leaf. text: &'a str, /// The total byte position of the iterator. byte_idx: usize, at_end: bool, line_idx: usize, total_lines: usize, } #[derive(Debug, Clone)] enum LinesEnum<'a> { Full { /// A stack of nodes that represents the current tree position. /// This stack contains only internal nodes, the leaf text is /// stored in the `Lines::text` field instead. /// Each entry contains a tree node and the index of the current /// child that is stored next on the stack (or the index of the /// leaf) for the last node. node_stack: Vec<(&'a Arc, usize)>, /// The position within the current leaf (`Lines::text`). leaf_byte_idx: u32, /// The total number of bytes this iterator can traverse. total_bytes: usize, }, Light, } impl<'a> Lines<'a> { #[inline(always)] pub(crate) fn new(node: &Arc) -> Lines { let info = node.text_info(); Lines::new_with_range_at( node, 0, (0, info.bytes as usize), (0, info.line_breaks as usize + 1), ) } #[inline(always)] pub(crate) fn new_with_range( node: &Arc, byte_idx_range: (usize, usize), line_idx_range: (usize, usize), ) -> Lines { Lines::new_with_range_at(node, line_idx_range.0, byte_idx_range, line_idx_range) } pub(crate) fn new_with_range_at( node: &Arc, line: usize, byte_idx_range: (usize, usize), line_idx_range: (usize, usize), ) -> Lines { debug_assert!(node.is_char_boundary(byte_idx_range.0)); debug_assert!(node.is_char_boundary(byte_idx_range.1)); debug_assert!(line >= line_idx_range.0); // For convenience/readability. let total_lines = line_idx_range.1 - line_idx_range.0; // Special-case: empty slice/rope. if byte_idx_range.0 == byte_idx_range.1 { return Lines { iter: LinesEnum::Light, text: "", at_end: false, is_reversed: false, byte_idx: 0, line_idx: 0, total_lines: 1, }; } // Special-case: root is a leaf. Return light version of the iterator. if node.is_leaf() { let text = &node.leaf_text()[byte_idx_range.0..byte_idx_range.1]; return Lines::from_str_at(text, line - line_idx_range.0, total_lines); } // Common case. Traverse into the tree to build the iterator. let mut start_byte_idx = byte_idx_range.0; let mut end_byte_idx = byte_idx_range.1; let mut line_idx = line; let mut chunk_byte_start = 0; let mut node_stack: Vec<(&Arc, usize)> = Vec::new(); let mut node_ref = node; loop { match **node_ref { // Recursively traverse into whichever child has the target line break, // bounded by the start and end bytes of the slice/rope. Node::Internal(ref children) => { // Find the appropriate child. let (child_i, acc) = children.search_by(|_, end_info| { if (end_info.bytes as usize) >= end_byte_idx { true } else if line_idx <= (end_info.line_breaks as usize) { (end_info.bytes as usize) > start_byte_idx } else { false } }); // Update tracking info. start_byte_idx = start_byte_idx.saturating_sub(acc.bytes as usize); end_byte_idx -= acc.bytes as usize; line_idx -= acc.line_breaks as usize; chunk_byte_start += acc.bytes as usize; // Add to the node stack. node_stack.push((node_ref, child_i)); node_ref = &children.nodes()[child_i]; } // Create the iterator. Node::Leaf(ref text) => { let leaf_byte_idx = line_to_byte_idx(text, line_idx) .max(start_byte_idx) .min(end_byte_idx); let res = Lines { iter: LinesEnum::Full { node_stack, leaf_byte_idx: leaf_byte_idx as u32, total_bytes: byte_idx_range.1 - byte_idx_range.0, }, is_reversed: false, text, byte_idx: chunk_byte_start + leaf_byte_idx - byte_idx_range.0, at_end: leaf_byte_idx == end_byte_idx && line_idx > byte_to_line_idx(&text[..end_byte_idx], end_byte_idx), line_idx: line - line_idx_range.0, total_lines, }; return res; } } } } /// NOT PART OF THE PUBLIC API (hidden from docs for a reason!). /// /// This is only exposed publicly for use in property testing. #[doc(hidden)] pub fn from_str_pt(text: &str) -> Lines { let line_count = byte_to_line_idx(text, text.len()) + 1; Lines::from_str(text, line_count) } pub(crate) fn from_str(text: &str, lines: usize) -> Lines { Lines { iter: LinesEnum::Light, is_reversed: false, text: text, byte_idx: 0, at_end: false, line_idx: 0, total_lines: lines, } } pub(crate) fn from_str_at(text: &str, line: usize, lines: usize) -> Lines { Lines { iter: LinesEnum::Light, is_reversed: false, text: text, byte_idx: line_to_byte_idx(text, line), at_end: line >= lines, line_idx: line.min(lines), total_lines: lines, } } /// Reverses the direction of the iterator in-place. /// /// In other words, swaps the behavior of [`prev()`](Lines::prev()) /// and [`next()`](Lines::next()). #[inline] pub fn reverse(&mut self) { self.is_reversed = !self.is_reversed; } /// Same as `reverse()`, but returns itself. /// /// This is useful when chaining iterator methods: /// /// ```rust /// # use ropey::Rope; /// # let rope = Rope::from_str("Hello there\n world!\n"); /// // Enumerate the rope's lines in reverse, starting from the end. /// for (i, line) in rope.lines_at(rope.len_lines()).reversed().enumerate() { /// println!("{} {}", i, line); /// # assert_eq!(line, rope.line(rope.len_lines() - i - 1)); /// } #[inline] #[must_use] pub fn reversed(mut self) -> Lines<'a> { self.reverse(); self } /// Advances the iterator backwards and returns the previous value. /// /// Runs in O(1) time with respect to rope length and O(N) time with /// respect to line length. #[inline(always)] pub fn prev(&mut self) -> Option> { if self.is_reversed { self.next_impl() } else { self.prev_impl() } } fn prev_impl(&mut self) -> Option> { match *self { Lines { iter: LinesEnum::Full { ref mut node_stack, ref mut leaf_byte_idx, .. }, ref mut byte_idx, ref mut text, ref mut at_end, ref mut line_idx, .. } => { let tail = &text[..*leaf_byte_idx as usize]; // The only line yielded by this iterator that doesn't // end with a line break is the very last line. As the // very last line requires a special conditon here // anyway we can save the result so we don't have to // count newlines later. let ends_with_line_break = if std::mem::take(at_end) { if ends_with_line_break(tail) { *line_idx -= 1; return Some(RopeSlice(RSEnum::Light { text: "", char_count: 0, utf16_surrogate_count: 0, line_break_count: 0, })); } false } else if *byte_idx == 0 { return None; } else { true }; *line_idx -= 1; // Get the byte index of the start of the line within the chunk, // and whether we know if the line is contained entirely within // the chunk or not. let (line_start_idx, line_inside_chunk) = { let line_start = last_line_start_byte_idx(trim_line_break(tail)); let line_len = *leaf_byte_idx as usize - line_start; if line_len >= *byte_idx { (*leaf_byte_idx as usize - *byte_idx, true) } else { (line_start, line_start > 0) } }; let chunk_line = &tail[line_start_idx..]; *byte_idx -= chunk_line.len(); // If the line is contained entirely within the current chunk, return it. if line_inside_chunk { *leaf_byte_idx = line_start_idx as u32; return Some(RopeSlice(RSEnum::Light { text: chunk_line, char_count: count_chars(chunk_line) as Count, utf16_surrogate_count: count_utf16_surrogates(chunk_line) as Count, line_break_count: ends_with_line_break as Count, })); } // We need to advance to the next (preceding) chunk that contains // a line break. As the line might span across multiple chunks we // track the closest common parent node (and position within that // node) during traversal to avoid expensive `RopeSlice` construction // later. let mut shared_parent = node_stack.len() - 1; let mut pos_in_shared_parent = { let (parent, child_i) = *node_stack.last().unwrap(); parent.children().info()[..child_i] .iter() .fold(TextInfo::new(), |res, it| res + *it) }; let mut len = TextInfo::from_str(tail); pos_in_shared_parent += len; // If the line starts exactly at the start of the chunk // then it might not actually span multiple chunks. let mut multi_chunk_slice = !tail.is_empty(); let head_start = loop { let mut stack_idx = node_stack.len() - 1; let (_, child_i) = node_stack.last_mut().unwrap(); // If the iterator has reached the start of the parent node, advance // to the previous parent. if *child_i == 0 { // Find how high up the stack we need to go to advance to // the previous chunk. while node_stack[stack_idx].1 == 0 { debug_assert_ne!(stack_idx, 0, "iterated past the first leaf"); stack_idx -= 1; } // If we've reached a new high position in the stack, accumulate its // TextInfo for `RopeSlice` construction later. if stack_idx < shared_parent { for (node, child_i) in &node_stack[stack_idx..shared_parent] { for &child_pos in &node.children().info()[..*child_i] { pos_in_shared_parent += child_pos; } } shared_parent = stack_idx; } // Advance to the previous chunk. node_stack[stack_idx].1 -= 1; while stack_idx < (node_stack.len() - 1) { let child_i = node_stack[stack_idx].1; let node = &node_stack[stack_idx].0.children().nodes()[child_i]; node_stack[stack_idx + 1] = (node, node.child_count() - 1); stack_idx += 1; } } else { // Advance to the previous sibling chunk. *child_i -= 1; } let (node, child_i) = *node_stack.last().unwrap(); let info = node.children().info()[child_i]; let available_bytes = *byte_idx; if info.line_breaks != 0 { // This chunk contains a line break so it will contain the start of our line. *text = node.children().nodes()[child_i].leaf_text(); // Find the start of the line within the chunk. // The function used here is slightly different because it must // not skip the first line break. // A line break at the end of the chunk is already the line break // we are looking for. The line break belonging to this line is // always contained in the chunk we started this iteration at. let mut line_start = last_line_start_byte_idx(text); // Cut off the line at the start of the iterator. let line_len = text.len() - line_start; if line_len >= available_bytes { line_start = text.len() - available_bytes; } break line_start; } if info.bytes as usize >= available_bytes { // This chunk does not contain a line break but the current // line still ends here because the iterator is exhaused. *text = node.children().nodes()[child_i].leaf_text(); break text.len() - *byte_idx; } len += info; *byte_idx -= info.bytes as usize; multi_chunk_slice = true; }; let head = &text[head_start..]; // Book keeping. *byte_idx -= head.len(); *leaf_byte_idx = head_start as u32; // Construct the `RopeSlice` containing the line. // Note that `head` never contains any line breaks because the // iterator stops at the first line break (see comment above). let head_chars = count_chars(head) as Count; let head_surrogates = count_utf16_surrogates(head) as Count; let line = if multi_chunk_slice { RSEnum::Full { node: node_stack[shared_parent].0, start_info: pos_in_shared_parent - TextInfo { bytes: head.len() as Count, chars: head_chars, utf16_surrogates: head_surrogates, line_breaks: 0, } - len, end_info: pos_in_shared_parent, } } else { RSEnum::Light { text: head, char_count: head_chars, utf16_surrogate_count: head_surrogates, line_break_count: 0, } }; let line = RopeSlice(line); Some(line) } Lines { iter: LinesEnum::Light, ref mut text, ref mut byte_idx, ref mut at_end, ref mut line_idx, .. } => { if std::mem::take(at_end) { if text.is_empty() || ends_with_line_break(text) { *line_idx -= 1; return Some("".into()); } } else if *byte_idx == 0 { return None; } let end_idx = *byte_idx; let start_idx = last_line_start_byte_idx(trim_line_break(&text[..end_idx])); *byte_idx = start_idx; *line_idx -= 1; let line = &text[start_idx..end_idx]; return Some(RopeSlice(RSEnum::Light { text: line, char_count: count_chars(line) as Count, utf16_surrogate_count: count_utf16_surrogates(line) as Count, line_break_count: 1, })); } } } fn next_impl(&mut self) -> Option> { match *self { Lines { iter: LinesEnum::Full { ref mut node_stack, ref mut leaf_byte_idx, total_bytes, }, ref mut byte_idx, ref mut text, ref mut at_end, ref mut line_idx, .. } => { if *at_end { return None; } else if *byte_idx == total_bytes { *at_end = true; *line_idx += 1; return Some(RopeSlice(RSEnum::Light { text: "", char_count: 0, utf16_surrogate_count: 0, line_break_count: 0, })); } *line_idx += 1; let head = &text[*leaf_byte_idx as usize..]; let mut line_len = line_to_byte_idx(head, 1); // Check if the iterators needs to advance to the next chunk. // During this check the number of newline (0 or 1) is yielded // for free so save that aswell. let available_bytes = total_bytes - *byte_idx; let (line_inside_chunk, line_break_count) = if line_len >= available_bytes { // If the iterator is exhausted we don't need to switch chunks. // Check if the last line has a line break to decide whether // we still need to yield an empty line later. line_len = available_bytes; let ends_with_line_break = ends_with_line_break(&head[..line_len]); *at_end = !ends_with_line_break; // Reached end of the text, so no need to advance. (true, ends_with_line_break as u64) } else { // Iterator is not yet exhausted, so advance to the next chunk // if we've reached the chunk boundary and the last character // is not a line break. If the iterator is not exhausted, a // line always ends with a line break. (line_len != head.len() || ends_with_line_break(head), 1) }; // Yield the current line if it is contained within the current chunk. if line_inside_chunk { let line = &head[..line_len]; *byte_idx += line_len; *leaf_byte_idx += line_len as u32; return Some(RopeSlice(RSEnum::Light { text: line, char_count: count_chars(line) as Count, utf16_surrogate_count: count_utf16_surrogates(line) as Count, line_break_count, })); } *byte_idx += head.len(); // We need to advance to the next chunk that contains // a line break. As the line might span across multiple chunks we // track the closest common parent node (and position within that // node) during traversal to avoid expensive `RopeSlice` construction // later. let mut shared_parent = node_stack.len() - 1; let mut pos_in_shared_parent = { let (parent, child_i) = *node_stack.last().unwrap(); parent.children().info()[..=child_i] .iter() .fold(TextInfo::new(), |res, it| res + *it) }; let mut len = TextInfo::from_str(head); pos_in_shared_parent -= len; // If the line starts exactly at the start of the next chunk // then it might not actually span multiple chunks. let mut multi_chunk_slice = !head.is_empty(); let (tail_len, tail_ends_with_newline) = loop { let mut stack_idx = node_stack.len() - 1; // Advance to the next sibling chunk. let (_, child_i) = node_stack.last_mut().unwrap(); *child_i += 1; // If the iterator has reached the end of the parent node, advance // to the next parent. if *child_i >= node_stack[stack_idx].0.child_count() { // Find how high up the stack we need to go to advance to // the next chunk. while node_stack[stack_idx].1 >= (node_stack[stack_idx].0.child_count() - 1) { debug_assert_ne!(stack_idx, 0, "iterated past the last leaf"); stack_idx -= 1; } // If we've reached a new high position in the stack, accumulate its // TextInfo for `RopeSlice` construction later. if stack_idx < shared_parent { for (node, child_i) in &node_stack[stack_idx..shared_parent] { for &child_pos in &node.children().info()[..*child_i] { pos_in_shared_parent += child_pos; } } shared_parent = stack_idx; } // Advance to the next chunk. node_stack[stack_idx].1 += 1; while stack_idx < (node_stack.len() - 1) { let child_i = node_stack[stack_idx].1; let node = &node_stack[stack_idx].0.children().nodes()[child_i]; node_stack[stack_idx + 1] = (node, 0); stack_idx += 1; } } let (node, child_i) = *node_stack.last().unwrap(); let info = node.children().info()[child_i]; let available_bytes = total_bytes - *byte_idx; if info.line_breaks != 0 { // This chunk contains a line break so it will contain the start of our line. *text = node.children().nodes()[child_i].leaf_text(); // Find the end of the line within the chunk. let mut line_end = line_to_byte_idx(text, 1); // Check if the iterator was exhausted. let ends_with_newline = if line_end >= available_bytes { // Handle terminating lines without a line break properly. line_end = available_bytes; let ends_with_newline = ends_with_line_break(&text[..line_end]); *at_end = !ends_with_newline; ends_with_newline } else { true }; break (line_end, ends_with_newline); } if info.bytes as usize >= available_bytes { // This chunk does not contain a line break but the current // line still ends here because the iterator is exhausted. *at_end = true; *text = node.children().nodes()[child_i].leaf_text(); break (available_bytes, false); } len += info; *byte_idx += info.bytes as usize; multi_chunk_slice = true; }; // Book keeping. *byte_idx += tail_len; *leaf_byte_idx = tail_len as u32; // Construct the `RopeSlice` containing the line. let line_tail = &text[..tail_len]; let line_tail_chars = count_chars(line_tail) as Count; let line_tail_surrogates = count_utf16_surrogates(line_tail) as Count; let line = if multi_chunk_slice { RSEnum::Full { node: node_stack[shared_parent].0, start_info: pos_in_shared_parent, end_info: pos_in_shared_parent + len + TextInfo { bytes: tail_len as Count, chars: line_tail_chars, utf16_surrogates: line_tail_surrogates, line_breaks: tail_ends_with_newline as Count, }, } } else { RSEnum::Light { text: line_tail, char_count: line_tail_chars, utf16_surrogate_count: line_tail_surrogates, line_break_count: tail_ends_with_newline as Count, } }; Some(RopeSlice(line)) } Lines { iter: LinesEnum::Light, text, ref mut byte_idx, ref mut at_end, ref mut line_idx, .. } => { if *at_end { return None; } else if *byte_idx == text.len() { *at_end = true; *line_idx += 1; return Some("".into()); } let start_idx = *byte_idx; let end_idx = line_to_byte_idx(&text[start_idx..], 1) + start_idx; *byte_idx = end_idx; *line_idx += 1; if end_idx == text.len() { *at_end = !ends_with_line_break(text); } return Some((&text[start_idx..end_idx]).into()); } } } } impl<'a> Iterator for Lines<'a> { type Item = RopeSlice<'a>; /// Advances the iterator forward and returns the next value. /// /// Runs in O(1) time with respect to rope length and O(N) time with /// respect to line length. #[inline(always)] fn next(&mut self) -> Option> { if self.is_reversed { self.prev_impl() } else { self.next_impl() } } fn size_hint(&self) -> (usize, Option) { if self.is_reversed { (self.line_idx, Some(self.line_idx)) } else { ( self.total_lines - self.line_idx, Some(self.total_lines - self.line_idx), ) } } } impl ExactSizeIterator for Lines<'_> {} //========================================================== /// An iterator over a `Rope`'s contiguous `str` chunks. /// /// Internally, each `Rope` stores text as a segemented collection of utf8 /// strings. This iterator iterates over those segments, returning a /// `&str` slice for each one. It is useful for situations such as: /// /// - Writing a rope's utf8 text data to disk (but see /// [`write_to()`](crate::rope::Rope::write_to) for a convenience function that does this /// for casual use-cases). /// - Streaming a rope's text data somewhere. /// - Saving a rope to a non-utf8 encoding, doing the encoding conversion /// incrementally as you go. /// - Writing custom iterators over a rope's text data. /// /// There are precisely two guarantees about the yielded chunks: /// /// - All non-empty chunks are yielded, and they are yielded in order. /// - CRLF pairs are never split across chunks. /// /// There are no guarantees about the size of yielded chunks, and except for /// CRLF pairs and being valid `str` slices there are no guarantees about /// where the chunks are split. For example, they may be zero-sized, they /// don't necessarily align with line breaks, etc. #[derive(Debug, Clone)] pub struct Chunks<'a> { iter: ChunksEnum<'a>, is_reversed: bool, } #[derive(Debug, Clone)] enum ChunksEnum<'a> { Full { node_stack: Vec<(&'a Arc, usize)>, // (node ref, index of current child) total_bytes: usize, // Total bytes in the data range of the iterator. byte_idx: isize, // The index of the current byte relative to the data range start. }, Light { text: &'a str, is_end: bool, }, } impl<'a> Chunks<'a> { #[inline(always)] pub(crate) fn new(node: &Arc) -> Chunks { let info = node.text_info(); Chunks::new_with_range_at_byte( node, 0, (0, info.bytes as usize), (0, info.chars as usize), (0, info.line_breaks as usize + 1), ) .0 } #[inline(always)] pub(crate) fn new_with_range( node: &Arc, byte_idx_range: (usize, usize), char_idx_range: (usize, usize), line_break_idx_range: (usize, usize), ) -> Chunks { Chunks::new_with_range_at_byte( node, byte_idx_range.0, byte_idx_range, char_idx_range, line_break_idx_range, ) .0 } /// The main workhorse function for creating new `Chunks` iterators. /// /// Creates a new `Chunks` iterator from the given node, starting the /// iterator at the chunk containing the `at_byte` byte index (i.e. the /// `next()` method will yield the chunk containing that byte). The range /// of the iterator is bounded by `byte_idx_range`. /// /// Both `at_byte` and `byte_idx_range` are relative to the beginning of /// of the passed node. /// /// Passing an `at_byte` equal to the max of `byte_idx_range` creates an /// iterator at the end of forward iteration. /// /// Returns the iterator and the byte/char/line index of its start relative /// to the start of the node. pub(crate) fn new_with_range_at_byte( node: &Arc, at_byte: usize, byte_idx_range: (usize, usize), char_idx_range: (usize, usize), line_break_idx_range: (usize, usize), ) -> (Chunks, usize, usize, usize) { debug_assert!(at_byte >= byte_idx_range.0); debug_assert!(at_byte <= byte_idx_range.1); // For convenience/readability. let start_byte = byte_idx_range.0; let end_byte = byte_idx_range.1; // Special-case for empty text contents. if start_byte == end_byte { return ( Chunks { iter: ChunksEnum::Light { text: "", is_end: false, }, is_reversed: false, }, 0, 0, 0, ); } // If root is a leaf, return light version of the iter. if node.is_leaf() { let text = &node.leaf_text()[start_byte..end_byte]; if at_byte == end_byte { return ( Chunks { iter: ChunksEnum::Light { text: text, is_end: true, }, is_reversed: false, }, text.len(), count_chars(text), byte_to_line_idx(text, text.len()), ); } else { return ( Chunks { iter: ChunksEnum::Light { text: text, is_end: false, }, is_reversed: false, }, 0, 0, 0, ); } } // Create and populate the node stack, and determine the char index // within the first chunk, and byte index of the start of that chunk. let mut info = TextInfo::new(); let mut byte_idx = at_byte as isize; let node_stack = { let mut node_stack: Vec<(&Arc, usize)> = Vec::new(); let mut node_ref = node; loop { match **node_ref { Node::Leaf(ref text) => { if at_byte < end_byte || byte_idx == 0 { byte_idx = info.bytes as isize - start_byte as isize; } else { byte_idx = (info.bytes as isize + text.len() as isize) - start_byte as isize; info = TextInfo { bytes: byte_idx_range.1 as u64, chars: char_idx_range.1 as u64, utf16_surrogates: 0, // Bogus value, not needed line_breaks: line_break_idx_range.1 as u64 - 1, }; (*node_stack.last_mut().unwrap()).1 += 1; } break; } Node::Internal(ref children) => { let (child_i, acc_info) = children.search_byte_idx(byte_idx as usize); info += acc_info; node_stack.push((node_ref, child_i)); node_ref = &children.nodes()[child_i]; byte_idx -= acc_info.bytes as isize; } } } node_stack }; // Create the iterator. ( Chunks { iter: ChunksEnum::Full { node_stack: node_stack, total_bytes: end_byte - start_byte, byte_idx: byte_idx, }, is_reversed: false, }, (info.bytes as usize).max(byte_idx_range.0), (info.chars as usize).max(char_idx_range.0), (info.line_breaks as usize).max(line_break_idx_range.0), ) } #[inline(always)] pub(crate) fn new_with_range_at_char( node: &Arc, at_char: usize, byte_idx_range: (usize, usize), char_idx_range: (usize, usize), line_break_idx_range: (usize, usize), ) -> (Chunks, usize, usize, usize) { let at_byte = if at_char == char_idx_range.1 { byte_idx_range.1 } else { (node.get_chunk_at_char(at_char).1.bytes as usize).max(byte_idx_range.0) }; Chunks::new_with_range_at_byte( node, at_byte, byte_idx_range, char_idx_range, line_break_idx_range, ) } #[inline(always)] pub(crate) fn new_with_range_at_line_break( node: &Arc, at_line_break: usize, byte_idx_range: (usize, usize), char_idx_range: (usize, usize), line_break_idx_range: (usize, usize), ) -> (Chunks, usize, usize, usize) { let at_byte = if at_line_break == line_break_idx_range.1 { byte_idx_range.1 } else { (node.get_chunk_at_line_break(at_line_break).1.bytes as usize).max(byte_idx_range.0) }; Chunks::new_with_range_at_byte( node, at_byte, byte_idx_range, char_idx_range, line_break_idx_range, ) } pub(crate) fn from_str(text: &str, at_end: bool) -> Chunks { Chunks { iter: ChunksEnum::Light { text: text, is_end: at_end, }, is_reversed: false, } } /// Reverses the direction of the iterator in-place. /// /// In other words, swaps the behavior of [`prev()`](Chunks::prev()) /// and [`next()`](Chunks::next()). #[inline] pub fn reverse(&mut self) { self.is_reversed = !self.is_reversed; } /// Same as `reverse()`, but returns itself. /// /// This is useful when chaining iterator methods: /// /// ```rust /// # use ropey::Rope; /// # let rope = Rope::from_str("Hello there\n world!\n"); /// // Enumerate the rope's chunks in reverse, starting from the end. /// for (i, chunk) in rope.chunks_at_byte(rope.len_bytes()).0.reversed().enumerate() { /// println!("{} {}", i, chunk); /// # assert_eq!(chunk, rope.chunks().nth(rope.chunks().count() - i - 1).unwrap()); /// } #[inline] #[must_use] pub fn reversed(mut self) -> Chunks<'a> { self.reverse(); self } /// Advances the iterator backwards and returns the previous value. /// /// Runs in amortized O(1) time and worst-case O(log N) time. #[inline(always)] pub fn prev(&mut self) -> Option<&'a str> { if !self.is_reversed { self.prev_impl() } else { self.next_impl() } } fn prev_impl(&mut self) -> Option<&'a str> { match *self { Chunks { iter: ChunksEnum::Full { ref mut node_stack, total_bytes, ref mut byte_idx, }, .. } => { if *byte_idx <= 0 { return None; } // Progress the node stack if needed. let mut stack_idx = node_stack.len() - 1; if node_stack[stack_idx].1 == 0 { while node_stack[stack_idx].1 == 0 { if stack_idx == 0 { return None; } else { stack_idx -= 1; } } node_stack[stack_idx].1 -= 1; while stack_idx < (node_stack.len() - 1) { let child_i = node_stack[stack_idx].1; let node = &node_stack[stack_idx].0.children().nodes()[child_i]; node_stack[stack_idx + 1] = (node, node.child_count() - 1); stack_idx += 1; } node_stack[stack_idx].1 += 1; } // Fetch the node and child index. let (node, ref mut child_i) = node_stack.last_mut().unwrap(); *child_i -= 1; // Get the text, sliced to the appropriate range. let text = node.children().nodes()[*child_i].leaf_text(); *byte_idx -= text.len() as isize; let text_slice = { let start_byte = if *byte_idx < 0 { (-*byte_idx) as usize } else { 0 }; let end_byte = text.len().min((total_bytes as isize - *byte_idx) as usize); &text[start_byte..end_byte] }; // Return the text. return Some(text_slice); } Chunks { iter: ChunksEnum::Light { text, ref mut is_end, }, .. } => { if !*is_end || text.is_empty() { return None; } else { *is_end = false; return Some(text); } } } } fn next_impl(&mut self) -> Option<&'a str> { match *self { Chunks { iter: ChunksEnum::Full { ref mut node_stack, total_bytes, ref mut byte_idx, }, .. } => { if *byte_idx >= total_bytes as isize { return None; } // Progress the node stack if needed. let mut stack_idx = node_stack.len() - 1; if node_stack[stack_idx].1 >= node_stack[stack_idx].0.child_count() { while node_stack[stack_idx].1 >= (node_stack[stack_idx].0.child_count() - 1) { if stack_idx == 0 { return None; } else { stack_idx -= 1; } } node_stack[stack_idx].1 += 1; while stack_idx < (node_stack.len() - 1) { let child_i = node_stack[stack_idx].1; let node = &node_stack[stack_idx].0.children().nodes()[child_i]; node_stack[stack_idx + 1] = (node, 0); stack_idx += 1; } } // Fetch the node and child index. let (node, ref mut child_i) = node_stack.last_mut().unwrap(); // Get the text, sliced to the appropriate range. let text = node.children().nodes()[*child_i].leaf_text(); let text_slice = { let start_byte = if *byte_idx < 0 { (-*byte_idx) as usize } else { 0 }; let end_byte = text.len().min((total_bytes as isize - *byte_idx) as usize); &text[start_byte..end_byte] }; // Book keeping. *byte_idx += text.len() as isize; *child_i += 1; // Return the text. return Some(text_slice); } Chunks { iter: ChunksEnum::Light { text, ref mut is_end, }, .. } => { if *is_end || text.is_empty() { return None; } else { *is_end = true; return Some(text); } } } } } impl<'a> Iterator for Chunks<'a> { type Item = &'a str; /// Advances the iterator forward and returns the next value. /// /// Runs in amortized O(1) time and worst-case O(log N) time. #[inline(always)] fn next(&mut self) -> Option<&'a str> { if !self.is_reversed { self.next_impl() } else { self.prev_impl() } } } #[cfg(test)] mod tests { #![allow(clippy::while_let_on_iterator)] use super::*; use crate::Rope; const TEXT: &str = "\r\n\ Hello there! How're you doing? It's a fine day, \ isn't it? Aren't you glad we're alive?\r\n\ こんにちは!元気ですか?日はいいですね。\ 私たちが生きだって嬉しいではないか?\r\n\ Hello there! How're you doing? It's a fine day, \ isn't it? Aren't you glad we're alive?\r\n\ こんにちは!元気ですか?日はいいですね。\ 私たちが生きだって嬉しいではないか?\r\n\ Hello there! How're you doing? It's a fine day, \ isn't it? Aren't you glad we're alive?\r\n\ こんにちは!元気ですか?日はいいですね。\ 私たちが生きだって嬉しいではないか?\r\n\ Hello there! How're you doing? It's a fine day, \ isn't it? Aren't you glad we're alive?\r\n\ こんにちは!元気ですか?日はいいですね。\ 私たちが生きだって嬉しいではないか?\r\n\ Hello there! How're you doing? It's a fine day, \ isn't it? Aren't you glad we're alive?\r\n\ こんにちは!元気ですか?日はいいですね。\ 私たちが生きだって嬉しいではないか?\r\n\ Hello there! How're you doing? It's a fine day, \ isn't it? Aren't you glad we're alive?\r\n\ こんにちは!元気ですか?日はいいですね。\ 私たちが生きだって嬉しいではないか?\r\n\ Hello there! How're you doing? It's a fine day, \ isn't it? Aren't you glad we're alive?\r\n\ こんにちは!元気ですか?日はいいですね。\ 私たちが生きだって嬉しいではないか?\r\n\ Hello there! How're you doing? It's a fine day, \ isn't it? Aren't you glad we're alive?\r\n\ こんにちは!元気ですか?日はいいですね。\ 私たちが生きだって嬉しいではないか?\r\n\ Hello there! How're you doing? It's a fine day, \ isn't it? Aren't you glad we're alive?\r\n\ こんにちは!元気ですか?日はいいですね。\ 私たちが生きだって嬉しいではないか?\r\n\ Hello there! How're you doing? It's a fine day, \ isn't it? Aren't you glad we're alive?\r\n\ こんにちは!元気ですか?日はいいですね。\ 私たちが生きだって嬉しいではないか?\r\n\ Hello there! How're you doing? It's a fine day, \ isn't it? Aren't you glad we're alive?\r\n\ こんにちは!元気ですか?日はいいですね。\ 私たちが生きだって嬉しいではないか?\r\n\ Hello there! How're you doing? It's a fine day, \ isn't it? Aren't you glad we're alive?\r\n\ こんにちは!元気ですか?日はいいですね。\ 私たちが生きだって嬉しいではないか?\r\n\ Hello there! How're you doing? It's a fine day, \ isn't it? Aren't you glad we're alive?\r\n\ こんにちは!元気ですか?日はいいですね。\ 私たちが生きだって嬉しいではないか?\r\n\ Hello there! How're you doing? It's a fine day, \ isn't it? Aren't you glad we're alive?\r\n\ こんにちは!元気ですか?日はいいですね。\ 私たちが生きだって嬉しいではないか?\r\n\ Hello there! How're you doing? It's a fine day, \ isn't it? Aren't you glad we're alive?\r\n\ こんにちは!元気ですか?日はいいですね。\ 私たちが生きだって嬉しいではないか?\r\n\ Hello there! How're you doing? It's a fine day, \ isn't it? Aren't you glad we're alive?\r\n\ こんにちは!元気ですか?日はいいですね。\ 私たちが生きだって嬉しいではないか?\r\n\ "; #[test] #[cfg_attr(miri, ignore)] fn bytes_01() { let r = Rope::from_str(TEXT); for (br, bt) in r.bytes().zip(TEXT.bytes()) { assert_eq!(br, bt); } } #[test] #[cfg_attr(miri, ignore)] fn bytes_02() { let r = Rope::from_str(TEXT); let mut itr = r.bytes(); while let Some(_) = itr.next() {} let mut i = TEXT.len(); while let Some(b) = itr.prev() { i -= 1; assert_eq!(b, TEXT.as_bytes()[i]); } } #[test] #[cfg_attr(miri, ignore)] fn bytes_03() { let r = Rope::from_str(TEXT); let mut itr = r.bytes(); itr.next(); itr.prev(); assert_eq!(None, itr.prev()); } #[test] #[cfg_attr(miri, ignore)] fn bytes_04() { let r = Rope::from_str(TEXT); let mut itr = r.bytes(); while let Some(_) = itr.next() {} itr.prev(); itr.next(); assert_eq!(None, itr.next()); } #[test] #[cfg_attr(miri, ignore)] fn bytes_05() { let r = Rope::from_str(TEXT); let mut itr = r.bytes(); assert_eq!(None, itr.prev()); itr.next(); itr.prev(); assert_eq!(None, itr.prev()); } #[test] #[cfg_attr(miri, ignore)] fn bytes_06() { let r = Rope::from_str(TEXT); let mut itr = r.bytes(); while let Some(_) = itr.next() {} assert_eq!(None, itr.next()); itr.prev(); itr.next(); assert_eq!(None, itr.next()); } #[test] #[cfg_attr(miri, ignore)] fn bytes_07() { let mut itr = Bytes::from_str("a"); assert_eq!(Some(0x61), itr.next()); assert_eq!(None, itr.next()); assert_eq!(Some(0x61), itr.prev()); assert_eq!(None, itr.prev()); } #[test] #[cfg_attr(miri, ignore)] fn bytes_at_01() { let r = Rope::from_str(TEXT); let mut bytes_1 = TEXT.bytes(); for i in 0..(r.len_bytes() + 1) { let mut bytes_2 = r.bytes_at(i); assert_eq!(bytes_1.next(), bytes_2.next()); } } #[test] #[cfg_attr(miri, ignore)] fn bytes_at_02() { let r = Rope::from_str(TEXT); let mut bytes = r.bytes_at(r.len_bytes()); assert_eq!(bytes.next(), None); } #[test] #[cfg_attr(miri, ignore)] fn bytes_at_03() { let r = Rope::from_str(TEXT); let mut bytes_1 = r.bytes_at(r.len_bytes()); let mut bytes_2 = TEXT.bytes(); while let Some(b) = bytes_2.next_back() { assert_eq!(bytes_1.prev(), Some(b)); } } #[test] #[cfg_attr(miri, ignore)] fn bytes_exact_size_iter_01() { let r = Rope::from_str(TEXT); let s = r.slice(34..301); let mut byte_count = s.len_bytes(); let mut bytes = s.bytes(); assert_eq!(byte_count, bytes.len()); while let Some(_) = bytes.next() { byte_count -= 1; assert_eq!(byte_count, bytes.len()); } bytes.next(); bytes.next(); bytes.next(); bytes.next(); bytes.next(); bytes.next(); bytes.next(); assert_eq!(bytes.len(), 0); assert_eq!(byte_count, 0); } #[test] #[cfg_attr(miri, ignore)] fn bytes_exact_size_iter_02() { let r = Rope::from_str(TEXT); let s = r.slice(34..301); for i in 0..=s.len_bytes() { let bytes = s.bytes_at(i); assert_eq!(s.len_bytes() - i, bytes.len()); } } #[test] #[cfg_attr(miri, ignore)] fn bytes_exact_size_iter_03() { let r = Rope::from_str(TEXT); let s = r.slice(34..301); let mut byte_count = 0; let mut bytes = s.bytes_at(s.len_bytes()); assert_eq!(byte_count, bytes.len()); while bytes.prev().is_some() { byte_count += 1; assert_eq!(byte_count, bytes.len()); } assert_eq!(bytes.len(), s.len_bytes()); bytes.prev(); bytes.prev(); bytes.prev(); bytes.prev(); bytes.prev(); bytes.prev(); bytes.prev(); assert_eq!(bytes.len(), s.len_bytes()); assert_eq!(byte_count, s.len_bytes()); } #[test] #[cfg_attr(miri, ignore)] fn bytes_reverse_01() { let r = Rope::from_str(TEXT); let mut itr = r.bytes(); let mut stack = Vec::new(); for _ in 0..32 { stack.push(itr.next().unwrap()); } itr.reverse(); for _ in 0..32 { assert_eq!(stack.pop(), itr.next()); } } #[test] #[cfg_attr(miri, ignore)] fn bytes_reverse_02() { let r = Rope::from_str(TEXT); let mut itr = r.bytes_at(r.len_bytes() / 3); let mut stack = Vec::new(); for _ in 0..32 { stack.push(itr.next().unwrap()); } itr.reverse(); for _ in 0..32 { assert_eq!(stack.pop(), itr.next()); } } #[test] #[cfg_attr(miri, ignore)] fn bytes_reverse_03() { let r = Rope::from_str(TEXT); let mut itr = r.bytes_at(r.len_bytes() / 3); let mut stack = Vec::new(); itr.reverse(); for _ in 0..32 { stack.push(itr.next().unwrap()); } itr.reverse(); for _ in 0..32 { assert_eq!(stack.pop(), itr.next()); } } #[test] #[cfg_attr(miri, ignore)] fn bytes_reverse_04() { let mut itr = Bytes::from_str("a"); assert_eq!(Some(0x61), itr.next()); assert_eq!(None, itr.next()); itr.reverse(); assert_eq!(Some(0x61), itr.next()); assert_eq!(None, itr.next()); } #[test] #[cfg_attr(miri, ignore)] fn bytes_reverse_exact_size_iter_01() { let r = Rope::from_str(TEXT); let s = r.slice(34..301); let mut bytes = s.bytes_at(42); bytes.reverse(); let mut byte_count = 42; assert_eq!(42, bytes.len()); while let Some(_) = bytes.next() { byte_count -= 1; assert_eq!(byte_count, bytes.len()); } bytes.next(); bytes.next(); bytes.next(); bytes.next(); bytes.next(); bytes.next(); bytes.next(); assert_eq!(bytes.len(), 0); assert_eq!(byte_count, 0); } #[test] #[cfg_attr(miri, ignore)] fn chars_01() { let r = Rope::from_str(TEXT); for (cr, ct) in r.chars().zip(TEXT.chars()) { assert_eq!(cr, ct); } } #[test] #[cfg_attr(miri, ignore)] fn chars_02() { let r = Rope::from_str(TEXT); let mut itr = r.chars(); let mut text_itr = TEXT.chars(); while let Some(_) = itr.next() {} while let Some(b) = itr.prev() { assert_eq!(b, text_itr.next_back().unwrap()); } } #[test] #[cfg_attr(miri, ignore)] fn chars_03() { let r = Rope::from_str(TEXT); let mut itr = r.chars(); itr.next(); itr.prev(); assert_eq!(None, itr.prev()); } #[test] #[cfg_attr(miri, ignore)] fn chars_04() { let r = Rope::from_str(TEXT); let mut itr = r.chars(); while let Some(_) = itr.next() {} itr.prev(); itr.next(); assert_eq!(None, itr.next()); } #[test] #[cfg_attr(miri, ignore)] fn chars_05() { let r = Rope::from_str(TEXT); let mut itr = r.chars(); assert_eq!(None, itr.prev()); itr.next(); itr.prev(); assert_eq!(None, itr.prev()); } #[test] #[cfg_attr(miri, ignore)] fn chars_06() { let r = Rope::from_str(TEXT); let mut itr = r.chars(); while let Some(_) = itr.next() {} assert_eq!(None, itr.next()); itr.prev(); itr.next(); assert_eq!(None, itr.next()); } #[test] #[cfg_attr(miri, ignore)] fn chars_07() { let mut itr = Chars::from_str("a"); assert_eq!(Some('a'), itr.next()); assert_eq!(None, itr.next()); assert_eq!(Some('a'), itr.prev()); assert_eq!(None, itr.prev()); } #[test] #[cfg_attr(miri, ignore)] fn chars_at_01() { let r = Rope::from_str(TEXT); let mut chars_1 = TEXT.chars(); for i in 0..(r.len_chars() + 1) { let mut chars_2 = r.chars_at(i); assert_eq!(chars_1.next(), chars_2.next()); } } #[test] #[cfg_attr(miri, ignore)] fn chars_at_02() { let r = Rope::from_str(TEXT); let mut chars = r.chars_at(r.len_chars()); assert_eq!(chars.next(), None); } #[test] #[cfg_attr(miri, ignore)] fn chars_at_03() { let r = Rope::from_str(TEXT); let mut chars_1 = r.chars_at(r.len_chars()); let mut chars_2 = TEXT.chars(); while let Some(c) = chars_2.next_back() { assert_eq!(chars_1.prev(), Some(c)); } } #[test] #[cfg_attr(miri, ignore)] fn chars_exact_size_iter_01() { let r = Rope::from_str(TEXT); let s = r.slice(34..301); let mut char_count = s.len_chars(); let mut chars = s.chars(); assert_eq!(char_count, chars.len()); while let Some(_) = chars.next() { char_count -= 1; assert_eq!(char_count, chars.len()); } assert_eq!(char_count, 0); assert_eq!(chars.len(), 0); } #[test] #[cfg_attr(miri, ignore)] fn chars_exact_size_iter_02() { let r = Rope::from_str(TEXT); let s = r.slice(34..301); for i in 0..=s.len_chars() { let chars = s.chars_at(i); assert_eq!(s.len_chars() - i, chars.len()); } } #[test] #[cfg_attr(miri, ignore)] fn chars_exact_size_iter_03() { let r = Rope::from_str(TEXT); let s = r.slice(34..301); let mut char_count = 0; let mut chars = s.chars_at(s.len_chars()); assert_eq!(char_count, chars.len()); while chars.prev().is_some() { char_count += 1; assert_eq!(char_count, chars.len()); } assert_eq!(char_count, s.len_chars()); assert_eq!(chars.len(), s.len_chars()); chars.prev(); assert_eq!(chars.len(), s.len_chars()); } #[test] #[cfg_attr(miri, ignore)] fn chars_reverse_01() { let r = Rope::from_str(TEXT); let mut itr = r.chars(); let mut stack = Vec::new(); for _ in 0..32 { stack.push(itr.next().unwrap()); } itr.reverse(); for _ in 0..32 { assert_eq!(stack.pop(), itr.next()); } } #[test] #[cfg_attr(miri, ignore)] fn chars_reverse_02() { let r = Rope::from_str(TEXT); let mut itr = r.chars_at(r.len_chars() / 3); let mut stack = Vec::new(); for _ in 0..32 { stack.push(itr.next().unwrap()); } itr.reverse(); for _ in 0..32 { assert_eq!(stack.pop(), itr.next()); } } #[test] #[cfg_attr(miri, ignore)] fn chars_reverse_03() { let r = Rope::from_str(TEXT); let mut itr = r.chars_at(r.len_chars() / 3); let mut stack = Vec::new(); itr.reverse(); for _ in 0..32 { stack.push(itr.next().unwrap()); } itr.reverse(); for _ in 0..32 { assert_eq!(stack.pop(), itr.next()); } } #[test] #[cfg_attr(miri, ignore)] fn chars_reverse_04() { let mut itr = Chars::from_str("a"); assert_eq!(Some('a'), itr.next()); assert_eq!(None, itr.next()); itr.reverse(); assert_eq!(Some('a'), itr.next()); assert_eq!(None, itr.next()); } #[test] #[cfg_attr(miri, ignore)] fn chars_reverse_exact_size_iter_01() { let r = Rope::from_str(TEXT); let s = r.slice(34..301); let mut chars = s.chars_at(42); chars.reverse(); let mut char_count = 42; assert_eq!(42, chars.len()); while let Some(_) = chars.next() { char_count -= 1; assert_eq!(char_count, chars.len()); } chars.next(); chars.next(); chars.next(); chars.next(); chars.next(); chars.next(); chars.next(); assert_eq!(chars.len(), 0); assert_eq!(char_count, 0); } #[test] #[cfg_attr(miri, ignore)] fn lines_21() { let r = Rope::from_str("a\nb\nc\nd\ne\nf\ng\nh\n"); for (line, c) in r.lines().zip('a'..='h') { assert_eq!(line, format!("{c}\n")) } for (line, c) in r .lines_at(r.len_lines() - 1) .reversed() .zip(('a'..='h').rev()) { assert_eq!(line, format!("{c}\n")) } let r = Rope::from_str("ab\nc\nd\ne\nf\ng\nh\n"); for (line, c) in r.slice(1..).lines().zip('b'..='h') { assert_eq!(line, format!("{c}\n")) } } #[test] #[cfg_attr(miri, ignore)] fn lines_01() { let r = Rope::from_str(TEXT); let s = r.slice(..); assert_eq!(34, r.lines().count()); assert_eq!(34, s.lines().count()); // Rope let mut lines = r.lines(); assert_eq!("\r\n", lines.next().unwrap()); for _ in 0..16 { assert_eq!( "Hello there! How're you doing? It's a fine day, \ isn't it? Aren't you glad we're alive?\r\n", lines.next().unwrap() ); assert_eq!( "こんにちは!元気ですか?日はいいですね。\ 私たちが生きだって嬉しいではないか?\r\n", lines.next().unwrap() ); } assert_eq!("", lines.next().unwrap()); assert!(lines.next().is_none()); // Slice let mut lines = s.lines(); assert_eq!("\r\n", lines.next().unwrap()); for _ in 0..16 { assert_eq!( "Hello there! How're you doing? It's a fine day, \ isn't it? Aren't you glad we're alive?\r\n", lines.next().unwrap() ); assert_eq!( "こんにちは!元気ですか?日はいいですね。\ 私たちが生きだって嬉しいではないか?\r\n", lines.next().unwrap() ); } assert_eq!("", lines.next().unwrap()); assert!(lines.next().is_none()); } #[test] #[cfg_attr(miri, ignore)] fn lines_02() { let text = "Hello there!\nHow goes it?"; let r = Rope::from_str(text); let s = r.slice(..); assert_eq!(2, r.lines().count()); assert_eq!(2, s.lines().count()); let mut lines = r.lines(); assert_eq!("Hello there!\n", lines.next().unwrap()); assert_eq!("How goes it?", lines.next().unwrap()); assert!(lines.next().is_none()); let mut lines = s.lines(); assert_eq!("Hello there!\n", lines.next().unwrap()); assert_eq!("How goes it?", lines.next().unwrap()); assert!(lines.next().is_none()); } #[test] #[cfg_attr(miri, ignore)] fn lines_03() { let text = "Hello there!\nHow goes it?\n"; let r = Rope::from_str(text); let s = r.slice(..); assert_eq!(3, r.lines().count()); assert_eq!(3, s.lines().count()); let mut lines = r.lines(); assert_eq!("Hello there!\n", lines.next().unwrap()); assert_eq!("How goes it?\n", lines.next().unwrap()); assert_eq!("", lines.next().unwrap()); assert!(lines.next().is_none()); let mut lines = s.lines(); assert_eq!("Hello there!\n", lines.next().unwrap()); assert_eq!("How goes it?\n", lines.next().unwrap()); assert_eq!("", lines.next().unwrap()); assert!(lines.next().is_none()); } #[test] #[cfg_attr(miri, ignore)] fn lines_04() { let text = "Hello there!\nHow goes it?\nYeah!"; let r = Rope::from_str(text); let s1 = r.slice(..25); let s2 = r.slice(..26); assert_eq!(2, s1.lines().count()); assert_eq!(3, s2.lines().count()); let mut lines = s1.lines(); assert_eq!("Hello there!\n", lines.next().unwrap()); assert_eq!("How goes it?", lines.next().unwrap()); assert!(lines.next().is_none()); let mut lines = s2.lines(); assert_eq!("Hello there!\n", lines.next().unwrap()); assert_eq!("How goes it?\n", lines.next().unwrap()); assert_eq!("", lines.next().unwrap()); assert!(lines.next().is_none()); } #[test] #[cfg_attr(miri, ignore)] fn lines_05() { let text = ""; let r = Rope::from_str(text); let s = r.slice(..); assert_eq!(1, r.lines().count()); assert_eq!(1, s.lines().count()); let mut lines = r.lines(); assert_eq!("", lines.next().unwrap()); assert!(lines.next().is_none()); let mut lines = s.lines(); assert_eq!("", lines.next().unwrap()); assert!(lines.next().is_none()); } #[test] #[cfg_attr(miri, ignore)] fn lines_06() { let text = "a"; let r = Rope::from_str(text); let s = r.slice(..); assert_eq!(1, r.lines().count()); assert_eq!(1, s.lines().count()); let mut lines = r.lines(); assert_eq!("a", lines.next().unwrap()); assert!(lines.next().is_none()); let mut lines = s.lines(); assert_eq!("a", lines.next().unwrap()); assert!(lines.next().is_none()); } #[test] #[cfg_attr(miri, ignore)] fn lines_07() { let text = "a\nb"; let r = Rope::from_str(text); let s = r.slice(..); assert_eq!(2, r.lines().count()); assert_eq!(2, s.lines().count()); let mut lines = r.lines(); assert_eq!("a\n", lines.next().unwrap()); assert_eq!("b", lines.next().unwrap()); assert!(lines.next().is_none()); let mut lines = s.lines(); assert_eq!("a\n", lines.next().unwrap()); assert_eq!("b", lines.next().unwrap()); assert!(lines.next().is_none()); } #[test] #[cfg_attr(miri, ignore)] fn lines_08() { let text = "\n"; let r = Rope::from_str(text); let s = r.slice(..); assert_eq!(2, r.lines().count()); assert_eq!(2, s.lines().count()); let mut lines = r.lines(); assert_eq!("\n", lines.next().unwrap()); assert_eq!("", lines.next().unwrap()); assert!(lines.next().is_none()); let mut lines = s.lines(); assert_eq!("\n", lines.next().unwrap()); assert_eq!("", lines.next().unwrap()); assert!(lines.next().is_none()); } #[test] #[cfg_attr(miri, ignore)] fn lines_09() { let text = "a\nb\n"; let r = Rope::from_str(text); let s = r.slice(..); assert_eq!(3, r.lines().count()); assert_eq!(3, s.lines().count()); let mut lines = r.lines(); assert_eq!("a\n", lines.next().unwrap()); assert_eq!("b\n", lines.next().unwrap()); assert_eq!("", lines.next().unwrap()); assert!(lines.next().is_none()); let mut lines = s.lines(); assert_eq!("a\n", lines.next().unwrap()); assert_eq!("b\n", lines.next().unwrap()); assert_eq!("", lines.next().unwrap()); assert!(lines.next().is_none()); } #[test] #[cfg_attr(miri, ignore)] fn lines_10() { let r = Rope::from_str(TEXT); let mut itr = r.lines(); assert_eq!(None, itr.prev()); assert_eq!(None, itr.prev()); } #[test] #[cfg_attr(miri, ignore)] fn lines_11() { let r = Rope::from_str(TEXT); let mut lines = Vec::new(); let mut itr = r.lines(); while let Some(line) = itr.next() { lines.push(line); } while let Some(line) = itr.prev() { assert_eq!(line, lines.pop().unwrap()); } assert!(lines.is_empty()); } #[test] #[cfg_attr(miri, ignore)] fn lines_12() { let r = Rope::from_str(TEXT); let s = r.slice(34..301); let mut lines = Vec::new(); let mut itr = s.lines(); while let Some(line) = itr.next() { lines.push(line); } while let Some(line) = itr.prev() { assert_eq!(line, lines.pop().unwrap()); } assert!(lines.is_empty()); } #[test] #[cfg_attr(miri, ignore)] fn lines_13() { let text = ""; let r = Rope::from_str(text); let s = r.slice(..); let mut lines = Vec::new(); let mut itr = s.lines(); while let Some(text) = itr.next() { lines.push(text); } while let Some(text) = itr.prev() { assert_eq!(text, lines.pop().unwrap()); } assert!(lines.is_empty()); } #[test] #[cfg_attr(miri, ignore)] fn lines_14() { let text = "a"; let r = Rope::from_str(text); let s = r.slice(..); let mut lines = Vec::new(); let mut itr = s.lines(); while let Some(text) = itr.next() { lines.push(text); } while let Some(text) = itr.prev() { assert_eq!(text, lines.pop().unwrap()); } assert!(lines.is_empty()); } #[test] #[cfg_attr(miri, ignore)] fn lines_15() { let text = "a\nb"; let r = Rope::from_str(text); let s = r.slice(..); let mut lines = Vec::new(); let mut itr = s.lines(); while let Some(text) = itr.next() { lines.push(text); } while let Some(text) = itr.prev() { assert_eq!(text, lines.pop().unwrap()); } assert!(lines.is_empty()); } #[test] #[cfg_attr(miri, ignore)] fn lines_16() { let text = "\n"; let r = Rope::from_str(text); let s = r.slice(..); let mut lines = Vec::new(); let mut itr = s.lines(); while let Some(text) = itr.next() { lines.push(text); } while let Some(text) = itr.prev() { assert_eq!(text, lines.pop().unwrap()); } assert!(lines.is_empty()); } #[test] #[cfg_attr(miri, ignore)] fn lines_17() { let text = "a\nb\n"; let r = Rope::from_str(text); let s = r.slice(..); let mut lines = Vec::new(); let mut itr = s.lines(); while let Some(text) = itr.next() { lines.push(text); } while let Some(text) = itr.prev() { assert_eq!(text, lines.pop().unwrap()); } assert!(lines.is_empty()); } #[test] #[cfg_attr(miri, ignore)] fn lines_at_01() { let r = Rope::from_str(TEXT); for i in 0..r.len_lines() { let line = r.line(i); let mut lines = r.lines_at(i); assert_eq!(Some(line), lines.next()); } let mut lines = r.lines_at(r.len_lines()); assert_eq!(None, lines.next()); } #[test] #[cfg_attr(miri, ignore)] fn lines_at_02() { let r = Rope::from_str(TEXT); let s = r.slice(34..301); for i in 0..s.len_lines() { let line = s.line(i); let mut lines = s.lines_at(i); assert_eq!(Some(line), lines.next()); } let mut lines = s.lines_at(s.len_lines()); assert_eq!(None, lines.next()); } #[test] #[cfg_attr(miri, ignore)] fn lines_at_03() { let r = Rope::from_str(TEXT); let s = r.slice(34..34); let mut lines = s.lines_at(0); assert_eq!("", lines.next().unwrap()); let mut lines = s.lines_at(1); assert_eq!(None, lines.next()); } #[test] #[cfg_attr(miri, ignore)] fn lines_exact_size_iter_01() { let r = Rope::from_str(TEXT); let s = r.slice(34..301); let mut line_count = s.len_lines(); let mut lines = s.lines(); assert_eq!(line_count, lines.len()); while let Some(_) = lines.next() { line_count -= 1; assert_eq!(line_count, lines.len()); } assert_eq!(lines.len(), 0); lines.next(); lines.next(); lines.next(); lines.next(); lines.next(); lines.next(); lines.next(); assert_eq!(lines.len(), 0); assert_eq!(line_count, 0); } #[test] #[cfg_attr(miri, ignore)] fn lines_exact_size_iter_02() { let r = Rope::from_str(TEXT); let s = r.slice(34..301); for i in 0..=s.len_lines() { let lines = s.lines_at(i); assert_eq!(s.len_lines() - i, lines.len()); } } #[test] #[cfg_attr(miri, ignore)] fn lines_exact_size_iter_03() { let r = Rope::from_str(TEXT); let s = r.slice(34..301); let mut line_count = 0; let mut lines = s.lines_at(s.len_lines()); assert_eq!(line_count, lines.len()); while lines.prev().is_some() { line_count += 1; assert_eq!(line_count, lines.len()); } assert_eq!(lines.len(), s.len_lines()); lines.prev(); lines.prev(); lines.prev(); lines.prev(); lines.prev(); lines.prev(); lines.prev(); assert_eq!(lines.len(), s.len_lines()); assert_eq!(line_count, s.len_lines()); } #[test] #[cfg_attr(miri, ignore)] fn lines_exact_size_iter_04() { // Make sure splitting CRLF pairs at the end works properly. let r = Rope::from_str("\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"); for i in 0..r.len_chars() { let s = r.slice(..i); let lines = s.lines(); if cfg!(any(feature = "cr_lines", feature = "unicode_lines")) { assert_eq!(lines.len(), 1 + ((i + 1) / 2)); } else { assert_eq!(lines.len(), 1 + (i / 2)); } assert_eq!(lines.len(), lines.count()); } } #[test] #[cfg_attr(miri, ignore)] fn lines_reverse_01() { let r = Rope::from_str(TEXT); let mut itr = r.lines(); let mut stack = Vec::new(); for _ in 0..8 { stack.push(itr.next().unwrap()); } itr.reverse(); for _ in 0..8 { assert_eq!(stack.pop().unwrap(), itr.next().unwrap()); } } #[test] #[cfg_attr(miri, ignore)] fn lines_reverse_02() { let r = Rope::from_str(TEXT); let mut itr = r.lines_at(r.len_lines() / 3); let mut stack = Vec::new(); for _ in 0..8 { stack.push(itr.next().unwrap()); } itr.reverse(); for _ in 0..8 { assert_eq!(stack.pop().unwrap(), itr.next().unwrap()); } } #[test] #[cfg_attr(miri, ignore)] fn lines_reverse_03() { let r = Rope::from_str(TEXT); let mut itr = r.lines_at(r.len_lines() / 3); let mut stack = Vec::new(); itr.reverse(); for _ in 0..8 { stack.push(itr.next().unwrap()); } itr.reverse(); for _ in 0..8 { assert_eq!(stack.pop().unwrap(), itr.next().unwrap()); } } #[test] #[cfg_attr(miri, ignore)] fn lines_reverse_04() { let mut itr = Lines::from_str("a\n", 1); assert_eq!(Some("a\n".into()), itr.next()); assert_eq!(Some("".into()), itr.next()); assert_eq!(None, itr.next()); itr.reverse(); assert_eq!(Some("".into()), itr.next()); assert_eq!(Some("a\n".into()), itr.next()); assert_eq!(None, itr.next()); } #[test] #[cfg_attr(miri, ignore)] fn lines_reverse_exact_size_iter_01() { let r = Rope::from_str(TEXT); let s = r.slice(34..301); let mut lines = s.lines_at(4); lines.reverse(); let mut line_count = 4; assert_eq!(4, lines.len()); while let Some(_) = lines.next() { line_count -= 1; assert_eq!(line_count, lines.len()); } lines.next(); lines.next(); lines.next(); lines.next(); lines.next(); lines.next(); lines.next(); assert_eq!(lines.len(), 0); assert_eq!(line_count, 0); } #[test] #[cfg_attr(miri, ignore)] fn lines_reverse_exact_size_iter_02() { // Make sure splitting CRLF pairs at the end works properly. let r = Rope::from_str("\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"); for i in 0..r.len_chars() { let s = r.slice(..i); let lines = s.lines_at((i + 1) / 2).reversed(); assert_eq!(lines.len(), lines.count()); } } #[test] #[cfg_attr(miri, ignore)] fn chunks_01() { let r = Rope::from_str(TEXT); let mut idx = 0; for chunk in r.chunks() { assert_eq!(chunk, &TEXT[idx..(idx + chunk.len())]); idx += chunk.len(); } } #[test] #[cfg_attr(miri, ignore)] fn chunks_02() { let r = Rope::from_str(""); let mut itr = r.chunks(); assert_eq!(None, itr.next()); } #[test] #[cfg_attr(miri, ignore)] fn chunks_03() { let r = Rope::from_str(TEXT); let mut itr = r.chunks(); assert_eq!(None, itr.prev()); } #[test] #[cfg_attr(miri, ignore)] fn chunks_04() { let r = Rope::from_str(TEXT); let mut chunks = Vec::new(); let mut itr = r.chunks(); while let Some(text) = itr.next() { chunks.push(text); } while let Some(text) = itr.prev() { assert_eq!(text, chunks.pop().unwrap()); } assert!(chunks.is_empty()); } #[test] #[cfg_attr(miri, ignore)] fn chunks_at_byte_01() { let r = Rope::from_str(TEXT); for i in 0..r.len_bytes() { let (chunk, b, c, l) = r.chunk_at_byte(i); let (mut chunks, bs, cs, ls) = r.chunks_at_byte(i); assert_eq!(b, bs); assert_eq!(c, cs); assert_eq!(l, ls); assert_eq!(Some(chunk), chunks.next()); } } #[test] #[cfg_attr(miri, ignore)] fn chunks_at_byte_02() { let r = Rope::from_str(TEXT); let s = r.slice(34..301); for i in 0..(s.len_chars() + 1) { let (chunk, b, c, l) = s.chunk_at_byte(i); let (mut chunks, bs, cs, ls) = s.chunks_at_byte(i); assert_eq!(b, bs); assert_eq!(c, cs); assert_eq!(l, ls); assert_eq!(Some(chunk), chunks.next()); } } #[test] #[cfg_attr(miri, ignore)] fn chunks_at_byte_03() { let r = Rope::from_str(TEXT); let s = r.slice(34..301); let (mut chunks, _, _, _) = s.chunks_at_byte(s.len_bytes()); assert_eq!(chunks.next(), None); let (mut chunks, _, _, _) = s.chunks_at_byte(s.len_bytes()); assert_eq!(s.chunks().last(), chunks.prev()); } #[test] #[cfg_attr(miri, ignore)] fn chunks_at_byte_04() { let r = Rope::from_str(TEXT); let s = r.slice(34..34); let (mut chunks, _, _, _) = s.chunks_at_byte(0); assert_eq!(chunks.next(), None); let (mut chunks, _, _, _) = s.chunks_at_byte(0); assert_eq!(chunks.prev(), None); } #[test] #[cfg_attr(miri, ignore)] fn chunks_at_char_01() { let r = Rope::from_str(TEXT); for i in 0..r.len_chars() { let (chunk, b, c, l) = r.chunk_at_char(i); let (mut chunks, bs, cs, ls) = r.chunks_at_char(i); assert_eq!(b, bs); assert_eq!(c, cs); assert_eq!(l, ls); assert_eq!(Some(chunk), chunks.next()); } } #[test] #[cfg_attr(miri, ignore)] fn chunks_at_char_02() { let r = Rope::from_str(TEXT); let s = r.slice(34..301); for i in 0..s.len_chars() { let (chunk, b, c, l) = s.chunk_at_char(i); let (mut chunks, bs, cs, ls) = s.chunks_at_char(i); assert_eq!(b, bs); assert_eq!(c, cs); assert_eq!(l, ls); assert_eq!(Some(chunk), chunks.next()); } } #[test] #[cfg_attr(miri, ignore)] fn chunks_at_char_03() { let r = Rope::from_str(TEXT); let s = r.slice(34..301); let (mut chunks, _, _, _) = s.chunks_at_char(s.len_chars()); assert_eq!(chunks.next(), None); } #[test] #[cfg_attr(miri, ignore)] fn chunks_at_char_04() { let r = Rope::from_str(TEXT); let s = r.slice(34..34); let (mut chunks, _, _, _) = s.chunks_at_char(0); assert_eq!(chunks.next(), None); let (mut chunks, _, _, _) = s.chunks_at_char(0); assert_eq!(chunks.prev(), None); } #[test] #[cfg_attr(miri, ignore)] fn chunks_at_line_break_01() { let r = Rope::from_str(TEXT); for i in 0..r.len_lines() { let (chunk, b, c, l) = r.chunk_at_line_break(i); let (mut chunks, bs, cs, ls) = r.chunks_at_line_break(i); assert_eq!(b, bs); assert_eq!(c, cs); assert_eq!(l, ls); assert_eq!(Some(chunk), chunks.next()); } } #[test] #[cfg_attr(miri, ignore)] fn chunks_at_line_break_02() { let r = Rope::from_str(TEXT); let s = r.slice(34..301); for i in 0..s.len_lines() { let (chunk, b, c, l) = s.chunk_at_line_break(i); let (mut chunks, bs, cs, ls) = s.chunks_at_line_break(i); assert_eq!(Some(chunk), chunks.next()); assert_eq!(b, bs); assert_eq!(c, cs); assert_eq!(l, ls); } } #[test] #[cfg_attr(miri, ignore)] fn chunks_at_line_break_03() { let r = Rope::from_str(TEXT); let s = r.slice(34..301); let (mut chunks, _, _, _) = s.chunks_at_line_break(s.len_lines()); assert_eq!(chunks.next(), None); } #[test] #[cfg_attr(miri, ignore)] fn chunks_at_line_break_04() { let r = Rope::from_str(TEXT); let s = r.slice(34..34); let (mut chunks, _, _, _) = s.chunks_at_line_break(0); assert_eq!(chunks.next(), None); let (mut chunks, _, _, _) = s.chunks_at_line_break(0); assert_eq!(chunks.prev(), None); } #[test] #[cfg_attr(miri, ignore)] fn chunks_reverse_01() { let r = Rope::from_str(TEXT); let mut itr = r.chunks(); let mut stack = Vec::new(); for _ in 0..8 { stack.push(itr.next().unwrap()); } itr.reverse(); for _ in 0..8 { assert_eq!(stack.pop().unwrap(), itr.next().unwrap()); } } #[test] #[cfg_attr(miri, ignore)] fn chunks_reverse_02() { let r = Rope::from_str(TEXT); let mut itr = r.chunks_at_char(r.len_chars() / 3).0; let mut stack = Vec::new(); for _ in 0..8 { stack.push(itr.next().unwrap()); } itr.reverse(); for _ in 0..8 { assert_eq!(stack.pop().unwrap(), itr.next().unwrap()); } } #[test] #[cfg_attr(miri, ignore)] fn chunks_reverse_03() { let r = Rope::from_str(TEXT); let mut itr = r.chunks_at_char(r.len_chars() / 3).0; let mut stack = Vec::new(); itr.reverse(); for _ in 0..8 { stack.push(itr.next().unwrap()); } itr.reverse(); for _ in 0..8 { assert_eq!(stack.pop().unwrap(), itr.next().unwrap()); } } #[test] #[cfg_attr(miri, ignore)] fn chunks_reverse_04() { let mut itr = Chunks::from_str("a\n", false); assert_eq!(Some("a\n"), itr.next()); assert_eq!(None, itr.next()); itr.reverse(); assert_eq!(Some("a\n"), itr.next()); assert_eq!(None, itr.next()); } #[test] #[cfg_attr(miri, ignore)] fn bytes_sliced_01() { let r = Rope::from_str(TEXT); let s_start = 34; let s_end = 301; let s_start_byte = r.char_to_byte(s_start); let s_end_byte = r.char_to_byte(s_end); let s1 = r.slice(s_start..s_end); let s2 = &TEXT[s_start_byte..s_end_byte]; let mut s1_bytes = s1.bytes(); let mut s2_bytes = s2.bytes(); assert_eq!(s1, s2); assert_eq!(s1.byte(0), s2.as_bytes()[0]); assert_eq!(s1.len_bytes(), s2.len()); assert_eq!(s1_bytes.len(), s2.len()); for _ in 0..(s2.len() + 1) { assert_eq!(s1_bytes.next(), s2_bytes.next()); } assert_eq!(s1_bytes.len(), 0); } #[test] #[cfg_attr(miri, ignore)] fn bytes_sliced_reverse_01() { let r = Rope::from_str(TEXT); let s_start = 34; let s_end = 301; let s = r.slice(s_start..s_end); let mut itr = s.bytes(); let mut stack = Vec::new(); for _ in 0..32 { stack.push(itr.next().unwrap()); } itr.reverse(); for _ in 0..32 { assert_eq!(stack.pop(), itr.next()); } } #[test] #[cfg_attr(miri, ignore)] fn bytes_at_sliced_01() { let r = Rope::from_str(TEXT); let s_start = 34; let s_end = 301; let s_start_byte = r.char_to_byte(s_start); let s_end_byte = r.char_to_byte(s_end); let s1 = r.slice(s_start..s_end); let s2 = &TEXT[s_start_byte..s_end_byte]; let mut bytes_1 = s2.bytes(); for i in 0..(s1.len_bytes() + 1) { let mut bytes_2 = s1.bytes_at(i); assert_eq!(bytes_1.next(), bytes_2.next()); } } #[test] #[cfg_attr(miri, ignore)] fn bytes_at_sliced_02() { let r = Rope::from_str(TEXT); let s = r.slice(34..301); let mut bytes = s.bytes_at(s.len_bytes()); assert_eq!(bytes.next(), None); } #[test] #[cfg_attr(miri, ignore)] fn bytes_at_sliced_03() { let r = Rope::from_str(TEXT); let s_start = 34; let s_end = 301; let s_start_byte = r.char_to_byte(s_start); let s_end_byte = r.char_to_byte(s_end); let s1 = r.slice(s_start..s_end); let s2 = &TEXT[s_start_byte..s_end_byte]; let mut bytes_1 = s1.bytes_at(s1.len_bytes()); let mut bytes_2 = s2.bytes(); while let Some(b) = bytes_2.next_back() { assert_eq!(bytes_1.prev(), Some(b)); } } #[test] #[cfg_attr(miri, ignore)] fn bytes_at_sliced_reverse_01() { let r = Rope::from_str(TEXT); let s_start = 34; let s_end = 301; let s = r.slice(s_start..s_end); let mut itr = s.bytes_at(s.len_bytes() / 3); let mut stack = Vec::new(); for _ in 0..32 { stack.push(itr.next().unwrap()); } itr.reverse(); for _ in 0..32 { assert_eq!(stack.pop(), itr.next()); } } #[test] #[cfg_attr(miri, ignore)] fn chars_sliced_01() { let r = Rope::from_str(TEXT); let s_start = 34; let s_end = 301; let s_start_byte = r.char_to_byte(s_start); let s_end_byte = r.char_to_byte(s_end); let s1 = r.slice(s_start..s_end); let s2 = &TEXT[s_start_byte..s_end_byte]; for (cr, ct) in s1.chars().zip(s2.chars()) { assert_eq!(cr, ct); } } #[test] #[cfg_attr(miri, ignore)] fn chars_sliced_reverse_01() { let r = Rope::from_str(TEXT); let s_start = 34; let s_end = 301; let s = r.slice(s_start..s_end); let mut itr = s.chars(); let mut stack = Vec::new(); for _ in 0..32 { stack.push(itr.next().unwrap()); } itr.reverse(); for _ in 0..32 { assert_eq!(stack.pop(), itr.next()); } } #[test] #[cfg_attr(miri, ignore)] fn chars_at_sliced_01() { let r = Rope::from_str(TEXT); let s_start = 34; let s_end = 301; let s_start_byte = r.char_to_byte(s_start); let s_end_byte = r.char_to_byte(s_end); let s1 = r.slice(s_start..s_end); let s2 = &TEXT[s_start_byte..s_end_byte]; let mut chars_1 = s2.chars(); for i in 0..(s1.len_chars() + 1) { let mut chars_2 = s1.chars_at(i); assert_eq!(chars_1.next(), chars_2.next()); } } #[test] #[cfg_attr(miri, ignore)] fn chars_at_sliced_02() { let r = Rope::from_str(TEXT); let s = r.slice(34..301); let mut chars = s.chars_at(s.len_chars()); assert_eq!(chars.next(), None); } #[test] #[cfg_attr(miri, ignore)] fn chars_at_sliced_03() { let r = Rope::from_str(TEXT); let s_start = 34; let s_end = 301; let s_start_char = r.char_to_byte(s_start); let s_end_char = r.char_to_byte(s_end); let s1 = r.slice(s_start..s_end); let s2 = &TEXT[s_start_char..s_end_char]; let mut chars_1 = s1.chars_at(s1.len_chars()); let mut chars_2 = s2.chars(); while let Some(c) = chars_2.next_back() { assert_eq!(chars_1.prev(), Some(c)); } } #[test] #[cfg_attr(miri, ignore)] fn chars_at_sliced_reverse_01() { let r = Rope::from_str(TEXT); let s_start = 34; let s_end = 301; let s = r.slice(s_start..s_end); let mut itr = s.chars_at(s.len_chars() / 3); let mut stack = Vec::new(); for _ in 0..32 { stack.push(itr.next().unwrap()); } itr.reverse(); for _ in 0..32 { assert_eq!(stack.pop(), itr.next()); } } #[test] #[cfg_attr(miri, ignore)] fn lines_sliced_01() { let r = Rope::from_str(TEXT); let s_start = 34; let s_end = 301; let s_start_byte = r.char_to_byte(s_start); let s_end_byte = r.char_to_byte(s_end); let s1 = r.slice(s_start..s_end); let s2 = &TEXT[s_start_byte..s_end_byte]; for (liner, linet) in s1.lines().zip(s2.lines()) { assert_eq!(liner.to_string().trim_end(), linet); } } #[test] #[cfg_attr(miri, ignore)] fn lines_sliced_reverse_01() { let r = Rope::from_str(TEXT); let s_start = 34; let s_end = 301; let s = r.slice(s_start..s_end); let mut itr = s.lines(); let mut stack = Vec::new(); for _ in 0..4 { stack.push(itr.next().unwrap()); } itr.reverse(); for _ in 0..4 { assert_eq!(stack.pop().unwrap(), itr.next().unwrap()); } } #[test] #[cfg_attr(miri, ignore)] fn chunks_sliced_01() { let r = Rope::from_str(TEXT); let s_start = 34; let s_end = 301; let s_start_byte = r.char_to_byte(s_start); let s_end_byte = r.char_to_byte(s_end); let s1 = r.slice(s_start..s_end); let s2 = &TEXT[s_start_byte..s_end_byte]; let mut idx = 0; for chunk in s1.chunks() { assert_eq!(chunk, &s2[idx..(idx + chunk.len())]); idx += chunk.len(); } assert_eq!(idx, s2.len()); } #[test] #[cfg_attr(miri, ignore)] fn chunks_sliced_reverse_01() { let r = Rope::from_str(TEXT); let s_start = 34; let s_end = 301; let s = r.slice(s_start..s_end); let mut itr = s.chunks(); let mut stack = Vec::new(); for _ in 0..8 { stack.push(itr.next().unwrap()); } itr.reverse(); for _ in 0..8 { assert_eq!(stack.pop(), itr.next()); } } #[test] #[cfg_attr(miri, ignore)] fn empty_iter() { let rope = Rope::from_str(""); let r: Vec<_> = rope.lines().collect(); assert_eq!(&[""], &*r) } } ropey-1.6.1/src/lib.rs000064400000000000000000000335071046102023000127020ustar 00000000000000//! Ropey is a utf8 text rope for Rust. It is fast, robust, and can handle //! huge texts and memory-incoherent edits with ease. //! //! Ropey's atomic unit of text is Unicode scalar values (or `char`s in Rust) //! encoded as utf8. All of Ropey's editing and slicing operations are done //! in terms of char indices, which prevents accidental creation of invalid //! utf8 data. //! //! The library is made up of four main components: //! //! - [`Rope`]: the main rope type. //! - [`RopeSlice`]: an immutable view into part of a //! `Rope`. //! - [`iter`]: iterators over `Rope`/`RopeSlice` data. //! - [`RopeBuilder`]: an efficient incremental //! `Rope` builder. //! //! //! # A Basic Example //! //! Let's say we want to open up a text file, replace the 516th line (the //! writing was terrible!), and save it back to disk. It's contrived, but will //! give a good sampling of the APIs and how they work together. //! //! ```no_run //! # use std::io::Result; //! use std::fs::File; //! use std::io::{BufReader, BufWriter}; //! use ropey::Rope; //! //! # fn do_stuff() -> Result<()> { //! // Load a text file. //! let mut text = Rope::from_reader( //! BufReader::new(File::open("my_great_book.txt")?) //! )?; //! //! // Print the 516th line (zero-indexed) to see the terrible //! // writing. //! println!("{}", text.line(515)); //! //! // Get the start/end char indices of the line. //! let start_idx = text.line_to_char(515); //! let end_idx = text.line_to_char(516); //! //! // Remove the line... //! text.remove(start_idx..end_idx); //! //! // ...and replace it with something better. //! text.insert(start_idx, "The flowers are... so... dunno.\n"); //! //! // Print the changes, along with the previous few lines for context. //! let start_idx = text.line_to_char(511); //! let end_idx = text.line_to_char(516); //! println!("{}", text.slice(start_idx..end_idx)); //! //! // Write the file back out to disk. //! text.write_to( //! BufWriter::new(File::create("my_great_book.txt")?) //! )?; //! # Ok(()) //! # } //! # do_stuff().unwrap(); //! ``` //! //! More examples can be found in the `examples` directory of the git //! repository. Many of those examples demonstrate doing non-trivial things //! with Ropey such as grapheme handling, search-and-replace, and streaming //! loading of non-utf8 text files. //! //! //! # Low-level APIs //! //! Ropey also provides access to some of its low-level APIs, enabling client //! code to efficiently work with a `Rope`'s data and implement new //! functionality. The most important of those API's are: //! //! - The [`chunk_at_*()`](Rope::chunk_at_byte) //! chunk-fetching methods of `Rope` and `RopeSlice`. //! - The [`Chunks`](iter::Chunks) iterator. //! - The functions in [`str_utils`] for operating on //! `&str` slices. //! //! Internally, each `Rope` stores text as a segemented collection of utf8 //! strings. The chunk-fetching methods and `Chunks` iterator provide direct //! access to those strings (or "chunks") as `&str` slices, allowing client //! code to work directly with the underlying utf8 data. //! //! The chunk-fetching methods and `str_utils` functions are the basic //! building blocks that Ropey itself uses to build much of its functionality. //! For example, the [`Rope::byte_to_char()`] //! method can be reimplemented as a free function like this: //! //! ```no_run //! use ropey::{ //! Rope, //! str_utils::byte_to_char_idx //! }; //! //! fn byte_to_char(rope: &Rope, byte_idx: usize) -> usize { //! let (chunk, b, c, _) = rope.chunk_at_byte(byte_idx); //! c + byte_to_char_idx(chunk, byte_idx - b) //! } //! ``` //! //! And this will be just as efficient as Ropey's implementation. //! //! The chunk-fetching methods in particular are among the fastest functions //! that Ropey provides, generally operating in the sub-hundred nanosecond //! range for medium-sized (~200kB) documents on recent-ish computer systems. //! //! //! # A Note About Line Breaks //! //! Some of Ropey's APIs use the concept of line breaks or lines of text. //! //! Ropey considers the start of the rope and positions immediately //! _after_ line breaks to be the start of new lines. And it treats //! line breaks as being a part of the lines they mark the end of. //! //! For example, the rope `"Hello"` has a single line: `"Hello"`. The //! rope `"Hello\nworld"` has two lines: `"Hello\n"` and `"world"`. And //! the rope `"Hello\nworld\n"` has three lines: `"Hello\n"`, //! `"world\n"`, and `""`. //! //! Ropey can be configured at build time via feature flags to recognize //! different line breaks. Ropey always recognizes: //! //! - `U+000A` — LF (Line Feed) //! - `U+000D` `U+000A` — CRLF (Carriage Return + Line Feed) //! //! With the `cr_lines` feature, the following are also recognized: //! //! - `U+000D` — CR (Carriage Return) //! //! With the `unicode_lines` feature, in addition to all of the //! above, the following are also recognized (bringing Ropey into //! conformance with //! [Unicode Annex #14](https://www.unicode.org/reports/tr14/#BK)): //! //! - `U+000B` — VT (Vertical Tab) //! - `U+000C` — FF (Form Feed) //! - `U+0085` — NEL (Next Line) //! - `U+2028` — Line Separator //! - `U+2029` — Paragraph Separator //! //! (Note: `unicode_lines` is enabled by default, and always implies //! `cr_lines`.) //! //! CRLF pairs are always treated as a single line break, and are never split //! across chunks. Note, however, that slicing can still split them. //! //! //! # A Note About SIMD Acceleration //! //! Ropey has a `simd` feature flag (enabled by default) that enables //! explicit SIMD on supported platforms to improve performance. //! //! There is a bit of a footgun here: if you disable default features to //! configure line break behavior (as per the section above) then SIMD //! will also get disabled, and performance will suffer. So be careful //! to explicitly re-enable the `simd` feature flag (if desired) when //! doing that. #![allow(clippy::collapsible_if)] #![allow(clippy::inline_always)] #![allow(clippy::needless_return)] #![allow(clippy::redundant_field_names)] #![allow(clippy::type_complexity)] extern crate smallvec; extern crate str_indices; mod crlf; mod rope; mod rope_builder; mod slice; mod tree; pub mod iter; pub mod str_utils; use std::ops::Bound; pub use crate::rope::Rope; pub use crate::rope_builder::RopeBuilder; pub use crate::slice::RopeSlice; /// NOT PART OF THE PUBLIC API (hidden from docs for a reason!) /// These are only exposed for tests that live in the `tests` directory. #[doc(hidden)] pub use crate::tree::{MAX_BYTES, MAX_CHILDREN, MIN_BYTES, MIN_CHILDREN}; //============================================================== // Error reporting types. /// Ropey's result type. pub type Result = std::result::Result; /// Ropey's error type. #[derive(Clone, Copy)] #[non_exhaustive] pub enum Error { /// Indicates that the passed byte index was out of bounds. /// /// Contains the index attempted and the actual length of the /// `Rope`/`RopeSlice` in bytes, in that order. ByteIndexOutOfBounds(usize, usize), /// Indicates that the passed char index was out of bounds. /// /// Contains the index attempted and the actual length of the /// `Rope`/`RopeSlice` in chars, in that order. CharIndexOutOfBounds(usize, usize), /// Indicates that the passed line index was out of bounds. /// /// Contains the index attempted and the actual length of the /// `Rope`/`RopeSlice` in lines, in that order. LineIndexOutOfBounds(usize, usize), /// Indicates that the passed utf16 code-unit index was out of /// bounds. /// /// Contains the index attempted and the actual length of the /// `Rope`/`RopeSlice` in utf16 code units, in that order. Utf16IndexOutOfBounds(usize, usize), /// Indicates that the passed byte index was not a char boundary. /// /// Contains the passed byte index. ByteIndexNotCharBoundary(usize), /// Indicates that the passed byte range didn't line up with char /// boundaries. /// /// Contains the [start, end) byte indices of the range, in that order. /// When either the start or end are `None`, that indicates a half-open /// range. ByteRangeNotCharBoundary( Option, // Start. Option, // End. ), /// Indicates that a reversed byte-index range (end < start) was /// encountered. /// /// Contains the [start, end) byte indices of the range, in that order. ByteRangeInvalid( usize, // Start. usize, // End. ), /// Indicates that a reversed char-index range (end < start) was /// encountered. /// /// Contains the [start, end) char indices of the range, in that order. CharRangeInvalid( usize, // Start. usize, // End. ), /// Indicates that the passed byte-index range was partially or fully /// out of bounds. /// /// Contains the [start, end) byte indices of the range and the actual /// length of the `Rope`/`RopeSlice` in bytes, in that order. When /// either the start or end are `None`, that indicates a half-open range. ByteRangeOutOfBounds( Option, // Start. Option, // End. usize, // Rope byte length. ), /// Indicates that the passed char-index range was partially or fully /// out of bounds. /// /// Contains the [start, end) char indices of the range and the actual /// length of the `Rope`/`RopeSlice` in chars, in that order. When /// either the start or end are `None`, that indicates a half-open range. CharRangeOutOfBounds( Option, // Start. Option, // End. usize, // Rope char length. ), } impl std::error::Error for Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None } // Deprecated in std. fn description(&self) -> &str { "" } // Deprecated in std. fn cause(&self) -> Option<&dyn std::error::Error> { None } } impl std::fmt::Debug for Error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match *self { Error::ByteIndexOutOfBounds(index, len) => { write!( f, "Byte index out of bounds: byte index {}, Rope/RopeSlice byte length {}", index, len ) } Error::CharIndexOutOfBounds(index, len) => { write!( f, "Char index out of bounds: char index {}, Rope/RopeSlice char length {}", index, len ) } Error::LineIndexOutOfBounds(index, len) => { write!( f, "Line index out of bounds: line index {}, Rope/RopeSlice line count {}", index, len ) } Error::Utf16IndexOutOfBounds(index, len) => { write!(f, "Utf16 code-unit index out of bounds: utf16 index {}, Rope/RopeSlice utf16 length {}", index, len) } Error::ByteIndexNotCharBoundary(index) => { write!( f, "Byte index is not a valid char boundary: byte index {}", index ) } Error::ByteRangeNotCharBoundary(start_idx_opt, end_idx_opt) => { write!(f, "Byte range does not align with char boundaries: range ")?; write_range(f, start_idx_opt, end_idx_opt) } Error::ByteRangeInvalid(start_idx, end_idx) => { write!( f, "Invalid byte range {}..{}: start must be <= end", start_idx, end_idx ) } Error::CharRangeInvalid(start_idx, end_idx) => { write!( f, "Invalid char range {}..{}: start must be <= end", start_idx, end_idx ) } Error::ByteRangeOutOfBounds(start_idx_opt, end_idx_opt, len) => { write!(f, "Byte range out of bounds: byte range ")?; write_range(f, start_idx_opt, end_idx_opt)?; write!(f, ", Rope/RopeSlice byte length {}", len) } Error::CharRangeOutOfBounds(start_idx_opt, end_idx_opt, len) => { write!(f, "Char range out of bounds: char range ")?; write_range(f, start_idx_opt, end_idx_opt)?; write!(f, ", Rope/RopeSlice char length {}", len) } } } } impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // Just re-use the debug impl. std::fmt::Debug::fmt(self, f) } } fn write_range( f: &mut std::fmt::Formatter<'_>, start_idx: Option, end_idx: Option, ) -> std::fmt::Result { match (start_idx, end_idx) { (None, None) => { write!(f, "..") } (Some(start), None) => { write!(f, "{}..", start) } (None, Some(end)) => { write!(f, "..{}", end) } (Some(start), Some(end)) => { write!(f, "{}..{}", start, end) } } } //============================================================== // Range handling utilities. #[inline(always)] pub(crate) fn start_bound_to_num(b: Bound<&usize>) -> Option { match b { Bound::Included(n) => Some(*n), Bound::Excluded(n) => Some(*n + 1), Bound::Unbounded => None, } } #[inline(always)] pub(crate) fn end_bound_to_num(b: Bound<&usize>) -> Option { match b { Bound::Included(n) => Some(*n + 1), Bound::Excluded(n) => Some(*n), Bound::Unbounded => None, } } ropey-1.6.1/src/rope.rs000064400000000000000000003136161046102023000131030ustar 00000000000000use std::io; use std::iter::FromIterator; use std::ops::RangeBounds; use std::sync::Arc; use crate::crlf; use crate::iter::{Bytes, Chars, Chunks, Lines}; use crate::rope_builder::RopeBuilder; use crate::slice::RopeSlice; use crate::str_utils::{ byte_to_char_idx, byte_to_line_idx, byte_to_utf16_surrogate_idx, char_to_byte_idx, char_to_line_idx, line_to_byte_idx, line_to_char_idx, utf16_code_unit_to_char_idx, }; use crate::tree::{Count, Node, NodeChildren, TextInfo, MAX_BYTES, MIN_BYTES}; use crate::{end_bound_to_num, start_bound_to_num, Error, Result}; /// A utf8 text rope. /// /// The time complexity of nearly all edit and query operations on `Rope` are /// worst-case `O(log N)` in the length of the rope. `Rope` is designed to /// work efficiently even for huge (in the gigabytes) and pathological (all on /// one line) texts. /// /// # Editing Operations /// /// The primary editing operations on `Rope` are insertion and removal of text. /// For example: /// /// ``` /// # use ropey::Rope; /// # /// let mut rope = Rope::from_str("Hello みんなさん!"); /// rope.remove(6..11); /// rope.insert(6, "world"); /// /// assert_eq!(rope, "Hello world!"); /// ``` /// /// # Query Operations /// /// `Rope` provides a rich set of efficient query functions, including querying /// rope length in bytes/`char`s/lines, fetching individual `char`s or lines, /// and converting between byte/`char`/line indices. For example, to find the /// starting `char` index of a given line: /// /// ``` /// # use ropey::Rope; /// # /// let rope = Rope::from_str("Hello みんなさん!\nHow are you?\nThis text has multiple lines!"); /// /// assert_eq!(rope.line_to_char(0), 0); /// assert_eq!(rope.line_to_char(1), 13); /// assert_eq!(rope.line_to_char(2), 26); /// ``` /// /// # Slicing /// /// You can take immutable slices of a `Rope` using `slice()`: /// /// ``` /// # use ropey::Rope; /// # /// let mut rope = Rope::from_str("Hello みんなさん!"); /// let middle = rope.slice(3..8); /// /// assert_eq!(middle, "lo みん"); /// ``` /// /// # Cloning /// /// Cloning `Rope`s is extremely cheap, running in `O(1)` time and taking a /// small constant amount of memory for the new clone, regardless of text size. /// This is accomplished by data sharing between `Rope` clones. The memory /// used by clones only grows incrementally as the their contents diverge due /// to edits. All of this is thread safe, so clones can be sent freely /// between threads. /// /// The primary intended use-case for this feature is to allow asynchronous /// processing of `Rope`s. For example, saving a large document to disk in a /// separate thread while the user continues to perform edits. #[derive(Clone)] pub struct Rope { pub(crate) root: Arc, } impl Rope { //----------------------------------------------------------------------- // Constructors /// Creates an empty `Rope`. #[inline] pub fn new() -> Self { Rope { root: Arc::new(Node::new()), } } /// Creates a `Rope` from a string slice. /// /// Runs in O(N) time. #[inline] #[allow(clippy::should_implement_trait)] pub fn from_str(text: &str) -> Self { RopeBuilder::new().build_at_once(text) } /// Creates a `Rope` from the output of a reader. /// /// This is a convenience function, and provides *no specific guarantees* /// about performance or internal implementation aside from the runtime /// complexity listed below. /// /// When more precise control over IO behavior, buffering, etc. is desired, /// you should handle IO yourself and use [`RopeBuilder`] to build the /// `Rope`. /// /// Runs in O(N) time. /// /// # Errors /// /// - If the reader returns an error, `from_reader` stops and returns /// that error. /// - If non-utf8 data is encountered, an IO error with kind /// `InvalidData` is returned. /// /// Note: some data from the reader is likely consumed even if there is /// an error. #[allow(unused_mut)] pub fn from_reader(mut reader: T) -> io::Result { const BUFFER_SIZE: usize = MAX_BYTES * 2; let mut builder = RopeBuilder::new(); let mut buffer = [0u8; BUFFER_SIZE]; let mut fill_idx = 0; // How much `buffer` is currently filled with valid data loop { match reader.read(&mut buffer[fill_idx..]) { Ok(read_count) => { fill_idx += read_count; // Determine how much of the buffer is valid utf8. let valid_count = match std::str::from_utf8(&buffer[..fill_idx]) { Ok(_) => fill_idx, Err(e) => e.valid_up_to(), }; // Append the valid part of the buffer to the rope. if valid_count > 0 { // The unsafe block here is reinterpreting the bytes as // utf8. This is safe because the bytes being // reinterpreted have already been validated as utf8 // just above. builder.append(unsafe { std::str::from_utf8_unchecked(&buffer[..valid_count]) }); } // Shift the un-read part of the buffer to the beginning. if valid_count < fill_idx { buffer.copy_within(valid_count..fill_idx, 0); } fill_idx -= valid_count; if fill_idx == BUFFER_SIZE { // Buffer is full and none of it could be consumed. Utf8 // codepoints don't get that large, so it's clearly not // valid text. return Err(io::Error::new( io::ErrorKind::InvalidData, "stream did not contain valid UTF-8", )); } // If we're done reading if read_count == 0 { if fill_idx > 0 { // We couldn't consume all data. return Err(io::Error::new( io::ErrorKind::InvalidData, "stream contained invalid UTF-8", )); } else { return Ok(builder.finish()); } } } Err(e) => { // Read error return Err(e); } } } } //----------------------------------------------------------------------- // Convenience output methods /// Writes the contents of the `Rope` to a writer. /// /// This is a convenience function, and provides *no specific guarantees* /// about performance or internal implementation aside from the runtime /// complexity listed below. /// /// When more precise control over IO behavior, buffering, etc. is /// desired, you should handle IO yourself and use the [`Chunks`] /// iterator to iterate through the `Rope`'s contents. /// /// Runs in O(N) time. /// /// # Errors /// /// - If the writer returns an error, `write_to` stops and returns that /// error. /// /// Note: some data may have been written even if an error is returned. #[allow(unused_mut)] pub fn write_to(&self, mut writer: T) -> io::Result<()> { for chunk in self.chunks() { writer.write_all(chunk.as_bytes())?; } Ok(()) } //----------------------------------------------------------------------- // Informational methods /// Total number of bytes in the `Rope`. /// /// Runs in O(1) time. #[inline] pub fn len_bytes(&self) -> usize { self.root.byte_count() } /// Total number of chars in the `Rope`. /// /// Runs in O(1) time. #[inline] pub fn len_chars(&self) -> usize { self.root.char_count() } /// Total number of lines in the `Rope`. /// /// Runs in O(1) time. #[inline] pub fn len_lines(&self) -> usize { self.root.line_break_count() + 1 } /// Total number of utf16 code units that would be in `Rope` if it were /// encoded as utf16. /// /// Ropey stores text internally as utf8, but sometimes it is necessary /// to interact with external APIs that still use utf16. This function is /// primarily intended for such situations, and is otherwise not very /// useful. /// /// Runs in O(1) time. #[inline] pub fn len_utf16_cu(&self) -> usize { let info = self.root.text_info(); (info.chars + info.utf16_surrogates) as usize } //----------------------------------------------------------------------- // Memory management methods /// Total size of the `Rope`'s text buffer space, in bytes. /// /// This includes unoccupied text buffer space. You can calculate /// the unoccupied space with `capacity() - len_bytes()`. In general, /// there will always be some unoccupied buffer space. /// /// Runs in O(N) time. pub fn capacity(&self) -> usize { let mut byte_count = 0; for chunk in self.chunks() { byte_count += chunk.len().max(MAX_BYTES); } byte_count } /// Shrinks the `Rope`'s capacity to the minimum possible. /// /// This will rarely result in `capacity() == len_bytes()`. `Rope` /// stores text in a sequence of fixed-capacity chunks, so an exact fit /// only happens for texts that are both a precise multiple of that /// capacity _and_ have code point boundaries that line up exactly with /// the capacity boundaries. /// /// After calling this, the difference between `capacity()` and /// `len_bytes()` is typically under 1KB per megabyte of text in the /// `Rope`. /// /// **NOTE:** calling this on a `Rope` clone causes it to stop sharing /// all data with its other clones. In such cases you will very likely /// be _increasing_ total memory usage despite shrinking the `Rope`'s /// capacity. /// /// Runs in O(N) time, and uses O(log N) additional space during /// shrinking. pub fn shrink_to_fit(&mut self) { let mut node_stack = Vec::new(); let mut builder = RopeBuilder::new(); node_stack.push(self.root.clone()); *self = Rope::new(); loop { if node_stack.is_empty() { break; } if node_stack.last().unwrap().is_leaf() { builder.append(node_stack.last().unwrap().leaf_text()); node_stack.pop(); } else if node_stack.last().unwrap().child_count() == 0 { node_stack.pop(); } else { let (_, next_node) = Arc::make_mut(node_stack.last_mut().unwrap()) .children_mut() .remove(0); node_stack.push(next_node); } } *self = builder.finish(); } //----------------------------------------------------------------------- // Edit methods /// Inserts `text` at char index `char_idx`. /// /// Runs in O(M + log N) time, where N is the length of the `Rope` and M /// is the length of `text`. /// /// # Panics /// /// Panics if `char_idx` is out of bounds (i.e. `char_idx > len_chars()`). #[inline] pub fn insert(&mut self, char_idx: usize, text: &str) { // Bounds check self.try_insert(char_idx, text).unwrap() } /// Inserts a single char `ch` at char index `char_idx`. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `char_idx` is out of bounds (i.e. `char_idx > len_chars()`). #[inline] pub fn insert_char(&mut self, char_idx: usize, ch: char) { self.try_insert_char(char_idx, ch).unwrap() } /// Private internal-only method that does a single insertion of /// sufficiently small text. /// /// This only works correctly for insertion texts smaller than or equal to /// `MAX_BYTES - 4`. /// /// Note that a lot of the complexity in this method comes from avoiding /// splitting CRLF pairs and, when possible, avoiding re-scanning text for /// text info. It is otherwise conceptually fairly straightforward. fn insert_internal(&mut self, char_idx: usize, ins_text: &str) { let mut ins_text = ins_text; let mut left_seam = false; let root_info = self.root.text_info(); let (l_info, residual) = Arc::make_mut(&mut self.root).edit_chunk_at_char( char_idx, root_info, |idx, cur_info, leaf_text| { // First check if we have a left seam. if idx == 0 && char_idx > 0 && ins_text.as_bytes()[0] == 0x0A { left_seam = true; ins_text = &ins_text[1..]; // Early out if it was only an LF. if ins_text.is_empty() { return (cur_info, None); } } // Find our byte index let byte_idx = char_to_byte_idx(leaf_text, idx); // No node splitting if (leaf_text.len() + ins_text.len()) <= MAX_BYTES { // Calculate new info without doing a full re-scan of cur_text. let new_info = { // Get summed info of current text and to-be-inserted text. #[allow(unused_mut)] let mut info = cur_info + TextInfo::from_str(ins_text); // Check for CRLF pairs on the insertion seams, and // adjust line break counts accordingly. #[cfg(any(feature = "cr_lines", feature = "unicode_lines"))] { if byte_idx > 0 { if leaf_text.as_bytes()[byte_idx - 1] == 0x0D && ins_text.as_bytes()[0] == 0x0A { info.line_breaks -= 1; } if byte_idx < leaf_text.len() && leaf_text.as_bytes()[byte_idx - 1] == 0x0D && leaf_text.as_bytes()[byte_idx] == 0x0A { info.line_breaks += 1; } } if byte_idx < leaf_text.len() && *ins_text.as_bytes().last().unwrap() == 0x0D && leaf_text.as_bytes()[byte_idx] == 0x0A { info.line_breaks -= 1; } } info }; // Insert the text and return the new info leaf_text.insert_str(byte_idx, ins_text); (new_info, None) } // We're splitting the node else { let r_text = leaf_text.insert_str_split(byte_idx, ins_text); let l_text_info = TextInfo::from_str(leaf_text); if r_text.len() > 0 { let r_text_info = TextInfo::from_str(&r_text); ( l_text_info, Some((r_text_info, Arc::new(Node::Leaf(r_text)))), ) } else { // Leaf couldn't be validly split, so leave it oversized (l_text_info, None) } } }, ); // Handle root splitting, if any. if let Some((r_info, r_node)) = residual { let mut l_node = Arc::new(Node::new()); std::mem::swap(&mut l_node, &mut self.root); let mut children = NodeChildren::new(); children.push((l_info, l_node)); children.push((r_info, r_node)); *Arc::make_mut(&mut self.root) = Node::Internal(children); } // Insert the LF to the left. // TODO: this code feels fairly redundant with above. Can we DRY this // better? if left_seam { // Do the insertion let root_info = self.root.text_info(); let (l_info, residual) = Arc::make_mut(&mut self.root).edit_chunk_at_char( char_idx - 1, root_info, |_, cur_info, leaf_text| { let byte_idx = leaf_text.len(); // No node splitting if (leaf_text.len() + ins_text.len()) <= MAX_BYTES { // Calculate new info without doing a full re-scan of cur_text let mut new_info = cur_info; new_info.bytes += 1; new_info.chars += 1; #[cfg(not(any(feature = "cr_lines", feature = "unicode_lines")))] { new_info.line_breaks += 1; } #[cfg(any(feature = "cr_lines", feature = "unicode_lines"))] if *leaf_text.as_bytes().last().unwrap() != 0x0D { new_info.line_breaks += 1; } // Insert the text and return the new info leaf_text.insert_str(byte_idx, "\n"); (new_info, None) } // We're splitting the node else { let r_text = leaf_text.insert_str_split(byte_idx, "\n"); let l_text_info = TextInfo::from_str(leaf_text); if r_text.len() > 0 { let r_text_info = TextInfo::from_str(&r_text); ( l_text_info, Some((r_text_info, Arc::new(Node::Leaf(r_text)))), ) } else { // Leaf couldn't be validly split, so leave it oversized (l_text_info, None) } } }, ); // Handle root splitting, if any. if let Some((r_info, r_node)) = residual { let mut l_node = Arc::new(Node::new()); std::mem::swap(&mut l_node, &mut self.root); let mut children = NodeChildren::new(); children.push((l_info, l_node)); children.push((r_info, r_node)); *Arc::make_mut(&mut self.root) = Node::Internal(children); } } } /// Removes the text in the given char index range. /// /// Uses range syntax, e.g. `2..7`, `2..`, etc. The range is in `char` /// indices. /// /// Runs in O(M + log N) time, where N is the length of the `Rope` and M /// is the length of the range being removed. /// /// # Example /// /// ``` /// # use ropey::Rope; /// let mut rope = Rope::from_str("Hello world!"); /// rope.remove(5..); /// /// assert_eq!("Hello", rope); /// ``` /// /// # Panics /// /// Panics if the start of the range is greater than the end, or if the /// end is out of bounds (i.e. `end > len_chars()`). pub fn remove(&mut self, char_range: R) where R: RangeBounds, { self.try_remove(char_range).unwrap() } /// Splits the `Rope` at `char_idx`, returning the right part of /// the split. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `char_idx` is out of bounds (i.e. `char_idx > len_chars()`). pub fn split_off(&mut self, char_idx: usize) -> Self { self.try_split_off(char_idx).unwrap() } /// Appends a `Rope` to the end of this one, consuming the other `Rope`. /// /// Runs in O(log N) time. pub fn append(&mut self, other: Self) { if self.len_chars() == 0 { // Special case let mut other = other; std::mem::swap(self, &mut other); } else if other.len_chars() > 0 { let left_info = self.root.text_info(); let right_info = other.root.text_info(); let seam_byte_i = if other.char(0) == '\n' { Some(left_info.bytes) } else { None }; let l_depth = self.root.depth(); let r_depth = other.root.depth(); if l_depth > r_depth { let extra = Arc::make_mut(&mut self.root).append_at_depth(other.root, l_depth - r_depth); if let Some(node) = extra { let mut children = NodeChildren::new(); children.push((self.root.text_info(), Arc::clone(&self.root))); children.push((node.text_info(), node)); self.root = Arc::new(Node::Internal(children)); } } else { let mut other = other; let extra = Arc::make_mut(&mut other.root) .prepend_at_depth(Arc::clone(&self.root), r_depth - l_depth); if let Some(node) = extra { let mut children = NodeChildren::new(); children.push((node.text_info(), node)); children.push((other.root.text_info(), Arc::clone(&other.root))); other.root = Arc::new(Node::Internal(children)); } *self = other; }; // Fix up any mess left behind. let root = Arc::make_mut(&mut self.root); if let Some(i) = seam_byte_i { root.fix_crlf_seam(i, true); } if (left_info.bytes as usize) < MIN_BYTES || (right_info.bytes as usize) < MIN_BYTES { root.fix_tree_seam(left_info.chars as usize); } self.pull_up_singular_nodes(); } } //----------------------------------------------------------------------- // Index conversion methods /// Returns the char index of the given byte. /// /// Notes: /// /// - If the byte is in the middle of a multi-byte char, returns the /// index of the char that the byte belongs to. /// - `byte_idx` can be one-past-the-end, which will return /// one-past-the-end char index. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `byte_idx` is out of bounds (i.e. `byte_idx > len_bytes()`). #[inline] pub fn byte_to_char(&self, byte_idx: usize) -> usize { self.try_byte_to_char(byte_idx).unwrap() } /// Returns the line index of the given byte. /// /// Notes: /// /// - Lines are zero-indexed. This is functionally equivalent to /// counting the line endings before the specified byte. /// - `byte_idx` can be one-past-the-end, which will return the /// last line index. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `byte_idx` is out of bounds (i.e. `byte_idx > len_bytes()`). #[inline] pub fn byte_to_line(&self, byte_idx: usize) -> usize { self.try_byte_to_line(byte_idx).unwrap() } /// Returns the byte index of the given char. /// /// Notes: /// /// - `char_idx` can be one-past-the-end, which will return /// one-past-the-end byte index. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `char_idx` is out of bounds (i.e. `char_idx > len_chars()`). #[inline] pub fn char_to_byte(&self, char_idx: usize) -> usize { self.try_char_to_byte(char_idx).unwrap() } /// Returns the line index of the given char. /// /// Notes: /// /// - Lines are zero-indexed. This is functionally equivalent to /// counting the line endings before the specified char. /// - `char_idx` can be one-past-the-end, which will return the /// last line index. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `char_idx` is out of bounds (i.e. `char_idx > len_chars()`). #[inline] pub fn char_to_line(&self, char_idx: usize) -> usize { self.try_char_to_line(char_idx).unwrap() } /// Returns the utf16 code unit index of the given char. /// /// Ropey stores text internally as utf8, but sometimes it is necessary /// to interact with external APIs that still use utf16. This function is /// primarily intended for such situations, and is otherwise not very /// useful. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `char_idx` is out of bounds (i.e. `char_idx > len_chars()`). #[inline] pub fn char_to_utf16_cu(&self, char_idx: usize) -> usize { self.try_char_to_utf16_cu(char_idx).unwrap() } /// Returns the char index of the given utf16 code unit. /// /// Ropey stores text internally as utf8, but sometimes it is necessary /// to interact with external APIs that still use utf16. This function is /// primarily intended for such situations, and is otherwise not very /// useful. /// /// Note: if the utf16 code unit is in the middle of a char, returns the /// index of the char that it belongs to. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `utf16_cu_idx` is out of bounds /// (i.e. `utf16_cu_idx > len_utf16_cu()`). #[inline] pub fn utf16_cu_to_char(&self, utf16_cu_idx: usize) -> usize { self.try_utf16_cu_to_char(utf16_cu_idx).unwrap() } /// Returns the byte index of the start of the given line. /// /// Notes: /// /// - Lines are zero-indexed. /// - `line_idx` can be one-past-the-end, which will return /// one-past-the-end byte index. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `line_idx` is out of bounds (i.e. `line_idx > len_lines()`). #[inline] pub fn line_to_byte(&self, line_idx: usize) -> usize { self.try_line_to_byte(line_idx).unwrap() } /// Returns the char index of the start of the given line. /// /// Notes: /// /// - Lines are zero-indexed. /// - `line_idx` can be one-past-the-end, which will return /// one-past-the-end char index. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `line_idx` is out of bounds (i.e. `line_idx > len_lines()`). #[inline] pub fn line_to_char(&self, line_idx: usize) -> usize { self.try_line_to_char(line_idx).unwrap() } //----------------------------------------------------------------------- // Fetch methods /// Returns the byte at `byte_idx`. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `byte_idx` is out of bounds (i.e. `byte_idx >= len_bytes()`). #[inline] pub fn byte(&self, byte_idx: usize) -> u8 { // Bounds check if let Some(out) = self.get_byte(byte_idx) { out } else { panic!( "Attempt to index past end of Rope: byte index {}, Rope byte length {}", byte_idx, self.len_bytes() ); } } /// Returns the char at `char_idx`. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `char_idx` is out of bounds (i.e. `char_idx >= len_chars()`). #[inline] pub fn char(&self, char_idx: usize) -> char { if let Some(out) = self.get_char(char_idx) { out } else { panic!( "Attempt to index past end of Rope: char index {}, Rope char length {}", char_idx, self.len_chars() ); } } /// Returns the line at `line_idx`. /// /// Note: lines are zero-indexed. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `line_idx` is out of bounds (i.e. `line_idx >= len_lines()`). #[inline] pub fn line(&self, line_idx: usize) -> RopeSlice { if let Some(out) = self.get_line(line_idx) { out } else { let len_lines = self.len_lines(); panic!( "Attempt to index past end of Rope: line index {}, Rope line length {}", line_idx, len_lines ); } } /// Returns the chunk containing the given byte index. /// /// Also returns the byte and char indices of the beginning of the chunk /// and the index of the line that the chunk starts on. /// /// Note: for convenience, a one-past-the-end `byte_idx` returns the last /// chunk of the `RopeSlice`. /// /// The return value is organized as /// `(chunk, chunk_byte_idx, chunk_char_idx, chunk_line_idx)`. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `byte_idx` is out of bounds (i.e. `byte_idx > len_bytes()`). #[inline] pub fn chunk_at_byte(&self, byte_idx: usize) -> (&str, usize, usize, usize) { // Bounds check if let Some(out) = self.get_chunk_at_byte(byte_idx) { out } else { panic!( "Attempt to index past end of Rope: byte index {}, Rope byte length {}", byte_idx, self.len_bytes() ); } } /// Returns the chunk containing the given char index. /// /// Also returns the byte and char indices of the beginning of the chunk /// and the index of the line that the chunk starts on. /// /// Note: for convenience, a one-past-the-end `char_idx` returns the last /// chunk of the `RopeSlice`. /// /// The return value is organized as /// `(chunk, chunk_byte_idx, chunk_char_idx, chunk_line_idx)`. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `char_idx` is out of bounds (i.e. `char_idx > len_chars()`). #[inline] pub fn chunk_at_char(&self, char_idx: usize) -> (&str, usize, usize, usize) { if let Some(out) = self.get_chunk_at_char(char_idx) { out } else { panic!( "Attempt to index past end of Rope: char index {}, Rope char length {}", char_idx, self.len_chars() ); } } /// Returns the chunk containing the given line break. /// /// Also returns the byte and char indices of the beginning of the chunk /// and the index of the line that the chunk starts on. /// /// Note: for convenience, both the beginning and end of the rope are /// considered line breaks for the purposes of indexing. For example, in /// the string `"Hello \n world!"` 0 would give the first chunk, 1 would /// give the chunk containing the newline character, and 2 would give the /// last chunk. /// /// The return value is organized as /// `(chunk, chunk_byte_idx, chunk_char_idx, chunk_line_idx)`. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `line_break_idx` is out of bounds (i.e. `line_break_idx > len_lines()`). #[inline] pub fn chunk_at_line_break(&self, line_break_idx: usize) -> (&str, usize, usize, usize) { if let Some(out) = self.get_chunk_at_line_break(line_break_idx) { out } else { panic!( "Attempt to index past end of Rope: line break index {}, max index {}", line_break_idx, self.len_lines() ); } } //----------------------------------------------------------------------- // Slicing /// Gets an immutable slice of the `Rope`, using char indices. /// /// Uses range syntax, e.g. `2..7`, `2..`, etc. /// /// # Example /// /// ``` /// # use ropey::Rope; /// let rope = Rope::from_str("Hello world!"); /// let slice = rope.slice(..5); /// /// assert_eq!("Hello", slice); /// ``` /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if the start of the range is greater than the end, or if the /// end is out of bounds (i.e. `end > len_chars()`). #[inline] pub fn slice(&self, char_range: R) -> RopeSlice where R: RangeBounds, { self.get_slice(char_range).unwrap() } /// Gets and immutable slice of the `Rope`, using byte indices. /// /// Uses range syntax, e.g. `2..7`, `2..`, etc. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if: /// - The start of the range is greater than the end. /// - The end is out of bounds (i.e. `end > len_bytes()`). /// - The range doesn't align with char boundaries. pub fn byte_slice(&self, byte_range: R) -> RopeSlice where R: RangeBounds, { match self.get_byte_slice_impl(byte_range) { Ok(s) => return s, Err(e) => panic!("byte_slice(): {}", e), } } //----------------------------------------------------------------------- // Iterator methods /// Creates an iterator over the bytes of the `Rope`. /// /// Runs in O(log N) time. #[inline] pub fn bytes(&self) -> Bytes { Bytes::new(&self.root) } /// Creates an iterator over the bytes of the `Rope`, starting at byte /// `byte_idx`. /// /// If `byte_idx == len_bytes()` then an iterator at the end of the /// `Rope` is created (i.e. `next()` will return `None`). /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `byte_idx` is out of bounds (i.e. `byte_idx > len_bytes()`). #[inline] pub fn bytes_at(&self, byte_idx: usize) -> Bytes { if let Some(out) = self.get_bytes_at(byte_idx) { out } else { panic!( "Attempt to index past end of Rope: byte index {}, Rope byte length {}", byte_idx, self.len_bytes() ); } } /// Creates an iterator over the chars of the `Rope`. /// /// Runs in O(log N) time. #[inline] pub fn chars(&self) -> Chars { Chars::new(&self.root) } /// Creates an iterator over the chars of the `Rope`, starting at char /// `char_idx`. /// /// If `char_idx == len_chars()` then an iterator at the end of the /// `Rope` is created (i.e. `next()` will return `None`). /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `char_idx` is out of bounds (i.e. `char_idx > len_chars()`). #[inline] pub fn chars_at(&self, char_idx: usize) -> Chars { if let Some(out) = self.get_chars_at(char_idx) { out } else { panic!( "Attempt to index past end of Rope: char index {}, Rope char length {}", char_idx, self.len_chars() ); } } /// Creates an iterator over the lines of the `Rope`. /// /// Runs in O(log N) time. #[inline] pub fn lines(&self) -> Lines { Lines::new(&self.root) } /// Creates an iterator over the lines of the `Rope`, starting at line /// `line_idx`. /// /// If `line_idx == len_lines()` then an iterator at the end of the /// `Rope` is created (i.e. `next()` will return `None`). /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `line_idx` is out of bounds (i.e. `line_idx > len_lines()`). #[inline] pub fn lines_at(&self, line_idx: usize) -> Lines { if let Some(out) = self.get_lines_at(line_idx) { out } else { panic!( "Attempt to index past end of Rope: line index {}, Rope line length {}", line_idx, self.len_lines() ); } } /// Creates an iterator over the chunks of the `Rope`. /// /// Runs in O(log N) time. #[inline] pub fn chunks(&self) -> Chunks { Chunks::new(&self.root) } /// Creates an iterator over the chunks of the `Rope`, with the /// iterator starting at the chunk containing `byte_idx`. /// /// Also returns the byte and char indices of the beginning of the first /// chunk to be yielded, and the index of the line that chunk starts on. /// /// If `byte_idx == len_bytes()` an iterator at the end of the `Rope` /// (yielding `None` on a call to `next()`) is created. /// /// The return value is organized as /// `(iterator, chunk_byte_idx, chunk_char_idx, chunk_line_idx)`. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `byte_idx` is out of bounds (i.e. `byte_idx > len_bytes()`). #[inline] pub fn chunks_at_byte(&self, byte_idx: usize) -> (Chunks, usize, usize, usize) { if let Some(out) = self.get_chunks_at_byte(byte_idx) { out } else { panic!( "Attempt to index past end of Rope: byte index {}, Rope byte length {}", byte_idx, self.len_bytes() ); } } /// Creates an iterator over the chunks of the `Rope`, with the /// iterator starting at the chunk containing `char_idx`. /// /// Also returns the byte and char indices of the beginning of the first /// chunk to be yielded, and the index of the line that chunk starts on. /// /// If `char_idx == len_chars()` an iterator at the end of the `Rope` /// (yielding `None` on a call to `next()`) is created. /// /// The return value is organized as /// `(iterator, chunk_byte_idx, chunk_char_idx, chunk_line_idx)`. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `char_idx` is out of bounds (i.e. `char_idx > len_chars()`). #[inline] pub fn chunks_at_char(&self, char_idx: usize) -> (Chunks, usize, usize, usize) { if let Some(out) = self.get_chunks_at_char(char_idx) { out } else { panic!( "Attempt to index past end of Rope: char index {}, Rope char length {}", char_idx, self.len_chars() ); } } /// Creates an iterator over the chunks of the `Rope`, with the /// iterator starting at the chunk containing `line_break_idx`. /// /// Also returns the byte and char indices of the beginning of the first /// chunk to be yielded, and the index of the line that chunk starts on. /// /// Note: for convenience, both the beginning and end of the `Rope` are /// considered line breaks for the purposes of indexing. For example, in /// the string `"Hello \n world!"` 0 would create an iterator starting on /// the first chunk, 1 would create an iterator starting on the chunk /// containing the newline character, and 2 would create an iterator at /// the end of the `Rope` (yielding `None` on a call to `next()`). /// /// The return value is organized as /// `(iterator, chunk_byte_idx, chunk_char_idx, chunk_line_idx)`. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `line_break_idx` is out of bounds (i.e. `line_break_idx > len_lines()`). #[inline] pub fn chunks_at_line_break(&self, line_break_idx: usize) -> (Chunks, usize, usize, usize) { if let Some(out) = self.get_chunks_at_line_break(line_break_idx) { out } else { panic!( "Attempt to index past end of Rope: line break index {}, max index {}", line_break_idx, self.len_lines() ); } } /// Returns true if this rope and `other` point to precisely the same /// in-memory data. /// /// This happens when one of the ropes is a clone of the other and /// neither have been modified since then. Because clones initially /// share all the same data, it can be useful to check if they still /// point to precisely the same memory as a way of determining /// whether they are both still unmodified. /// /// Note: this is distinct from checking for equality: two ropes can /// have the same *contents* (equal) but be stored in different /// memory locations (not instances). Importantly, two clones that /// post-cloning are modified identically will *not* be instances /// anymore, even though they will have equal contents. /// /// Runs in O(1) time. #[inline] pub fn is_instance(&self, other: &Rope) -> bool { Arc::ptr_eq(&self.root, &other.root) } //----------------------------------------------------------------------- // Debugging /// NOT PART OF THE PUBLIC API (hidden from docs for a reason!) /// /// Debugging tool to make sure that all of the meta-data of the /// tree is consistent with the actual data. #[doc(hidden)] pub fn assert_integrity(&self) { self.root.assert_integrity(); } /// NOT PART OF THE PUBLIC API (hidden from docs for a reason!) /// /// Debugging tool to make sure that all of the following invariants /// hold true throughout the tree: /// /// - The tree is the same height everywhere. /// - All internal nodes have the minimum number of children. /// - All leaf nodes are non-empty. /// - CRLF pairs are never split over chunk boundaries. #[doc(hidden)] pub fn assert_invariants(&self) { self.root.assert_balance(); self.root.assert_node_size(true); self.assert_crlf_seams(); } /// Checks that CRLF pairs are never split over chunk boundaries. fn assert_crlf_seams(&self) { if self.chunks().count() > 0 { let mut itr = self.chunks(); let mut last_chunk = itr.next().unwrap(); for chunk in itr { if !chunk.is_empty() && !last_chunk.is_empty() { assert!(crlf::seam_is_break(last_chunk.as_bytes(), chunk.as_bytes())); last_chunk = chunk; } else if last_chunk.is_empty() { last_chunk = chunk; } } } } //----------------------------------------------------------------------- // Internal utilities /// Iteratively replaces the root node with its child if it only has /// one child. pub(crate) fn pull_up_singular_nodes(&mut self) { while (!self.root.is_leaf()) && self.root.child_count() == 1 { let child = if let Node::Internal(ref children) = *self.root { Arc::clone(&children.nodes()[0]) } else { unreachable!() }; self.root = child; } } } /// # Non-Panicking /// /// The methods in this impl block provide non-panicking versions of /// `Rope`'s panicking methods. They return either `Option::None` or /// `Result::Err()` when their panicking counterparts would have panicked. impl Rope { /// Non-panicking version of [`insert()`](Rope::insert). #[inline] pub fn try_insert(&mut self, char_idx: usize, text: &str) -> Result<()> { // Bounds check if char_idx <= self.len_chars() { // We have three cases here: // 1. The insertion text is very large, in which case building a new // Rope out of it and splicing it into the existing Rope is most // efficient. // 2. The insertion text is somewhat large, in which case splitting it // up into chunks and repeatedly inserting them is the most // efficient. The splitting is necessary because the insertion code // only works correctly below a certain insertion size. // 3. The insertion text is small, in which case we can simply insert // it. // // Cases #2 and #3 are rolled into one case here, where case #3 just // results in the text being "split" into only one chunk. // // The boundary for what constitutes "very large" text was arrived at // experimentally, by testing at what point Rope build + splice becomes // faster than split + repeated insert. This constant is likely worth // revisiting from time to time as Ropey evolves. if text.len() > MAX_BYTES * 6 { // Case #1: very large text, build rope and splice it in. let text_rope = Rope::from_str(text); let right = self.split_off(char_idx); self.append(text_rope); self.append(right); } else { // Cases #2 and #3: split into chunks and repeatedly insert. let mut text = text; while !text.is_empty() { // Split a chunk off from the end of the text. // We do this from the end instead of the front so that // the repeated insertions can keep re-using the same // insertion point. let split_idx = crlf::find_good_split( text.len() - (MAX_BYTES - 4).min(text.len()), text.as_bytes(), false, ); let ins_text = &text[split_idx..]; text = &text[..split_idx]; // Do the insertion. self.insert_internal(char_idx, ins_text); } } Ok(()) } else { Err(Error::CharIndexOutOfBounds(char_idx, self.len_chars())) } } /// Non-panicking version of [`insert_char()`](Rope::insert_char). #[inline] pub fn try_insert_char(&mut self, char_idx: usize, ch: char) -> Result<()> { // Bounds check if char_idx <= self.len_chars() { let mut buf = [0u8; 4]; self.insert_internal(char_idx, ch.encode_utf8(&mut buf)); Ok(()) } else { Err(Error::CharIndexOutOfBounds(char_idx, self.len_chars())) } } /// Non-panicking version of [`remove()`](Rope::remove). pub fn try_remove(&mut self, char_range: R) -> Result<()> where R: RangeBounds, { let start_opt = start_bound_to_num(char_range.start_bound()); let end_opt = end_bound_to_num(char_range.end_bound()); let start = start_opt.unwrap_or(0); let end = end_opt.unwrap_or_else(|| self.len_chars()); if end.max(start) > self.len_chars() { Err(Error::CharRangeOutOfBounds( start_opt, end_opt, self.len_chars(), )) } else if start > end { Err(Error::CharRangeInvalid(start, end)) } else { // A special case that the rest of the logic doesn't handle // correctly. if start == 0 && end == self.len_chars() { self.root = Arc::new(Node::new()); return Ok(()); } let root = Arc::make_mut(&mut self.root); let root_info = root.text_info(); let (_, crlf_seam, needs_fix) = root.remove_char_range(start, end, root_info); if crlf_seam { let seam_idx = root.char_to_text_info(start).bytes; root.fix_crlf_seam(seam_idx as Count, false); } if needs_fix { root.fix_tree_seam(start); } self.pull_up_singular_nodes(); Ok(()) } } /// Non-panicking version of [`split_off()`](Rope::split_off). pub fn try_split_off(&mut self, char_idx: usize) -> Result { // Bounds check if char_idx <= self.len_chars() { if char_idx == 0 { // Special case 1 let mut new_rope = Rope::new(); std::mem::swap(self, &mut new_rope); Ok(new_rope) } else if char_idx == self.len_chars() { // Special case 2 Ok(Rope::new()) } else { // Do the split let mut new_rope = Rope { root: Arc::new(Arc::make_mut(&mut self.root).split(char_idx)), }; // Fix up the edges Arc::make_mut(&mut self.root).zip_fix_right(); Arc::make_mut(&mut new_rope.root).zip_fix_left(); self.pull_up_singular_nodes(); new_rope.pull_up_singular_nodes(); Ok(new_rope) } } else { Err(Error::CharIndexOutOfBounds(char_idx, self.len_chars())) } } /// Non-panicking version of [`byte_to_char()`](Rope::byte_to_char). #[inline] pub fn try_byte_to_char(&self, byte_idx: usize) -> Result { // Bounds check if byte_idx <= self.len_bytes() { let (chunk, b, c, _) = self.chunk_at_byte(byte_idx); Ok(c + byte_to_char_idx(chunk, byte_idx - b)) } else { Err(Error::ByteIndexOutOfBounds(byte_idx, self.len_bytes())) } } /// Non-panicking version of [`byte_to_line()`](Rope::byte_to_line). #[inline] pub fn try_byte_to_line(&self, byte_idx: usize) -> Result { // Bounds check if byte_idx <= self.len_bytes() { let (chunk, b, _, l) = self.chunk_at_byte(byte_idx); Ok(l + byte_to_line_idx(chunk, byte_idx - b)) } else { Err(Error::ByteIndexOutOfBounds(byte_idx, self.len_bytes())) } } /// Non-panicking version of [`char_to_byte()`](Rope::char_to_byte). #[inline] pub fn try_char_to_byte(&self, char_idx: usize) -> Result { // Bounds check if char_idx <= self.len_chars() { let (chunk, b, c, _) = self.chunk_at_char(char_idx); Ok(b + char_to_byte_idx(chunk, char_idx - c)) } else { Err(Error::CharIndexOutOfBounds(char_idx, self.len_chars())) } } /// Non-panicking version of [`char_to_line()`](Rope::char_to_line). #[inline] pub fn try_char_to_line(&self, char_idx: usize) -> Result { // Bounds check if char_idx <= self.len_chars() { let (chunk, _, c, l) = self.chunk_at_char(char_idx); Ok(l + char_to_line_idx(chunk, char_idx - c)) } else { Err(Error::CharIndexOutOfBounds(char_idx, self.len_chars())) } } /// Non-panicking version of [`char_to_utf16_cu()`](Rope::char_to_utf16_cu). #[inline] pub fn try_char_to_utf16_cu(&self, char_idx: usize) -> Result { // Bounds check if char_idx <= self.len_chars() { let (chunk, chunk_start_info) = self.root.get_chunk_at_char(char_idx); let chunk_byte_idx = char_to_byte_idx(chunk, char_idx - chunk_start_info.chars as usize); let surrogate_count = byte_to_utf16_surrogate_idx(chunk, chunk_byte_idx); Ok(char_idx + chunk_start_info.utf16_surrogates as usize + surrogate_count) } else { Err(Error::CharIndexOutOfBounds(char_idx, self.len_chars())) } } /// Non-panicking version of [`utf16_cu_to_char()`](Rope::utf16_cu_to_char). #[inline] pub fn try_utf16_cu_to_char(&self, utf16_cu_idx: usize) -> Result { // Bounds check if utf16_cu_idx <= self.len_utf16_cu() { let (chunk, chunk_start_info) = self.root.get_chunk_at_utf16_code_unit(utf16_cu_idx); let chunk_utf16_cu_idx = utf16_cu_idx - (chunk_start_info.chars + chunk_start_info.utf16_surrogates) as usize; let chunk_char_idx = utf16_code_unit_to_char_idx(chunk, chunk_utf16_cu_idx); Ok(chunk_start_info.chars as usize + chunk_char_idx) } else { Err(Error::Utf16IndexOutOfBounds( utf16_cu_idx, self.len_utf16_cu(), )) } } /// Non-panicking version of [`line_to_byte()`](Rope::line_to_byte). #[inline] pub fn try_line_to_byte(&self, line_idx: usize) -> Result { // Bounds check if line_idx <= self.len_lines() { if line_idx == self.len_lines() { Ok(self.len_bytes()) } else { let (chunk, b, _, l) = self.chunk_at_line_break(line_idx); Ok(b + line_to_byte_idx(chunk, line_idx - l)) } } else { Err(Error::LineIndexOutOfBounds(line_idx, self.len_lines())) } } /// Non-panicking version of [`line_to_char()`](Rope::line_to_char). #[inline] pub fn try_line_to_char(&self, line_idx: usize) -> Result { // Bounds check if line_idx <= self.len_lines() { if line_idx == self.len_lines() { Ok(self.len_chars()) } else { let (chunk, _, c, l) = self.chunk_at_line_break(line_idx); Ok(c + line_to_char_idx(chunk, line_idx - l)) } } else { Err(Error::LineIndexOutOfBounds(line_idx, self.len_lines())) } } /// Non-panicking version of [`byte()`](Rope::byte). #[inline] pub fn get_byte(&self, byte_idx: usize) -> Option { // Bounds check if byte_idx < self.len_bytes() { let (chunk, chunk_byte_idx, _, _) = self.chunk_at_byte(byte_idx); let chunk_rel_byte_idx = byte_idx - chunk_byte_idx; Some(chunk.as_bytes()[chunk_rel_byte_idx]) } else { None } } /// Non-panicking version of [`char()`](Rope::char). #[inline] pub fn get_char(&self, char_idx: usize) -> Option { // Bounds check if char_idx < self.len_chars() { let (chunk, _, chunk_char_idx, _) = self.chunk_at_char(char_idx); let byte_idx = char_to_byte_idx(chunk, char_idx - chunk_char_idx); Some(chunk[byte_idx..].chars().next().unwrap()) } else { None } } /// Non-panicking version of [`line()`](Rope::line). #[inline] pub fn get_line(&self, line_idx: usize) -> Option { use crate::slice::RSEnum; use crate::str_utils::{count_chars, count_utf16_surrogates}; let len_lines = self.len_lines(); // Bounds check if line_idx < len_lines { let (chunk_1, _, c1, l1) = self.chunk_at_line_break(line_idx); let (chunk_2, _, c2, l2) = self.chunk_at_line_break(line_idx + 1); if c1 == c2 { let text1 = &chunk_1[line_to_byte_idx(chunk_1, line_idx - l1)..]; let text2 = &text1[..line_to_byte_idx(text1, 1)]; Some(RopeSlice(RSEnum::Light { text: text2, char_count: count_chars(text2) as Count, utf16_surrogate_count: count_utf16_surrogates(text2) as Count, line_break_count: if line_idx == (len_lines - 1) { 0 } else { 1 }, })) } else { let start = c1 + line_to_char_idx(chunk_1, line_idx - l1); let end = c2 + line_to_char_idx(chunk_2, line_idx + 1 - l2); Some(self.slice(start..end)) } } else { None } } /// Non-panicking version of [`chunk_at_byte()`](Rope::chunk_at_byte). #[inline] pub fn get_chunk_at_byte(&self, byte_idx: usize) -> Option<(&str, usize, usize, usize)> { // Bounds check if byte_idx <= self.len_bytes() { let (chunk, info) = self.root.get_chunk_at_byte(byte_idx); Some(( chunk, info.bytes as usize, info.chars as usize, info.line_breaks as usize, )) } else { None } } /// Non-panicking version of [`chunk_at_char()`](Rope::chunk_at_char). #[inline] pub fn get_chunk_at_char(&self, char_idx: usize) -> Option<(&str, usize, usize, usize)> { // Bounds check if char_idx <= self.len_chars() { let (chunk, info) = self.root.get_chunk_at_char(char_idx); Some(( chunk, info.bytes as usize, info.chars as usize, info.line_breaks as usize, )) } else { None } } /// Non-panicking version of [`chunk_at_line_break()`](Rope::chunk_at_line_break). #[inline] pub fn get_chunk_at_line_break( &self, line_break_idx: usize, ) -> Option<(&str, usize, usize, usize)> { // Bounds check if line_break_idx <= self.len_lines() { let (chunk, info) = self.root.get_chunk_at_line_break(line_break_idx); Some(( chunk, info.bytes as usize, info.chars as usize, info.line_breaks as usize, )) } else { None } } /// Non-panicking version of [`slice()`](Rope::slice). #[inline] pub fn get_slice(&self, char_range: R) -> Option where R: RangeBounds, { let start = start_bound_to_num(char_range.start_bound()).unwrap_or(0); let end = end_bound_to_num(char_range.end_bound()).unwrap_or_else(|| self.len_chars()); // Bounds check if start <= end && end <= self.len_chars() { Some(RopeSlice::new_with_range(&self.root, start, end)) } else { None } } /// Non-panicking version of [`byte_slice()`](Rope::byte_slice). #[inline] pub fn get_byte_slice(&self, byte_range: R) -> Option where R: RangeBounds, { self.get_byte_slice_impl(byte_range).ok() } pub(crate) fn get_byte_slice_impl(&self, byte_range: R) -> Result where R: RangeBounds, { let start_range = start_bound_to_num(byte_range.start_bound()); let end_range = end_bound_to_num(byte_range.end_bound()); // Bounds checks. match (start_range, end_range) { (Some(s), Some(e)) => { if s > e { return Err(Error::ByteRangeInvalid(s, e)); } else if e > self.len_bytes() { return Err(Error::ByteRangeOutOfBounds( start_range, end_range, self.len_bytes(), )); } } (Some(s), None) => { if s > self.len_bytes() { return Err(Error::ByteRangeOutOfBounds( start_range, end_range, self.len_bytes(), )); } } (None, Some(e)) => { if e > self.len_bytes() { return Err(Error::ByteRangeOutOfBounds( start_range, end_range, self.len_bytes(), )); } } _ => {} } let (start, end) = ( start_range.unwrap_or(0), end_range.unwrap_or_else(|| self.len_bytes()), ); RopeSlice::new_with_byte_range(&self.root, start, end).map_err(|e| { if let Error::ByteRangeNotCharBoundary(_, _) = e { Error::ByteRangeNotCharBoundary(start_range, end_range) } else { e } }) } /// Non-panicking version of [`bytes_at()`](Rope::bytes_at). #[inline] pub fn get_bytes_at(&self, byte_idx: usize) -> Option { // Bounds check if byte_idx <= self.len_bytes() { let info = self.root.text_info(); Some(Bytes::new_with_range_at( &self.root, byte_idx, (0, info.bytes as usize), (0, info.chars as usize), (0, info.line_breaks as usize + 1), )) } else { None } } /// Non-panicking version of [`chars_at()`](Rope::chars_at). #[inline] pub fn get_chars_at(&self, char_idx: usize) -> Option { // Bounds check if char_idx <= self.len_chars() { let info = self.root.text_info(); Some(Chars::new_with_range_at( &self.root, char_idx, (0, info.bytes as usize), (0, info.chars as usize), (0, info.line_breaks as usize + 1), )) } else { None } } /// Non-panicking version of [`lines_at()`](Rope::lines_at). #[inline] pub fn get_lines_at(&self, line_idx: usize) -> Option { // Bounds check if line_idx <= self.len_lines() { Some(Lines::new_with_range_at( &self.root, line_idx, (0, self.len_bytes()), (0, self.len_lines()), )) } else { None } } /// Non-panicking version of [`chunks_at_byte()`](Rope::chunks_at_byte). #[inline] pub fn get_chunks_at_byte(&self, byte_idx: usize) -> Option<(Chunks, usize, usize, usize)> { // Bounds check if byte_idx <= self.len_bytes() { Some(Chunks::new_with_range_at_byte( &self.root, byte_idx, (0, self.len_bytes()), (0, self.len_chars()), (0, self.len_lines()), )) } else { None } } /// Non-panicking version of [`chunks_at_char()`](Rope::chunks_at_char). #[inline] pub fn get_chunks_at_char(&self, char_idx: usize) -> Option<(Chunks, usize, usize, usize)> { // Bounds check if char_idx <= self.len_chars() { Some(Chunks::new_with_range_at_char( &self.root, char_idx, (0, self.len_bytes()), (0, self.len_chars()), (0, self.len_lines()), )) } else { None } } /// Non-panicking version of [`chunks_at_line_break()`](Rope::chunks_at_line_break). #[inline] pub fn get_chunks_at_line_break( &self, line_break_idx: usize, ) -> Option<(Chunks, usize, usize, usize)> { // Bounds check if line_break_idx <= self.len_lines() { Some(Chunks::new_with_range_at_line_break( &self.root, line_break_idx, (0, self.len_bytes()), (0, self.len_chars()), (0, self.len_lines()), )) } else { None } } } //============================================================== // Conversion impls impl<'a> From<&'a str> for Rope { #[inline] fn from(text: &'a str) -> Self { Rope::from_str(text) } } impl<'a> From> for Rope { #[inline] fn from(text: std::borrow::Cow<'a, str>) -> Self { Rope::from_str(&text) } } impl From for Rope { #[inline] fn from(text: String) -> Self { Rope::from_str(&text) } } /// Will share data where possible. /// /// Runs in O(log N) time. impl<'a> From> for Rope { fn from(s: RopeSlice<'a>) -> Self { use crate::slice::RSEnum; match s { RopeSlice(RSEnum::Full { node, start_info, end_info, }) => { let mut rope = Rope { root: Arc::clone(node), }; // Chop off right end if needed if end_info.chars < node.text_info().chars { { let root = Arc::make_mut(&mut rope.root); root.split(end_info.chars as usize); root.zip_fix_right(); } rope.pull_up_singular_nodes(); } // Chop off left end if needed if start_info.chars > 0 { { let root = Arc::make_mut(&mut rope.root); *root = root.split(start_info.chars as usize); root.zip_fix_left(); } rope.pull_up_singular_nodes(); } // Return the rope rope } RopeSlice(RSEnum::Light { text, .. }) => Rope::from_str(text), } } } impl From for String { #[inline] fn from(r: Rope) -> Self { String::from(&r) } } impl<'a> From<&'a Rope> for String { #[inline] fn from(r: &'a Rope) -> Self { let mut text = String::with_capacity(r.len_bytes()); text.extend(r.chunks()); text } } impl<'a> From for std::borrow::Cow<'a, str> { #[inline] fn from(r: Rope) -> Self { std::borrow::Cow::Owned(String::from(r)) } } /// Attempts to borrow the contents of the `Rope`, but will convert to an /// owned string if the contents is not contiguous in memory. /// /// Runs in best case O(1), worst case O(N). impl<'a> From<&'a Rope> for std::borrow::Cow<'a, str> { #[inline] fn from(r: &'a Rope) -> Self { if let Node::Leaf(ref text) = *r.root { std::borrow::Cow::Borrowed(text) } else { std::borrow::Cow::Owned(String::from(r)) } } } impl<'a> FromIterator<&'a str> for Rope { fn from_iter(iter: T) -> Self where T: IntoIterator, { let mut builder = RopeBuilder::new(); for chunk in iter { builder.append(chunk); } builder.finish() } } impl<'a> FromIterator> for Rope { fn from_iter(iter: T) -> Self where T: IntoIterator>, { let mut builder = RopeBuilder::new(); for chunk in iter { builder.append(&chunk); } builder.finish() } } impl FromIterator for Rope { fn from_iter(iter: T) -> Self where T: IntoIterator, { let mut builder = RopeBuilder::new(); for chunk in iter { builder.append(&chunk); } builder.finish() } } //============================================================== // Other impls impl std::fmt::Debug for Rope { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { f.debug_list().entries(self.chunks()).finish() } } impl std::fmt::Display for Rope { #[inline] fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { for chunk in self.chunks() { write!(f, "{}", chunk)? } Ok(()) } } impl std::default::Default for Rope { #[inline] fn default() -> Self { Self::new() } } impl std::cmp::Eq for Rope {} impl std::cmp::PartialEq for Rope { #[inline] fn eq(&self, other: &Rope) -> bool { self.slice(..) == other.slice(..) } } impl<'a> std::cmp::PartialEq<&'a str> for Rope { #[inline] fn eq(&self, other: &&'a str) -> bool { self.slice(..) == *other } } impl<'a> std::cmp::PartialEq for &'a str { #[inline] fn eq(&self, other: &Rope) -> bool { *self == other.slice(..) } } impl std::cmp::PartialEq for Rope { #[inline] fn eq(&self, other: &str) -> bool { self.slice(..) == other } } impl std::cmp::PartialEq for str { #[inline] fn eq(&self, other: &Rope) -> bool { self == other.slice(..) } } impl std::cmp::PartialEq for Rope { #[inline] fn eq(&self, other: &String) -> bool { self.slice(..) == other.as_str() } } impl std::cmp::PartialEq for String { #[inline] fn eq(&self, other: &Rope) -> bool { self.as_str() == other.slice(..) } } impl<'a> std::cmp::PartialEq> for Rope { #[inline] fn eq(&self, other: &std::borrow::Cow<'a, str>) -> bool { self.slice(..) == **other } } impl<'a> std::cmp::PartialEq for std::borrow::Cow<'a, str> { #[inline] fn eq(&self, other: &Rope) -> bool { **self == other.slice(..) } } impl std::cmp::Ord for Rope { #[inline] fn cmp(&self, other: &Rope) -> std::cmp::Ordering { self.slice(..).cmp(&other.slice(..)) } } impl std::cmp::PartialOrd for Rope { #[inline] fn partial_cmp(&self, other: &Rope) -> Option { Some(self.cmp(other)) } } impl std::hash::Hash for Rope { fn hash(&self, state: &mut H) { self.slice(..).hash(state) } } //============================================================== #[cfg(test)] mod tests { use super::*; use crate::str_utils::*; use std::hash::{Hash, Hasher}; // 127 bytes, 103 chars, 1 line const TEXT: &str = "Hello there! How're you doing? It's \ a fine day, isn't it? Aren't you glad \ we're alive? こんにちは、みんなさん!"; // 124 bytes, 100 chars, 4 lines const TEXT_LINES: &str = "Hello there! How're you doing?\nIt's \ a fine day, isn't it?\nAren't you glad \ we're alive?\nこんにちは、みんなさん!"; // 127 bytes, 107 chars, 111 utf16 code units, 1 line const TEXT_EMOJI: &str = "Hello there!🐸 How're you doing?🐸 It's \ a fine day, isn't it?🐸 Aren't you glad \ we're alive?🐸 こんにちは、みんなさん!"; #[test] fn new_01() { let r = Rope::new(); assert_eq!(r, ""); r.assert_integrity(); r.assert_invariants(); } #[test] fn from_str() { let r = Rope::from_str(TEXT); assert_eq!(r, TEXT); r.assert_integrity(); r.assert_invariants(); } #[test] fn len_bytes_01() { let r = Rope::from_str(TEXT); assert_eq!(r.len_bytes(), 127); } #[test] fn len_bytes_02() { let r = Rope::from_str(""); assert_eq!(r.len_bytes(), 0); } #[test] fn len_chars_01() { let r = Rope::from_str(TEXT); assert_eq!(r.len_chars(), 103); } #[test] fn len_chars_02() { let r = Rope::from_str(""); assert_eq!(r.len_chars(), 0); } #[test] fn len_lines_01() { let r = Rope::from_str(TEXT_LINES); assert_eq!(r.len_lines(), 4); } #[test] fn len_lines_02() { let r = Rope::from_str(""); assert_eq!(r.len_lines(), 1); } #[test] fn len_utf16_cu_01() { let r = Rope::from_str(TEXT); assert_eq!(r.len_utf16_cu(), 103); } #[test] fn len_utf16_cu_02() { let r = Rope::from_str(TEXT_EMOJI); assert_eq!(r.len_utf16_cu(), 111); } #[test] fn len_utf16_cu_03() { let r = Rope::from_str(""); assert_eq!(r.len_utf16_cu(), 0); } #[test] fn insert_01() { let mut r = Rope::from_str(TEXT); r.insert(3, "AA"); assert_eq!( r, "HelAAlo there! How're you doing? It's \ a fine day, isn't it? Aren't you glad \ we're alive? こんにちは、みんなさん!" ); r.assert_integrity(); r.assert_invariants(); } #[test] fn insert_02() { let mut r = Rope::from_str(TEXT); r.insert(0, "AA"); assert_eq!( r, "AAHello there! How're you doing? It's \ a fine day, isn't it? Aren't you glad \ we're alive? こんにちは、みんなさん!" ); r.assert_integrity(); r.assert_invariants(); } #[test] fn insert_03() { let mut r = Rope::from_str(TEXT); r.insert(103, "AA"); assert_eq!( r, "Hello there! How're you doing? It's \ a fine day, isn't it? Aren't you glad \ we're alive? こんにちは、みんなさん!AA" ); r.assert_integrity(); r.assert_invariants(); } #[test] fn insert_04() { let mut r = Rope::new(); r.insert(0, "He"); r.insert(2, "l"); r.insert(3, "l"); r.insert(4, "o w"); r.insert(7, "o"); r.insert(8, "rl"); r.insert(10, "d!"); r.insert(3, "zopter"); assert_eq!("Helzopterlo world!", r); r.assert_integrity(); r.assert_invariants(); } #[test] fn insert_05() { let mut r = Rope::new(); r.insert(0, "こんいちは、みんなさん!"); r.insert(7, "zopter"); assert_eq!("こんいちは、みzopterんなさん!", r); r.assert_integrity(); r.assert_invariants(); } #[test] fn insert_06() { let mut r = Rope::new(); r.insert(0, "こ"); r.insert(1, "ん"); r.insert(2, "い"); r.insert(3, "ち"); r.insert(4, "は"); r.insert(5, "、"); r.insert(6, "み"); r.insert(7, "ん"); r.insert(8, "な"); r.insert(9, "さ"); r.insert(10, "ん"); r.insert(11, "!"); r.insert(7, "zopter"); assert_eq!("こんいちは、みzopterんなさん!", r); r.assert_integrity(); r.assert_invariants(); } #[test] fn insert_char_01() { let mut r = Rope::from_str(TEXT); r.insert_char(3, 'A'); r.insert_char(12, '!'); r.insert_char(12, '!'); assert_eq!( r, "HelAlo there!!! How're you doing? It's \ a fine day, isn't it? Aren't you glad \ we're alive? こんにちは、みんなさん!" ); r.assert_integrity(); r.assert_invariants(); } #[test] fn insert_char_02() { let mut r = Rope::new(); r.insert_char(0, '!'); r.insert_char(0, 'こ'); r.insert_char(1, 'ん'); r.insert_char(2, 'い'); r.insert_char(3, 'ち'); r.insert_char(4, 'は'); r.insert_char(5, '、'); r.insert_char(6, 'み'); r.insert_char(7, 'ん'); r.insert_char(8, 'な'); r.insert_char(9, 'さ'); r.insert_char(10, 'ん'); assert_eq!("こんいちは、みんなさん!", r); r.assert_integrity(); r.assert_invariants(); } #[test] fn remove_01() { let mut r = Rope::from_str(TEXT); r.remove(5..11); r.remove(24..31); r.remove(19..25); r.remove(75..79); assert_eq!( r, "Hello! How're you \ a fine day, isn't it? Aren't you glad \ we're alive? こんにんなさん!" ); r.assert_integrity(); r.assert_invariants(); } #[test] fn remove_02() { let mut r = Rope::from_str("\r\n\r\n\r\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"); // Make sure CRLF pairs get merged properly, via // assert_invariants() below. r.remove(3..6); assert_eq!(r, "\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"); r.assert_integrity(); r.assert_invariants(); } #[test] fn remove_03() { let mut r = Rope::from_str(TEXT); // Make sure removing nothing actually does nothing. r.remove(45..45); assert_eq!(r, TEXT); r.assert_integrity(); r.assert_invariants(); } #[test] fn remove_04() { let mut r = Rope::from_str(TEXT); // Make sure removing everything works. r.remove(0..103); assert_eq!(r, ""); r.assert_integrity(); r.assert_invariants(); } #[test] fn remove_05() { let mut r = Rope::from_str(TEXT); // Make sure removing a large range works. r.remove(3..100); assert_eq!(r, "Helさん!"); r.assert_integrity(); r.assert_invariants(); } #[test] #[should_panic] fn remove_06() { let mut r = Rope::from_str(TEXT); #[allow(clippy::reversed_empty_ranges)] r.remove(56..55); // Wrong ordering of start/end on purpose. } #[test] #[should_panic] fn remove_07() { let mut r = Rope::from_str(TEXT); r.remove(102..104); // Removing past the end } #[test] #[should_panic] fn remove_08() { let mut r = Rope::from_str(TEXT); r.remove(103..104); // Removing past the end } #[test] #[should_panic] fn remove_09() { let mut r = Rope::from_str(TEXT); r.remove(104..104); // Removing past the end } #[test] #[should_panic] fn remove_10() { let mut r = Rope::from_str(TEXT); r.remove(104..105); // Removing past the end } #[test] fn split_off_01() { let mut r = Rope::from_str(TEXT); let r2 = r.split_off(50); assert_eq!( r, "Hello there! How're you doing? It's \ a fine day, " ); assert_eq!( r2, "isn't it? Aren't you glad \ we're alive? こんにちは、みんなさん!" ); r.assert_integrity(); r2.assert_integrity(); r.assert_invariants(); r2.assert_invariants(); } #[test] fn split_off_02() { let mut r = Rope::from_str(TEXT); let r2 = r.split_off(1); assert_eq!(r, "H"); assert_eq!( r2, "ello there! How're you doing? It's \ a fine day, isn't it? Aren't you glad \ we're alive? こんにちは、みんなさん!" ); r.assert_integrity(); r2.assert_integrity(); r.assert_invariants(); r2.assert_invariants(); } #[test] fn split_off_03() { let mut r = Rope::from_str(TEXT); let r2 = r.split_off(102); assert_eq!( r, "Hello there! How're you doing? It's \ a fine day, isn't it? Aren't you glad \ we're alive? こんにちは、みんなさん" ); assert_eq!(r2, "!"); r.assert_integrity(); r2.assert_integrity(); r.assert_invariants(); r2.assert_invariants(); } #[test] fn split_off_04() { let mut r = Rope::from_str(TEXT); let r2 = r.split_off(0); assert_eq!(r, ""); assert_eq!( r2, "Hello there! How're you doing? It's \ a fine day, isn't it? Aren't you glad \ we're alive? こんにちは、みんなさん!" ); r.assert_integrity(); r2.assert_integrity(); r.assert_invariants(); r2.assert_invariants(); } #[test] fn split_off_05() { let mut r = Rope::from_str(TEXT); let r2 = r.split_off(103); assert_eq!( r, "Hello there! How're you doing? It's \ a fine day, isn't it? Aren't you glad \ we're alive? こんにちは、みんなさん!" ); assert_eq!(r2, ""); r.assert_integrity(); r2.assert_integrity(); r.assert_invariants(); r2.assert_invariants(); } #[test] #[should_panic] fn split_off_06() { let mut r = Rope::from_str(TEXT); r.split_off(104); // One past the end of the rope } #[test] fn append_01() { let mut r = Rope::from_str( "Hello there! How're you doing? It's \ a fine day, isn't ", ); let r2 = Rope::from_str( "it? Aren't you glad \ we're alive? こんにちは、みんなさん!", ); r.append(r2); assert_eq!(r, TEXT); r.assert_integrity(); r.assert_invariants(); } #[test] fn append_02() { let mut r = Rope::from_str( "Hello there! How're you doing? It's \ a fine day, isn't it? Aren't you glad \ we're alive? こんに", ); let r2 = Rope::from_str("ちは、みんなさん!"); r.append(r2); assert_eq!(r, TEXT); r.assert_integrity(); r.assert_invariants(); } #[test] fn append_03() { let mut r = Rope::from_str( "Hello there! How're you doing? It's \ a fine day, isn't it? Aren't you glad \ we're alive? こんにちは、みんなさん", ); let r2 = Rope::from_str("!"); r.append(r2); assert_eq!(r, TEXT); r.assert_integrity(); r.assert_invariants(); } #[test] fn append_04() { let mut r = Rope::from_str("H"); let r2 = Rope::from_str( "ello there! How're you doing? It's \ a fine day, isn't it? Aren't you glad \ we're alive? こんにちは、みんなさん!", ); r.append(r2); assert_eq!(r, TEXT); r.assert_integrity(); r.assert_invariants(); } #[test] fn append_05() { let mut r = Rope::from_str(TEXT); let r2 = Rope::from_str(""); r.append(r2); assert_eq!(r, TEXT); r.assert_integrity(); r.assert_invariants(); } #[test] fn append_06() { let mut r = Rope::from_str(""); let r2 = Rope::from_str(TEXT); r.append(r2); assert_eq!(r, TEXT); r.assert_integrity(); r.assert_invariants(); } #[test] fn append_07() { let mut r = Rope::from_str("\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r"); let r2 = Rope::from_str("\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r"); r.append(r2); let s2 = r.to_string(); assert_eq!(r, "\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r"); assert_eq!(r.len_lines(), byte_to_line_idx(&s2, s2.len()) + 1); r.assert_integrity(); r.assert_invariants(); } #[test] fn shrink_to_fit_01() { let mut r = Rope::new(); for _ in 0..10 { let len = r.len_chars(); r.insert(len / 2, "こ"); r.insert(len / 2, "ん"); r.insert(len / 2, "い"); r.insert(len / 2, "ち"); r.insert(len / 2, "は"); r.insert(len / 2, "、"); r.insert(len / 2, "み"); r.insert(len / 2, "ん"); r.insert(len / 2, "な"); r.insert(len / 2, "さ"); r.insert(len / 2, "ん"); r.insert(len / 2, "!"); r.insert(len / 2, "zopter"); } let r2 = r.clone(); r.shrink_to_fit(); assert_eq!(r, r2); r.assert_integrity(); r.assert_invariants(); } #[test] fn byte_to_char_01() { let r = Rope::from_str(TEXT); assert_eq!(0, r.byte_to_char(0)); assert_eq!(1, r.byte_to_char(1)); assert_eq!(2, r.byte_to_char(2)); assert_eq!(91, r.byte_to_char(91)); assert_eq!(91, r.byte_to_char(92)); assert_eq!(91, r.byte_to_char(93)); assert_eq!(92, r.byte_to_char(94)); assert_eq!(92, r.byte_to_char(95)); assert_eq!(92, r.byte_to_char(96)); assert_eq!(102, r.byte_to_char(124)); assert_eq!(102, r.byte_to_char(125)); assert_eq!(102, r.byte_to_char(126)); assert_eq!(103, r.byte_to_char(127)); } #[test] fn byte_to_line_01() { let r = Rope::from_str(TEXT_LINES); assert_eq!(0, r.byte_to_line(0)); assert_eq!(0, r.byte_to_line(1)); assert_eq!(0, r.byte_to_line(31)); assert_eq!(1, r.byte_to_line(32)); assert_eq!(1, r.byte_to_line(33)); assert_eq!(1, r.byte_to_line(58)); assert_eq!(2, r.byte_to_line(59)); assert_eq!(2, r.byte_to_line(60)); assert_eq!(2, r.byte_to_line(87)); assert_eq!(3, r.byte_to_line(88)); assert_eq!(3, r.byte_to_line(89)); assert_eq!(3, r.byte_to_line(124)); } #[test] fn byte_to_line_02() { let r = Rope::from_str(""); assert_eq!(0, r.byte_to_line(0)); } #[test] fn byte_to_line_03() { let r = Rope::from_str("Hi there\n"); assert_eq!(0, r.byte_to_line(0)); assert_eq!(0, r.byte_to_line(8)); assert_eq!(1, r.byte_to_line(9)); } #[test] #[should_panic] fn byte_to_line_04() { let r = Rope::from_str(TEXT_LINES); r.byte_to_line(125); } #[test] fn char_to_byte_01() { let r = Rope::from_str(TEXT); assert_eq!(0, r.char_to_byte(0)); assert_eq!(1, r.char_to_byte(1)); assert_eq!(2, r.char_to_byte(2)); assert_eq!(91, r.char_to_byte(91)); assert_eq!(94, r.char_to_byte(92)); assert_eq!(97, r.char_to_byte(93)); assert_eq!(100, r.char_to_byte(94)); assert_eq!(124, r.char_to_byte(102)); assert_eq!(127, r.char_to_byte(103)); } #[test] fn char_to_line_01() { let r = Rope::from_str(TEXT_LINES); assert_eq!(0, r.char_to_line(0)); assert_eq!(0, r.char_to_line(1)); assert_eq!(0, r.char_to_line(31)); assert_eq!(1, r.char_to_line(32)); assert_eq!(1, r.char_to_line(33)); assert_eq!(1, r.char_to_line(58)); assert_eq!(2, r.char_to_line(59)); assert_eq!(2, r.char_to_line(60)); assert_eq!(2, r.char_to_line(87)); assert_eq!(3, r.char_to_line(88)); assert_eq!(3, r.char_to_line(89)); assert_eq!(3, r.char_to_line(100)); } #[test] fn char_to_line_02() { let r = Rope::from_str(""); assert_eq!(0, r.char_to_line(0)); } #[test] fn char_to_line_03() { let r = Rope::from_str("Hi there\n"); assert_eq!(0, r.char_to_line(0)); assert_eq!(0, r.char_to_line(8)); assert_eq!(1, r.char_to_line(9)); } #[test] #[should_panic] fn char_to_line_04() { let r = Rope::from_str(TEXT_LINES); r.char_to_line(101); } #[test] fn line_to_byte_01() { let r = Rope::from_str(TEXT_LINES); assert_eq!(0, r.line_to_byte(0)); assert_eq!(32, r.line_to_byte(1)); assert_eq!(59, r.line_to_byte(2)); assert_eq!(88, r.line_to_byte(3)); assert_eq!(124, r.line_to_byte(4)); } #[test] fn line_to_byte_02() { let r = Rope::from_str(""); assert_eq!(0, r.line_to_byte(0)); assert_eq!(0, r.line_to_byte(1)); } #[test] #[should_panic] fn line_to_byte_03() { let r = Rope::from_str(TEXT_LINES); r.line_to_byte(5); } #[test] fn line_to_char_01() { let r = Rope::from_str(TEXT_LINES); assert_eq!(0, r.line_to_char(0)); assert_eq!(32, r.line_to_char(1)); assert_eq!(59, r.line_to_char(2)); assert_eq!(88, r.line_to_char(3)); assert_eq!(100, r.line_to_char(4)); } #[test] fn line_to_char_02() { let r = Rope::from_str(""); assert_eq!(0, r.line_to_char(0)); assert_eq!(0, r.line_to_char(1)); } #[test] #[should_panic] fn line_to_char_03() { let r = Rope::from_str(TEXT_LINES); r.line_to_char(5); } #[test] fn char_to_utf16_cu_01() { let r = Rope::from_str(""); assert_eq!(0, r.char_to_utf16_cu(0)); } #[test] #[should_panic] fn char_to_utf16_cu_02() { let r = Rope::from_str(""); r.char_to_utf16_cu(1); } #[test] fn char_to_utf16_cu_03() { let r = Rope::from_str(TEXT_EMOJI); assert_eq!(0, r.char_to_utf16_cu(0)); assert_eq!(12, r.char_to_utf16_cu(12)); assert_eq!(14, r.char_to_utf16_cu(13)); assert_eq!(33, r.char_to_utf16_cu(32)); assert_eq!(35, r.char_to_utf16_cu(33)); assert_eq!(63, r.char_to_utf16_cu(61)); assert_eq!(65, r.char_to_utf16_cu(62)); assert_eq!(95, r.char_to_utf16_cu(92)); assert_eq!(97, r.char_to_utf16_cu(93)); assert_eq!(111, r.char_to_utf16_cu(107)); } #[test] #[should_panic] fn char_to_utf16_cu_04() { let r = Rope::from_str(TEXT_EMOJI); r.char_to_utf16_cu(108); } #[test] fn utf16_cu_to_char_01() { let r = Rope::from_str(""); assert_eq!(0, r.utf16_cu_to_char(0)); } #[test] #[should_panic] fn utf16_cu_to_char_02() { let r = Rope::from_str(""); r.utf16_cu_to_char(1); } #[test] fn utf16_cu_to_char_03() { let r = Rope::from_str(TEXT_EMOJI); assert_eq!(0, r.utf16_cu_to_char(0)); assert_eq!(12, r.utf16_cu_to_char(12)); assert_eq!(12, r.utf16_cu_to_char(13)); assert_eq!(13, r.utf16_cu_to_char(14)); assert_eq!(32, r.utf16_cu_to_char(33)); assert_eq!(32, r.utf16_cu_to_char(34)); assert_eq!(33, r.utf16_cu_to_char(35)); assert_eq!(61, r.utf16_cu_to_char(63)); assert_eq!(61, r.utf16_cu_to_char(64)); assert_eq!(62, r.utf16_cu_to_char(65)); assert_eq!(92, r.utf16_cu_to_char(95)); assert_eq!(92, r.utf16_cu_to_char(96)); assert_eq!(93, r.utf16_cu_to_char(97)); assert_eq!(107, r.utf16_cu_to_char(111)); } #[test] #[should_panic] fn utf16_cu_to_char_04() { let r = Rope::from_str(TEXT_EMOJI); r.utf16_cu_to_char(112); } #[test] fn byte_01() { let r = Rope::from_str(TEXT); assert_eq!(r.byte(0), b'H'); // UTF-8 for "wide exclamation mark" assert_eq!(r.byte(124), 0xEF); assert_eq!(r.byte(125), 0xBC); assert_eq!(r.byte(126), 0x81); } #[test] #[should_panic] fn byte_02() { let r = Rope::from_str(TEXT); r.byte(127); } #[test] #[should_panic] fn byte_03() { let r = Rope::from_str(""); r.byte(0); } #[test] fn char_01() { let r = Rope::from_str(TEXT); assert_eq!(r.char(0), 'H'); assert_eq!(r.char(10), 'e'); assert_eq!(r.char(18), 'r'); assert_eq!(r.char(102), '!'); } #[test] #[should_panic] fn char_02() { let r = Rope::from_str(TEXT); r.char(103); } #[test] #[should_panic] fn char_03() { let r = Rope::from_str(""); r.char(0); } #[test] fn line_01() { let r = Rope::from_str(TEXT_LINES); let l0 = r.line(0); assert_eq!(l0, "Hello there! How're you doing?\n"); assert_eq!(l0.len_bytes(), 32); assert_eq!(l0.len_chars(), 32); assert_eq!(l0.len_lines(), 2); let l1 = r.line(1); assert_eq!(l1, "It's a fine day, isn't it?\n"); assert_eq!(l1.len_bytes(), 27); assert_eq!(l1.len_chars(), 27); assert_eq!(l1.len_lines(), 2); let l2 = r.line(2); assert_eq!(l2, "Aren't you glad we're alive?\n"); assert_eq!(l2.len_bytes(), 29); assert_eq!(l2.len_chars(), 29); assert_eq!(l2.len_lines(), 2); let l3 = r.line(3); assert_eq!(l3, "こんにちは、みんなさん!"); assert_eq!(l3.len_bytes(), 36); assert_eq!(l3.len_chars(), 12); assert_eq!(l3.len_lines(), 1); } #[test] fn line_02() { let r = Rope::from_str("Hello there! How're you doing?\n"); assert_eq!(r.line(0), "Hello there! How're you doing?\n"); assert_eq!(r.line(1), ""); } #[test] fn line_03() { let r = Rope::from_str("Hi\nHi\nHi\nHi\nHi\nHi\n"); assert_eq!(r.line(0), "Hi\n"); assert_eq!(r.line(1), "Hi\n"); assert_eq!(r.line(2), "Hi\n"); assert_eq!(r.line(3), "Hi\n"); assert_eq!(r.line(4), "Hi\n"); assert_eq!(r.line(5), "Hi\n"); assert_eq!(r.line(6), ""); } #[test] fn line_04() { let r = Rope::from_str(""); assert_eq!(r.line(0), ""); } #[test] #[should_panic] fn line_05() { let r = Rope::from_str(TEXT_LINES); r.line(4); } #[test] fn line_06() { let r = Rope::from_str("1\n2\n3\n4\n5\n6\n7\n8"); assert_eq!(r.line(0).len_lines(), 2); assert_eq!(r.line(1).len_lines(), 2); assert_eq!(r.line(2).len_lines(), 2); assert_eq!(r.line(3).len_lines(), 2); assert_eq!(r.line(4).len_lines(), 2); assert_eq!(r.line(5).len_lines(), 2); assert_eq!(r.line(6).len_lines(), 2); assert_eq!(r.line(7).len_lines(), 1); } #[test] fn chunk_at_byte() { let r = Rope::from_str(TEXT_LINES); let mut t = TEXT_LINES; let mut last_chunk = ""; for i in 0..r.len_bytes() { let (chunk, b, c, l) = r.chunk_at_byte(i); assert_eq!(c, byte_to_char_idx(TEXT_LINES, b)); assert_eq!(l, byte_to_line_idx(TEXT_LINES, b)); if chunk != last_chunk { assert_eq!(chunk, &t[..chunk.len()]); t = &t[chunk.len()..]; last_chunk = chunk; } let c1 = { let i2 = byte_to_char_idx(TEXT_LINES, i); TEXT_LINES.chars().nth(i2).unwrap() }; let c2 = { let i2 = i - b; let i3 = byte_to_char_idx(chunk, i2); chunk.chars().nth(i3).unwrap() }; assert_eq!(c1, c2); } assert_eq!(t.len(), 0); } #[test] fn chunk_at_char() { let r = Rope::from_str(TEXT_LINES); let mut t = TEXT_LINES; let mut last_chunk = ""; for i in 0..r.len_chars() { let (chunk, b, c, l) = r.chunk_at_char(i); assert_eq!(b, char_to_byte_idx(TEXT_LINES, c)); assert_eq!(l, char_to_line_idx(TEXT_LINES, c)); if chunk != last_chunk { assert_eq!(chunk, &t[..chunk.len()]); t = &t[chunk.len()..]; last_chunk = chunk; } let c1 = TEXT_LINES.chars().nth(i).unwrap(); let c2 = { let i2 = i - c; chunk.chars().nth(i2).unwrap() }; assert_eq!(c1, c2); } assert_eq!(t.len(), 0); } #[test] fn chunk_at_line_break() { let r = Rope::from_str(TEXT_LINES); // First chunk { let (chunk, b, c, l) = r.chunk_at_line_break(0); assert_eq!(chunk, &TEXT_LINES[..chunk.len()]); assert_eq!(b, 0); assert_eq!(c, 0); assert_eq!(l, 0); } // Middle chunks for i in 1..r.len_lines() { let (chunk, b, c, l) = r.chunk_at_line_break(i); assert_eq!(chunk, &TEXT_LINES[b..(b + chunk.len())]); assert_eq!(c, byte_to_char_idx(TEXT_LINES, b)); assert_eq!(l, byte_to_line_idx(TEXT_LINES, b)); assert!(l < i); assert!(i <= byte_to_line_idx(TEXT_LINES, b + chunk.len())); } // Last chunk { let (chunk, b, c, l) = r.chunk_at_line_break(r.len_lines()); assert_eq!(chunk, &TEXT_LINES[(TEXT_LINES.len() - chunk.len())..]); assert_eq!(chunk, &TEXT_LINES[b..]); assert_eq!(c, byte_to_char_idx(TEXT_LINES, b)); assert_eq!(l, byte_to_line_idx(TEXT_LINES, b)); } } #[test] fn slice_01() { let r = Rope::from_str(TEXT); let s = r.slice(0..r.len_chars()); assert_eq!(TEXT, s); } #[test] fn slice_02() { let r = Rope::from_str(TEXT); let s = r.slice(5..21); assert_eq!(&TEXT[5..21], s); } #[test] fn slice_03() { let r = Rope::from_str(TEXT); let s = r.slice(31..97); assert_eq!(&TEXT[31..109], s); } #[test] fn slice_04() { let r = Rope::from_str(TEXT); let s = r.slice(53..53); assert_eq!("", s); } #[test] #[should_panic] fn slice_05() { let r = Rope::from_str(TEXT); #[allow(clippy::reversed_empty_ranges)] r.slice(53..52); // Wrong ordering on purpose. } #[test] #[should_panic] fn slice_06() { let r = Rope::from_str(TEXT); r.slice(102..104); } #[test] fn byte_slice_01() { let r = Rope::from_str(TEXT); let s = r.byte_slice(0..r.len_bytes()); assert_eq!(TEXT, s); } #[test] fn byte_slice_02() { let r = Rope::from_str(TEXT); let s = r.byte_slice(5..21); assert_eq!(&TEXT[5..21], s); } #[test] fn byte_slice_03() { let r = Rope::from_str(TEXT); let s = r.byte_slice(31..97); assert_eq!(&TEXT[31..97], s); } #[test] fn byte_slice_04() { let r = Rope::from_str(TEXT); let s = r.byte_slice(53..53); assert_eq!("", s); } #[test] #[should_panic] fn byte_slice_05() { let r = Rope::from_str(TEXT); #[allow(clippy::reversed_empty_ranges)] r.byte_slice(53..52); // Wrong ordering on purpose. } #[test] #[should_panic] fn byte_slice_06() { let r = Rope::from_str(TEXT); r.byte_slice(20..128); } #[test] #[should_panic] fn byte_slice_07() { let r = Rope::from_str(TEXT); // Not a char boundary. r.byte_slice(..96); } #[test] #[should_panic] fn byte_slice_08() { let r = Rope::from_str(TEXT); // Not a char boundary. r.byte_slice(96..); } #[test] fn eq_rope_01() { let r = Rope::from_str(""); assert_eq!(r, r); } #[test] fn eq_rope_02() { let r = Rope::from_str(TEXT); assert_eq!(r, r); } #[test] fn eq_rope_03() { let r1 = Rope::from_str(TEXT); let mut r2 = r1.clone(); r2.remove(26..27); r2.insert(26, "z"); assert_ne!(r1, r2); } #[test] fn eq_rope_04() { let r = Rope::from_str(""); assert_eq!(r, ""); assert_eq!("", r); } #[test] fn eq_rope_05() { let r = Rope::from_str(TEXT); assert_eq!(r, TEXT); assert_eq!(TEXT, r); } #[test] fn eq_rope_06() { let mut r = Rope::from_str(TEXT); r.remove(26..27); r.insert(26, "z"); assert_ne!(r, TEXT); assert_ne!(TEXT, r); } #[test] fn eq_rope_07() { let r = Rope::from_str(TEXT); let s: String = TEXT.into(); assert_eq!(r, s); assert_eq!(s, r); } #[test] fn to_string_01() { let r = Rope::from_str(TEXT); let s: String = (&r).into(); assert_eq!(r, s); } #[test] fn to_cow_01() { use std::borrow::Cow; let r = Rope::from_str(TEXT); let cow: Cow = (&r).into(); assert_eq!(r, cow); } #[test] fn to_cow_02() { use std::borrow::Cow; let r = Rope::from_str(TEXT); let cow: Cow = (r.clone()).into(); assert_eq!(r, cow); } #[test] fn to_cow_03() { use std::borrow::Cow; let r = Rope::from_str("a"); let cow: Cow = (&r).into(); // Make sure it's borrowed. if let Cow::Owned(_) = cow { panic!("Small Cow conversions should result in a borrow."); } assert_eq!(r, cow); } #[test] fn from_rope_slice_01() { let r1 = Rope::from_str(TEXT); let s = r1.slice(..); let r2: Rope = s.into(); assert_eq!(r1, r2); assert_eq!(s, r2); } #[test] fn from_rope_slice_02() { let r1 = Rope::from_str(TEXT); let s = r1.slice(0..24); let r2: Rope = s.into(); assert_eq!(s, r2); } #[test] fn from_rope_slice_03() { let r1 = Rope::from_str(TEXT); let s = r1.slice(13..89); let r2: Rope = s.into(); assert_eq!(s, r2); } #[test] fn from_rope_slice_04() { let r1 = Rope::from_str(TEXT); let s = r1.slice(13..41); let r2: Rope = s.into(); assert_eq!(s, r2); } #[test] fn from_iter_01() { let r1 = Rope::from_str(TEXT); let r2: Rope = Rope::from_iter(r1.chunks()); assert_eq!(r1, r2); } #[test] fn hash_01() { let mut h1 = std::collections::hash_map::DefaultHasher::new(); let mut h2 = std::collections::hash_map::DefaultHasher::new(); let r1 = Rope::from_str("Hello there!"); let mut r2 = Rope::from_str("Hlotee"); r2.insert_char(3, ' '); r2.insert_char(7, '!'); r2.insert_char(1, 'e'); r2.insert_char(3, 'l'); r2.insert_char(7, 'h'); r2.insert_char(9, 'r'); r1.hash(&mut h1); r2.hash(&mut h2); assert_eq!(h1.finish(), h2.finish()); } #[test] fn hash_02() { let mut h1 = std::collections::hash_map::DefaultHasher::new(); let mut h2 = std::collections::hash_map::DefaultHasher::new(); let r1 = Rope::from_str("Hello there!"); let r2 = Rope::from_str("Hello there."); r1.hash(&mut h1); r2.hash(&mut h2); assert_ne!(h1.finish(), h2.finish()); } #[test] fn hash_03() { let mut h1 = std::collections::hash_map::DefaultHasher::new(); let mut h2 = std::collections::hash_map::DefaultHasher::new(); let r = Rope::from_str("Hello there!"); let s = [Rope::from_str("Hello "), Rope::from_str("there!")]; r.hash(&mut h1); Rope::hash_slice(&s, &mut h2); assert_ne!(h1.finish(), h2.finish()); } #[test] fn is_instance_01() { let r = Rope::from_str("Hello there!"); let mut c1 = r.clone(); let c2 = c1.clone(); assert!(r.is_instance(&c1)); assert!(r.is_instance(&c2)); assert!(c1.is_instance(&c2)); c1.insert(0, "Oh! "); assert!(!r.is_instance(&c1)); assert!(r.is_instance(&c2)); assert!(!c1.is_instance(&c2)); } // Iterator tests are in the iter module } ropey-1.6.1/src/rope_builder.rs000064400000000000000000000304601046102023000146020ustar 00000000000000use std::sync::Arc; use smallvec::SmallVec; use crate::crlf; use crate::rope::Rope; use crate::tree::{Node, NodeChildren, NodeText, MAX_BYTES, MAX_CHILDREN, MIN_BYTES}; /// An efficient incremental `Rope` builder. /// /// This is used to efficiently build ropes from sequences of text /// chunks. It is useful for creating ropes from: /// /// - ...large text files, without pre-loading their entire contents into /// memory (but see [`from_reader()`](Rope::from_reader) for a convenience /// function that does this for casual use-cases). /// - ...streaming data sources. /// - ...non-utf8 text data, doing the encoding conversion incrementally /// as you go. /// /// Unlike repeatedly calling `Rope::insert()` on the end of a rope, /// this API runs in time linear to the amount of data fed to it, and /// is overall much faster. /// /// # Example /// ``` /// # use ropey::RopeBuilder; /// # /// let mut builder = RopeBuilder::new(); /// /// builder.append("Hello "); /// builder.append("world!\n"); /// builder.append("How's "); /// builder.append("it goin"); /// builder.append("g?"); /// /// let rope = builder.finish(); /// /// assert_eq!(rope, "Hello world!\nHow's it going?"); /// ``` #[derive(Debug, Clone)] pub struct RopeBuilder { stack: SmallVec<[Arc; 4]>, buffer: String, last_chunk_len_bytes: usize, } impl RopeBuilder { /// Creates a new RopeBuilder, ready for input. pub fn new() -> Self { RopeBuilder { stack: { let mut stack = SmallVec::new(); stack.push(Arc::new(Node::new())); stack }, buffer: String::new(), last_chunk_len_bytes: 0, } } /// Appends `chunk` to the end of the in-progress `Rope`. /// /// Call this method repeatedly to incrementally build up a /// `Rope`. The passed text chunk can be as large or small as /// desired, but larger chunks are more efficient. /// /// `chunk` must be valid utf8 text. pub fn append(&mut self, chunk: &str) { self.append_internal(chunk, false); } /// Finishes the build, and returns the `Rope`. /// /// Note: this method consumes the builder. If you want to continue /// building other ropes with the same prefix, you can clone the builder /// before calling `finish()`. pub fn finish(mut self) -> Rope { // Append the last leaf self.append_internal("", true); self.finish_internal(true) } /// Builds a rope all at once from a single string slice. /// /// This avoids the creation and use of the internal buffer. This is /// for internal use only, because the public-facing API has /// Rope::from_str(), which actually uses this for its implementation. pub(crate) fn build_at_once(mut self, chunk: &str) -> Rope { self.append_internal(chunk, true); self.finish_internal(true) } /// NOT PART OF THE PUBLIC API (hidden from docs for a reason!). /// /// Appends `contents` to the in-progress rope as a single leaf /// node (chunk). This is useful for building ropes with specific /// chunk configurations for testing purposes. It will happily append /// both empty and more-than-max-size chunks. /// /// This makes no attempt to be consistent with the standard `append()` /// method, and should not be used in conjunction with it. #[doc(hidden)] pub fn _append_chunk(&mut self, contents: &str) { self.append_leaf_node(Arc::new(Node::Leaf(NodeText::from_str(contents)))); } /// NOT PART OF THE PUBLIC API (hidden from docs for a reason!). /// /// Finishes the build without doing any tree fixing to adhere /// to the btree invariants. To be used with `_append_chunk()` to /// construct ropes with specific chunk boundaries for testing. #[doc(hidden)] pub fn _finish_no_fix(self) -> Rope { self.finish_internal(false) } //----------------------------------------------------------------- // Internal workings of `append()`. fn append_internal(&mut self, chunk: &str, is_last_chunk: bool) { let mut chunk = chunk; // Repeatedly chop text off the end of the input, creating // leaf nodes out of them and appending them to the tree. while !chunk.is_empty() || (!self.buffer.is_empty() && is_last_chunk) { // Get the text for the next leaf let (leaf_text, remainder) = self.get_next_leaf_text(chunk, is_last_chunk); chunk = remainder; self.last_chunk_len_bytes = chunk.len(); // Append the leaf to the rope match leaf_text { NextText::None => break, NextText::UseBuffer => { let leaf_text = NodeText::from_str(&self.buffer); self.append_leaf_node(Arc::new(Node::Leaf(leaf_text))); self.buffer.clear(); } NextText::String(s) => { self.append_leaf_node(Arc::new(Node::Leaf(NodeText::from_str(s)))); } } } } // Internal workings of `finish()`. // // When `fix_tree` is false, the resulting node tree is NOT fixed up // to adhere to the btree invariants. This is useful for some testing // code. But generally, `fix_tree` should be set to true. fn finish_internal(mut self, fix_tree: bool) -> Rope { // Zip up all the remaining nodes on the stack let mut stack_idx = self.stack.len() - 1; while stack_idx >= 1 { let node = self.stack.pop().unwrap(); if let Node::Internal(ref mut children) = *Arc::make_mut(&mut self.stack[stack_idx - 1]) { children.push((node.text_info(), node)); } else { unreachable!(); } stack_idx -= 1; } // Create the rope. let mut rope = Rope { root: self.stack.pop().unwrap(), }; // Fix up the tree to be well-formed. if fix_tree { Arc::make_mut(&mut rope.root).zip_fix_right(); if self.last_chunk_len_bytes < MIN_BYTES && self.last_chunk_len_bytes != rope.len_bytes() { // Merge the last chunk if it was too small. let idx = rope.len_chars() - rope.byte_to_char(rope.len_bytes() - self.last_chunk_len_bytes); Arc::make_mut(&mut rope.root).fix_tree_seam(idx); } rope.pull_up_singular_nodes(); } return rope; } // Returns (next_leaf_text, remaining_text) #[inline(always)] fn get_next_leaf_text<'a>( &mut self, text: &'a str, is_last_chunk: bool, ) -> (NextText<'a>, &'a str) { assert!( self.buffer.len() < MAX_BYTES, "RopeBuilder: buffer is already full when receiving a chunk! \ This should never happen!", ); // Simplest case: empty buffer and enough in `text` for a full // chunk, so just chop a chunk off from `text` and use that. if self.buffer.is_empty() && text.len() >= MAX_BYTES { let split_idx = crlf::find_good_split( MAX_BYTES.min(text.len() - 1), // - 1 to avoid CRLF split. text.as_bytes(), true, ); return (NextText::String(&text[..split_idx]), &text[split_idx..]); } // If the buffer + `text` is enough for a full chunk, push enough // of `text` onto the buffer to fill it and use that. else if (text.len() + self.buffer.len()) >= MAX_BYTES { let mut split_idx = crlf::find_good_split(MAX_BYTES - self.buffer.len(), text.as_bytes(), true); if split_idx == text.len() && text.as_bytes()[text.len() - 1] == 0x0D { // Avoid CRLF split. split_idx -= 1; }; self.buffer.push_str(&text[..split_idx]); return (NextText::UseBuffer, &text[split_idx..]); } // If we don't have enough text for a full chunk. else { // If it's our last chunk, wrap it all up! if is_last_chunk { if self.buffer.is_empty() { return if text.is_empty() { (NextText::None, "") } else { (NextText::String(text), "") }; } else { self.buffer.push_str(text); return (NextText::UseBuffer, ""); } } // Otherwise, just push to the buffer. else { self.buffer.push_str(text); return (NextText::None, ""); } } } fn append_leaf_node(&mut self, leaf: Arc) { let last = self.stack.pop().unwrap(); match *last { Node::Leaf(_) => { if last.leaf_text().is_empty() { self.stack.push(leaf); } else { let mut children = NodeChildren::new(); children.push((last.text_info(), last)); children.push((leaf.text_info(), leaf)); self.stack.push(Arc::new(Node::Internal(children))); } } Node::Internal(_) => { self.stack.push(last); let mut left = leaf; let mut stack_idx = (self.stack.len() - 1) as isize; loop { if stack_idx < 0 { // We're above the root, so do a root split. let mut children = NodeChildren::new(); children.push((left.text_info(), left)); self.stack.insert(0, Arc::new(Node::Internal(children))); break; } else if self.stack[stack_idx as usize].child_count() < (MAX_CHILDREN - 1) { // There's room to add a child, so do that. Arc::make_mut(&mut self.stack[stack_idx as usize]) .children_mut() .push((left.text_info(), left)); break; } else { // Not enough room to fit a child, so split. left = Arc::new(Node::Internal( Arc::make_mut(&mut self.stack[stack_idx as usize]) .children_mut() .push_split((left.text_info(), left)), )); std::mem::swap(&mut left, &mut self.stack[stack_idx as usize]); stack_idx -= 1; } } } } } } impl Default for RopeBuilder { fn default() -> Self { Self::new() } } enum NextText<'a> { None, UseBuffer, String(&'a str), } //=========================================================================== #[cfg(test)] mod tests { use super::*; // 127 bytes, 103 chars, 4 lines const TEXT: &str = "Hello there! How're you doing?\r\nIt's \ a fine day, isn't it?\r\nAren't you glad \ we're alive?\r\nこんにちは、みんなさん!"; #[test] fn rope_builder_01() { let mut b = RopeBuilder::new(); b.append("Hello there! How're you doing?\r"); b.append("\nIt's a fine "); b.append("d"); b.append("a"); b.append("y,"); b.append(" "); b.append("isn't it?"); b.append("\r"); b.append("\nAren't you "); b.append("glad we're alive?\r"); b.append("\n"); b.append("こんにち"); b.append("は、みんなさ"); b.append("ん!"); let r = b.finish(); assert_eq!(r, TEXT); r.assert_integrity(); r.assert_invariants(); } #[test] fn rope_builder_default_01() { let mut b = RopeBuilder::default(); b.append("Hello there! How're you doing?\r"); b.append("\nIt's a fine day, isn't it?\r\nAren't you "); b.append("glad we're alive?\r\nこんにちは、みんなさん!"); let r = b.finish(); assert_eq!(r, TEXT); r.assert_integrity(); r.assert_invariants(); } } ropey-1.6.1/src/slice.rs000064400000000000000000003117741046102023000132400ustar 00000000000000use std::ops::RangeBounds; use std::sync::Arc; use crate::iter::{Bytes, Chars, Chunks, Lines}; use crate::rope::Rope; use crate::str_utils::{ byte_to_char_idx, byte_to_line_idx, byte_to_utf16_surrogate_idx, char_to_byte_idx, char_to_line_idx, count_chars, count_line_breaks, count_utf16_surrogates, line_to_byte_idx, line_to_char_idx, utf16_code_unit_to_char_idx, }; use crate::tree::{Count, Node, TextInfo}; use crate::{end_bound_to_num, start_bound_to_num, Error, Result}; /// An immutable view into part of a `Rope`. /// /// Just like standard `&str` slices, `RopeSlice`s behave as if the text in /// their range is the only text that exists. All indexing is relative to /// the start of their range, and all iterators and methods that return text /// truncate that text to the range of the slice. /// /// In other words, the behavior of a `RopeSlice` is always identical to that /// of a full `Rope` created from the same text range. Nothing should be /// surprising here. #[derive(Copy, Clone)] pub struct RopeSlice<'a>(pub(crate) RSEnum<'a>); #[derive(Copy, Clone, Debug)] pub(crate) enum RSEnum<'a> { Full { node: &'a Arc, start_info: TextInfo, end_info: TextInfo, }, Light { text: &'a str, char_count: Count, utf16_surrogate_count: Count, line_break_count: Count, }, } impl<'a> RopeSlice<'a> { /// Used for tests and debugging purposes. #[allow(dead_code)] pub(crate) fn is_light(&self) -> bool { matches!(&self.0, RSEnum::Light { .. }) } pub(crate) fn new_with_range(node: &'a Arc, start: usize, end: usize) -> RopeSlice<'a> { assert!(start <= end); assert!(end <= node.text_info().chars as usize); // Early-out shortcut for taking a slice of the full thing. if start == 0 && end == node.char_count() { if node.is_leaf() { let text = node.leaf_text(); return RopeSlice(RSEnum::Light { text: text, char_count: (end - start) as Count, utf16_surrogate_count: count_utf16_surrogates(text) as Count, line_break_count: count_line_breaks(text) as Count, }); } else { return RopeSlice(RSEnum::Full { node: node, start_info: TextInfo { bytes: 0, chars: 0, utf16_surrogates: 0, line_breaks: 0, }, end_info: TextInfo { bytes: node.byte_count() as Count, chars: node.char_count() as Count, utf16_surrogates: node.utf16_surrogate_count() as Count, line_breaks: node.line_break_count() as Count, }, }); } } // Find the deepest node that still contains the full range given. let mut n_start = start; let mut n_end = end; let mut node = node; 'outer: loop { match *(node as &Node) { // Early out if we reach a leaf, because we can do the // simpler lightweight slice then. Node::Leaf(ref text) => { let start_byte = char_to_byte_idx(text, n_start); let end_byte = start_byte + char_to_byte_idx(&text[start_byte..], n_end - n_start); return RopeSlice(RSEnum::Light { text: &text[start_byte..end_byte], char_count: (n_end - n_start) as Count, utf16_surrogate_count: count_utf16_surrogates(&text[start_byte..end_byte]) as Count, line_break_count: count_line_breaks(&text[start_byte..end_byte]) as Count, }); } Node::Internal(ref children) => { let mut start_char = 0; for (i, inf) in children.info().iter().enumerate() { if n_start >= start_char && n_end <= (start_char + inf.chars as usize) { n_start -= start_char; n_end -= start_char; node = &children.nodes()[i]; continue 'outer; } start_char += inf.chars as usize; } break; } } } // Create the slice RopeSlice(RSEnum::Full { node: node, start_info: node.char_to_text_info(n_start), end_info: { #[cfg(any(feature = "cr_lines", feature = "unicode_lines"))] { let mut info = node.char_to_text_info(n_end); if node.is_crlf_split(n_end) { info.line_breaks += 1; } info } #[cfg(not(any(feature = "cr_lines", feature = "unicode_lines")))] node.char_to_text_info(n_end) }, }) } pub(crate) fn new_with_byte_range( node: &'a Arc, start: usize, end: usize, ) -> Result> { assert!(start <= end); assert!(end <= node.text_info().bytes as usize); // Early-out shortcut for taking a slice of the full thing. if start == 0 && end == node.byte_count() { if node.is_leaf() { let text = node.leaf_text(); return Ok(RopeSlice(RSEnum::Light { text, char_count: count_chars(text) as Count, utf16_surrogate_count: count_utf16_surrogates(text) as Count, line_break_count: count_line_breaks(text) as Count, })); } else { return Ok(RopeSlice(RSEnum::Full { node, start_info: TextInfo { bytes: 0, chars: 0, utf16_surrogates: 0, line_breaks: 0, }, end_info: TextInfo { bytes: node.byte_count() as Count, chars: node.char_count() as Count, utf16_surrogates: node.utf16_surrogate_count() as Count, line_breaks: node.line_break_count() as Count, }, })); } } // Find the deepest node that still contains the full range given. let mut n_start = start; let mut n_end = end; let mut node = node; 'outer: loop { match *(node as &Node) { // Early out if we reach a leaf, because we can do the // simpler lightweight slice then. Node::Leaf(ref text) => { let start_byte = n_start; let end_byte = n_end; if !text.is_char_boundary(start_byte) || !text.is_char_boundary(end_byte) { return Err(Error::ByteRangeNotCharBoundary(Some(start), Some(end))); } return Ok(RopeSlice(RSEnum::Light { text: &text[start_byte..end_byte], char_count: count_chars(&text[start_byte..end_byte]) as Count, utf16_surrogate_count: count_utf16_surrogates(&text[start_byte..end_byte]) as Count, line_break_count: count_line_breaks(&text[start_byte..end_byte]) as Count, })); } Node::Internal(ref children) => { let mut start_byte = 0; for (i, inf) in children.info().iter().enumerate() { if n_start >= start_byte && n_end <= (start_byte + inf.bytes as usize) { n_start -= start_byte; n_end -= start_byte; node = &children.nodes()[i]; continue 'outer; } start_byte += inf.bytes as usize; } break; } } } // Make sure the bytes indices are valid char boundaries. if !node.is_char_boundary(n_start) || !node.is_char_boundary(n_end) { return Err(Error::ByteRangeNotCharBoundary(Some(start), Some(end))); } // Create the slice Ok(RopeSlice(RSEnum::Full { node, start_info: node.byte_to_text_info(n_start), end_info: node.byte_to_text_info(n_end), })) } //----------------------------------------------------------------------- // Informational methods /// Total number of bytes in the `RopeSlice`. /// /// Runs in O(1) time. #[inline] pub fn len_bytes(&self) -> usize { match *self { RopeSlice(RSEnum::Full { end_info, start_info, .. }) => (end_info.bytes - start_info.bytes) as usize, RopeSlice(RSEnum::Light { text, .. }) => text.len(), } } /// Total number of chars in the `RopeSlice`. /// /// Runs in O(1) time. #[inline] pub fn len_chars(&self) -> usize { match *self { RopeSlice(RSEnum::Full { end_info, start_info, .. }) => (end_info.chars - start_info.chars) as usize, RopeSlice(RSEnum::Light { char_count, .. }) => char_count as usize, } } /// Total number of lines in the `RopeSlice`. /// /// Runs in O(1) time. #[inline] pub fn len_lines(&self) -> usize { match *self { RopeSlice(RSEnum::Full { end_info, start_info, .. }) => (end_info.line_breaks - start_info.line_breaks) as usize + 1, RopeSlice(RSEnum::Light { line_break_count, .. }) => line_break_count as usize + 1, } } /// Total number of utf16 code units that would be in the `RopeSlice` if /// it were encoded as utf16. /// /// Ropey stores text internally as utf8, but sometimes it is necessary /// to interact with external APIs that still use utf16. This function is /// primarily intended for such situations, and is otherwise not very /// useful. /// /// Runs in O(1) time. #[inline] pub fn len_utf16_cu(&self) -> usize { match *self { RopeSlice(RSEnum::Full { end_info, start_info, .. }) => { ((end_info.chars + end_info.utf16_surrogates) - (start_info.chars + start_info.utf16_surrogates)) as usize } RopeSlice(RSEnum::Light { char_count, utf16_surrogate_count, .. }) => (char_count + utf16_surrogate_count) as usize, } } //----------------------------------------------------------------------- // Index conversion methods /// Returns the char index of the given byte. /// /// Notes: /// /// - If the byte is in the middle of a multi-byte char, returns the /// index of the char that the byte belongs to. /// - `byte_idx` can be one-past-the-end, which will return one-past-the-end /// char index. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `byte_idx` is out of bounds (i.e. `byte_idx > len_bytes()`). #[inline] pub fn byte_to_char(&self, byte_idx: usize) -> usize { self.try_byte_to_char(byte_idx).unwrap() } /// Returns the line index of the given byte. /// /// Notes: /// /// - Lines are zero-indexed. This is functionally equivalent to /// counting the line endings before the specified byte. /// - `byte_idx` can be one-past-the-end, which will return the /// last line index. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `byte_idx` is out of bounds (i.e. `byte_idx > len_bytes()`). #[inline] pub fn byte_to_line(&self, byte_idx: usize) -> usize { self.try_byte_to_line(byte_idx).unwrap() } /// Returns the byte index of the given char. /// /// Notes: /// /// - `char_idx` can be one-past-the-end, which will return /// one-past-the-end byte index. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `char_idx` is out of bounds (i.e. `char_idx > len_chars()`). #[inline] pub fn char_to_byte(&self, char_idx: usize) -> usize { self.try_char_to_byte(char_idx).unwrap() } /// Returns the line index of the given char. /// /// Notes: /// /// - Lines are zero-indexed. This is functionally equivalent to /// counting the line endings before the specified char. /// - `char_idx` can be one-past-the-end, which will return the /// last line index. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `char_idx` is out of bounds (i.e. `char_idx > len_chars()`). #[inline] pub fn char_to_line(&self, char_idx: usize) -> usize { self.try_char_to_line(char_idx).unwrap() } /// Returns the utf16 code unit index of the given char. /// /// Ropey stores text internally as utf8, but sometimes it is necessary /// to interact with external APIs that still use utf16. This function is /// primarily intended for such situations, and is otherwise not very /// useful. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `char_idx` is out of bounds (i.e. `char_idx > len_chars()`). #[inline] pub fn char_to_utf16_cu(&self, char_idx: usize) -> usize { self.try_char_to_utf16_cu(char_idx).unwrap() } /// Returns the char index of the given utf16 code unit. /// /// Ropey stores text internally as utf8, but sometimes it is necessary /// to interact with external APIs that still use utf16. This function is /// primarily intended for such situations, and is otherwise not very /// useful. /// /// Note: if the utf16 code unit is in the middle of a char, returns the /// index of the char that it belongs to. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `utf16_cu_idx` is out of bounds /// (i.e. `utf16_cu_idx > len_utf16_cu()`). #[inline] pub fn utf16_cu_to_char(&self, utf16_cu_idx: usize) -> usize { self.try_utf16_cu_to_char(utf16_cu_idx).unwrap() } /// Returns the byte index of the start of the given line. /// /// Notes: /// /// - Lines are zero-indexed. /// - `line_idx` can be one-past-the-end, which will return /// one-past-the-end byte index. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `line_idx` is out of bounds (i.e. `line_idx > len_lines()`). #[inline] pub fn line_to_byte(&self, line_idx: usize) -> usize { self.try_line_to_byte(line_idx).unwrap() } /// Returns the char index of the start of the given line. /// /// Notes: /// /// - Lines are zero-indexed. /// - `line_idx` can be one-past-the-end, which will return /// one-past-the-end char index. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `line_idx` is out of bounds (i.e. `line_idx > len_lines()`). #[inline] pub fn line_to_char(&self, line_idx: usize) -> usize { self.try_line_to_char(line_idx).unwrap() } //----------------------------------------------------------------------- // Fetch methods /// Returns the byte at `byte_idx`. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `byte_idx` is out of bounds (i.e. `byte_idx >= len_bytes()`). #[inline] pub fn byte(&self, byte_idx: usize) -> u8 { // Bounds check if let Some(out) = self.get_byte(byte_idx) { out } else { panic!( "Attempt to index past end of slice: byte index {}, slice byte length {}", byte_idx, self.len_bytes() ); } } /// Returns the char at `char_idx`. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `char_idx` is out of bounds (i.e. `char_idx >= len_chars()`). #[inline] pub fn char(&self, char_idx: usize) -> char { if let Some(out) = self.get_char(char_idx) { out } else { panic!( "Attempt to index past end of slice: char index {}, slice char length {}", char_idx, self.len_chars() ); } } /// Returns the line at `line_idx`. /// /// Note: lines are zero-indexed. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `line_idx` is out of bounds (i.e. `line_idx >= len_lines()`). #[inline] pub fn line(&self, line_idx: usize) -> RopeSlice<'a> { if let Some(out) = self.get_line(line_idx) { out } else { let len_lines = self.len_lines(); panic!( "Attempt to index past end of slice: line index {}, slice line length {}", line_idx, len_lines ); } } /// Returns the chunk containing the given byte index. /// /// Also returns the byte and char indices of the beginning of the chunk /// and the index of the line that the chunk starts on. /// /// Note: for convenience, a one-past-the-end `byte_idx` returns the last /// chunk of the `RopeSlice`. /// /// The return value is organized as /// `(chunk, chunk_byte_idx, chunk_char_idx, chunk_line_idx)`. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `byte_idx` is out of bounds (i.e. `byte_idx > len_bytes()`). pub fn chunk_at_byte(&self, byte_idx: usize) -> (&'a str, usize, usize, usize) { self.try_chunk_at_byte(byte_idx).unwrap() } /// Returns the chunk containing the given char index. /// /// Also returns the byte and char indices of the beginning of the chunk /// and the index of the line that the chunk starts on. /// /// Note: for convenience, a one-past-the-end `char_idx` returns the last /// chunk of the `RopeSlice`. /// /// The return value is organized as /// `(chunk, chunk_byte_idx, chunk_char_idx, chunk_line_idx)`. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `char_idx` is out of bounds (i.e. `char_idx > len_chars()`). pub fn chunk_at_char(&self, char_idx: usize) -> (&'a str, usize, usize, usize) { if let Some(out) = self.get_chunk_at_char(char_idx) { out } else { panic!( "Attempt to index past end of slice: char index {}, slice char length {}", char_idx, self.len_chars() ); } } /// Returns the chunk containing the given line break. /// /// Also returns the byte and char indices of the beginning of the chunk /// and the index of the line that the chunk starts on. /// /// Note: for convenience, both the beginning and end of the slice are /// considered line breaks for the purposes of indexing. For example, in /// the string `"Hello \n world!"` 0 would give the first chunk, 1 would /// give the chunk containing the newline character, and 2 would give the /// last chunk. /// /// The return value is organized as /// `(chunk, chunk_byte_idx, chunk_char_idx, chunk_line_idx)`. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `line_break_idx` is out of bounds (i.e. `line_break_idx > len_lines()`). pub fn chunk_at_line_break(&self, line_break_idx: usize) -> (&'a str, usize, usize, usize) { if let Some(out) = self.get_chunk_at_line_break(line_break_idx) { out } else { panic!( "Attempt to index past end of Rope: line break index {}, max index {}", line_break_idx, self.len_lines() ); } } /// Returns the entire contents of the `RopeSlice` as a `&str` if /// possible. /// /// This is useful for optimizing cases where the slice is only a few /// characters or words, and therefore has a high chance of being /// contiguous in memory. /// /// For large slices this method will typically fail and return `None` /// because large slices usually cross chunk boundaries in the rope. /// /// (Also see the `From` impl for converting to a `Cow`.) /// /// Runs in O(1) time. #[inline] pub fn as_str(&self) -> Option<&'a str> { match *self { RopeSlice(RSEnum::Full { .. }) => None, RopeSlice(RSEnum::Light { text, .. }) => Some(text), } } //----------------------------------------------------------------------- // Slice creation /// Returns a sub-slice of the `RopeSlice` in the given char index range. /// /// Uses range syntax, e.g. `2..7`, `2..`, etc. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if the start of the range is greater than the end, or the end /// is out of bounds (i.e. `end > len_chars()`). pub fn slice(&self, char_range: R) -> RopeSlice<'a> where R: RangeBounds, { let (start, end) = { let start_range = start_bound_to_num(char_range.start_bound()); let end_range = end_bound_to_num(char_range.end_bound()); // Early-out shortcut for taking a slice of the full thing. if start_range == None && end_range == None { return *self; } ( start_range.unwrap_or(0), end_range.unwrap_or_else(|| self.len_chars()), ) }; // Bounds check assert!(start <= end); assert!( end <= self.len_chars(), "Attempt to slice past end of RopeSlice: slice end {}, RopeSlice length {}", end, self.len_chars() ); match *self { RopeSlice(RSEnum::Full { node, start_info, .. }) => RopeSlice::new_with_range( node, start_info.chars as usize + start, start_info.chars as usize + end, ), RopeSlice(RSEnum::Light { text, .. }) => { let start_byte = char_to_byte_idx(text, start); let end_byte = char_to_byte_idx(text, end); let new_text = &text[start_byte..end_byte]; RopeSlice(RSEnum::Light { text: new_text, char_count: (end - start) as Count, utf16_surrogate_count: count_utf16_surrogates(new_text) as Count, line_break_count: count_line_breaks(new_text) as Count, }) } } } /// Returns a sub-slice of the `RopeSlice` in the given byte index range. /// /// Uses range syntax, e.g. `2..7`, `2..`, etc. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if: /// - The start of the range is greater than the end. /// - The end is out of bounds (i.e. `end > len_bytes()`). /// - The range doesn't align with char boundaries. pub fn byte_slice(&self, byte_range: R) -> RopeSlice<'a> where R: RangeBounds, { match self.get_byte_slice_impl(byte_range) { Ok(s) => return s, Err(e) => panic!("byte_slice(): {}", e), } } //----------------------------------------------------------------------- // Iterator methods /// Creates an iterator over the bytes of the `RopeSlice`. /// /// Runs in O(log N) time. #[inline] pub fn bytes(&self) -> Bytes<'a> { match *self { RopeSlice(RSEnum::Full { node, start_info, end_info, }) => Bytes::new_with_range( node, (start_info.bytes as usize, end_info.bytes as usize), (start_info.chars as usize, end_info.chars as usize), ( start_info.line_breaks as usize, end_info.line_breaks as usize + 1, ), ), RopeSlice(RSEnum::Light { text, .. }) => Bytes::from_str(text), } } /// Creates an iterator over the bytes of the `RopeSlice`, starting at /// byte `byte_idx`. /// /// If `byte_idx == len_bytes()` then an iterator at the end of the /// `RopeSlice` is created (i.e. `next()` will return `None`). /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `byte_idx` is out of bounds (i.e. `byte_idx > len_bytes()`). #[inline] pub fn bytes_at(&self, byte_idx: usize) -> Bytes<'a> { if let Some(out) = self.get_bytes_at(byte_idx) { out } else { panic!( "Attempt to index past end of RopeSlice: byte index {}, RopeSlice byte length {}", byte_idx, self.len_bytes() ); } } /// Creates an iterator over the chars of the `RopeSlice`. /// /// Runs in O(log N) time. #[inline] pub fn chars(&self) -> Chars<'a> { match *self { RopeSlice(RSEnum::Full { node, start_info, end_info, }) => Chars::new_with_range( node, (start_info.bytes as usize, end_info.bytes as usize), (start_info.chars as usize, end_info.chars as usize), ( start_info.line_breaks as usize, end_info.line_breaks as usize + 1, ), ), RopeSlice(RSEnum::Light { text, .. }) => Chars::from_str(text), } } /// Creates an iterator over the chars of the `RopeSlice`, starting at /// char `char_idx`. /// /// If `char_idx == len_chars()` then an iterator at the end of the /// `RopeSlice` is created (i.e. `next()` will return `None`). /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `char_idx` is out of bounds (i.e. `char_idx > len_chars()`). #[inline] pub fn chars_at(&self, char_idx: usize) -> Chars<'a> { if let Some(out) = self.get_chars_at(char_idx) { out } else { panic!( "Attempt to index past end of RopeSlice: char index {}, RopeSlice char length {}", char_idx, self.len_chars() ); } } /// Creates an iterator over the lines of the `RopeSlice`. /// /// Runs in O(log N) time. #[inline] pub fn lines(&self) -> Lines<'a> { match *self { RopeSlice(RSEnum::Full { node, start_info, end_info, }) => Lines::new_with_range( node, (start_info.bytes as usize, end_info.bytes as usize), ( start_info.line_breaks as usize, end_info.line_breaks as usize + 1, ), ), RopeSlice(RSEnum::Light { text, line_break_count, .. }) => Lines::from_str(text, line_break_count as usize + 1), } } /// Creates an iterator over the lines of the `RopeSlice`, starting at /// line `line_idx`. /// /// If `line_idx == len_lines()` then an iterator at the end of the /// `RopeSlice` is created (i.e. `next()` will return `None`). /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `line_idx` is out of bounds (i.e. `line_idx > len_lines()`). #[inline] pub fn lines_at(&self, line_idx: usize) -> Lines<'a> { if let Some(out) = self.get_lines_at(line_idx) { out } else { panic!( "Attempt to index past end of RopeSlice: line index {}, RopeSlice line length {}", line_idx, self.len_lines() ); } } /// Creates an iterator over the chunks of the `RopeSlice`. /// /// Runs in O(log N) time. #[inline] pub fn chunks(&self) -> Chunks<'a> { match *self { RopeSlice(RSEnum::Full { node, start_info, end_info, }) => Chunks::new_with_range( node, (start_info.bytes as usize, end_info.bytes as usize), (start_info.chars as usize, end_info.chars as usize), ( start_info.line_breaks as usize, end_info.line_breaks as usize + 1, ), ), RopeSlice(RSEnum::Light { text, .. }) => Chunks::from_str(text, false), } } /// Creates an iterator over the chunks of the `RopeSlice`, with the /// iterator starting at the byte containing `byte_idx`. /// /// Also returns the byte and char indices of the beginning of the first /// chunk to be yielded, and the index of the line that chunk starts on. /// /// If `byte_idx == len_bytes()` an iterator at the end of the `RopeSlice` /// (yielding `None` on a call to `next()`) is created. /// /// The return value is organized as /// `(iterator, chunk_byte_idx, chunk_char_idx, chunk_line_idx)`. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `byte_idx` is out of bounds (i.e. `byte_idx > len_bytes()`). #[inline] pub fn chunks_at_byte(&self, byte_idx: usize) -> (Chunks<'a>, usize, usize, usize) { if let Some(out) = self.get_chunks_at_byte(byte_idx) { out } else { panic!( "Attempt to index past end of RopeSlice: byte index {}, RopeSlice byte length {}", byte_idx, self.len_bytes() ); } } /// Creates an iterator over the chunks of the `RopeSlice`, with the /// iterator starting on the chunk containing `char_idx`. /// /// Also returns the byte and char indices of the beginning of the first /// chunk to be yielded, and the index of the line that chunk starts on. /// /// If `char_idx == len_chars()` an iterator at the end of the `RopeSlice` /// (yielding `None` on a call to `next()`) is created. /// /// The return value is organized as /// `(iterator, chunk_byte_idx, chunk_char_idx, chunk_line_idx)`. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `char_idx` is out of bounds (i.e. `char_idx > len_chars()`). #[inline] pub fn chunks_at_char(&self, char_idx: usize) -> (Chunks<'a>, usize, usize, usize) { if let Some(out) = self.get_chunks_at_char(char_idx) { out } else { panic!( "Attempt to index past end of RopeSlice: char index {}, RopeSlice char length {}", char_idx, self.len_chars() ); } } /// Creates an iterator over the chunks of the `RopeSlice`, with the /// iterator starting at the chunk containing `line_break_idx`. /// /// Also returns the byte and char indices of the beginning of the first /// chunk to be yielded, and the index of the line that chunk starts on. /// /// Note: for convenience, both the beginning and end of the `RopeSlice` are /// considered line breaks for the purposes of indexing. For example, in /// the string `"Hello \n world!"` 0 would create an iterator starting on /// the first chunk, 1 would create an iterator starting on the chunk /// containing the newline character, and 2 would create an iterator at /// the end of the `RopeSlice` (yielding `None` on a call to `next()`). /// /// The return value is organized as /// `(iterator, chunk_byte_idx, chunk_char_idx, chunk_line_idx)`. /// /// Runs in O(log N) time. /// /// # Panics /// /// Panics if `line_break_idx` is out of bounds (i.e. `line_break_idx > len_lines()`). #[inline] pub fn chunks_at_line_break(&self, line_break_idx: usize) -> (Chunks<'a>, usize, usize, usize) { if let Some(out) = self.get_chunks_at_line_break(line_break_idx) { out } else { panic!( "Attempt to index past end of RopeSlice: line break index {}, RopeSlice line break max index {}", line_break_idx, self.len_lines() ); } } } /// # Non-Panicking /// /// The methods in this impl block provide non-panicking versions of /// `RopeSlice`'s panicking methods. They return either `Option::None` or /// `Result::Err()` when their panicking counterparts would have panicked. impl<'a> RopeSlice<'a> { /// Non-panicking version of [`byte_to_char()`](RopeSlice::byte_to_char). #[inline] pub fn try_byte_to_char(&self, byte_idx: usize) -> Result { // Bounds check if byte_idx <= self.len_bytes() { let (chunk, b, c, _) = self.chunk_at_byte(byte_idx); Ok(c + byte_to_char_idx(chunk, byte_idx - b)) } else { Err(Error::ByteIndexOutOfBounds(byte_idx, self.len_bytes())) } } /// Non-panicking version of [`byte_to_line()`](RopeSlice::byte_to_line). #[inline] pub fn try_byte_to_line(&self, byte_idx: usize) -> Result { // Bounds check if byte_idx <= self.len_bytes() { let (chunk, b, _, l) = self.chunk_at_byte(byte_idx); Ok(l + byte_to_line_idx(chunk, byte_idx - b)) } else { Err(Error::ByteIndexOutOfBounds(byte_idx, self.len_bytes())) } } /// Non-panicking version of [`char_to_byte()`](RopeSlice::char_to_byte). #[inline] pub fn try_char_to_byte(&self, char_idx: usize) -> Result { // Bounds check if char_idx <= self.len_chars() { let (chunk, b, c, _) = self.chunk_at_char(char_idx); Ok(b + char_to_byte_idx(chunk, char_idx - c)) } else { Err(Error::CharIndexOutOfBounds(char_idx, self.len_chars())) } } /// Non-panicking version of [`char_to_line()`](RopeSlice::char_to_line). #[inline] pub fn try_char_to_line(&self, char_idx: usize) -> Result { // Bounds check if char_idx <= self.len_chars() { let (chunk, _, c, l) = self.chunk_at_char(char_idx); Ok(l + char_to_line_idx(chunk, char_idx - c)) } else { Err(Error::CharIndexOutOfBounds(char_idx, self.len_chars())) } } /// Non-panicking version of [`char_to_utf16_cu()`](RopeSlice::char_to_utf16_cu). #[inline] pub fn try_char_to_utf16_cu(&self, char_idx: usize) -> Result { // Bounds check if char_idx <= self.len_chars() { match *self { RopeSlice(RSEnum::Full { node, start_info, .. }) => { let char_idx = char_idx + start_info.chars as usize; let (chunk, chunk_start_info) = node.get_chunk_at_char(char_idx); let chunk_byte_idx = char_to_byte_idx(chunk, char_idx - chunk_start_info.chars as usize); let surrogate_count = byte_to_utf16_surrogate_idx(chunk, chunk_byte_idx); Ok( char_idx + chunk_start_info.utf16_surrogates as usize + surrogate_count - start_info.chars as usize - start_info.utf16_surrogates as usize, ) } RopeSlice(RSEnum::Light { text, .. }) => { let byte_idx = char_to_byte_idx(text, char_idx); let surrogate_count = byte_to_utf16_surrogate_idx(text, byte_idx); Ok(char_idx + surrogate_count) } } } else { Err(Error::CharIndexOutOfBounds(char_idx, self.len_chars())) } } /// Non-panicking version of [`utf16_cu_to_char()`](RopeSlice::utf16_cu_to_char). #[inline] pub fn try_utf16_cu_to_char(&self, utf16_cu_idx: usize) -> Result { // Bounds check if utf16_cu_idx <= self.len_utf16_cu() { match *self { RopeSlice(RSEnum::Full { node, start_info, .. }) => { let utf16_cu_idx = utf16_cu_idx + (start_info.chars + start_info.utf16_surrogates) as usize; let (chunk, chunk_start_info) = node.get_chunk_at_utf16_code_unit(utf16_cu_idx); let chunk_utf16_cu_idx = utf16_cu_idx - (chunk_start_info.chars + chunk_start_info.utf16_surrogates) as usize; let chunk_char_idx = utf16_code_unit_to_char_idx(chunk, chunk_utf16_cu_idx); Ok( chunk_start_info.chars as usize + chunk_char_idx - start_info.chars as usize, ) } RopeSlice(RSEnum::Light { text, .. }) => { Ok(utf16_code_unit_to_char_idx(text, utf16_cu_idx)) } } } else { Err(Error::Utf16IndexOutOfBounds( utf16_cu_idx, self.len_utf16_cu(), )) } } /// Non-panicking version of [`line_to_byte()`](RopeSlice::line_to_byte). #[inline] pub fn try_line_to_byte(&self, line_idx: usize) -> Result { // Bounds check if line_idx <= self.len_lines() { if line_idx == self.len_lines() { Ok(self.len_bytes()) } else { let (chunk, b, _, l) = self.chunk_at_line_break(line_idx); Ok(b + line_to_byte_idx(chunk, line_idx - l)) } } else { Err(Error::LineIndexOutOfBounds(line_idx, self.len_lines())) } } /// Non-panicking version of [`line_to_char()`](RopeSlice::line_to_char). #[inline] pub fn try_line_to_char(&self, line_idx: usize) -> Result { // Bounds check if line_idx <= self.len_lines() { if line_idx == self.len_lines() { Ok(self.len_chars()) } else { let (chunk, _, c, l) = self.chunk_at_line_break(line_idx); Ok(c + line_to_char_idx(chunk, line_idx - l)) } } else { Err(Error::LineIndexOutOfBounds(line_idx, self.len_lines())) } } /// Non-panicking version of [`get_byte()`](RopeSlice::get_byte). #[inline] pub fn get_byte(&self, byte_idx: usize) -> Option { // Bounds check if byte_idx < self.len_bytes() { let (chunk, chunk_byte_idx, _, _) = self.chunk_at_byte(byte_idx); let chunk_rel_byte_idx = byte_idx - chunk_byte_idx; Some(chunk.as_bytes()[chunk_rel_byte_idx]) } else { None } } /// Non-panicking version of [`char()`](RopeSlice::char). #[inline] pub fn get_char(&self, char_idx: usize) -> Option { // Bounds check if char_idx < self.len_chars() { let (chunk, _, chunk_char_idx, _) = self.chunk_at_char(char_idx); let byte_idx = char_to_byte_idx(chunk, char_idx - chunk_char_idx); Some(chunk[byte_idx..].chars().next().unwrap()) } else { None } } /// Non-panicking version of [`line()`](RopeSlice::line). #[inline] pub fn get_line(&self, line_idx: usize) -> Option> { let len_lines = self.len_lines(); // Bounds check if line_idx < len_lines { let (chunk_1, _, c1, l1) = self.chunk_at_line_break(line_idx); let (chunk_2, _, c2, l2) = self.chunk_at_line_break(line_idx + 1); if c1 == c2 { let text1 = &chunk_1[line_to_byte_idx(chunk_1, line_idx - l1)..]; let text2 = &text1[..line_to_byte_idx(text1, 1)]; Some(RopeSlice(RSEnum::Light { text: text2, char_count: count_chars(text2) as Count, utf16_surrogate_count: count_utf16_surrogates(text2) as Count, line_break_count: if line_idx == (len_lines - 1) { 0 } else { 1 }, })) } else { let start = c1 + line_to_char_idx(chunk_1, line_idx - l1); let end = c2 + line_to_char_idx(chunk_2, line_idx + 1 - l2); Some(self.slice(start..end)) } } else { None } } /// Non-panicking version of [`chunk_at_byte()`](RopeSlice::chunk_at_byte). pub fn try_chunk_at_byte(&self, byte_idx: usize) -> Result<(&'a str, usize, usize, usize)> { // Bounds check if byte_idx <= self.len_bytes() { match *self { RopeSlice(RSEnum::Full { node, start_info, end_info, }) => { // Get the chunk. let (chunk, chunk_start_info) = node.get_chunk_at_byte(byte_idx + start_info.bytes as usize); // Calculate clipped start/end byte indices within the chunk. let chunk_start_byte_idx = start_info.bytes.saturating_sub(chunk_start_info.bytes); let chunk_end_byte_idx = (chunk.len() as Count).min(end_info.bytes - chunk_start_info.bytes); // Return the clipped chunk and byte offset. Ok(( &chunk[chunk_start_byte_idx as usize..chunk_end_byte_idx as usize], chunk_start_info.bytes.saturating_sub(start_info.bytes) as usize, chunk_start_info.chars.saturating_sub(start_info.chars) as usize, chunk_start_info .line_breaks .saturating_sub(start_info.line_breaks) as usize, )) } RopeSlice(RSEnum::Light { text, .. }) => Ok((text, 0, 0, 0)), } } else { Err(Error::ByteIndexOutOfBounds(byte_idx, self.len_bytes())) } } /// Non-panicking version of [`chunk_at_char()`](RopeSlice::chunk_at_char). pub fn get_chunk_at_char(&self, char_idx: usize) -> Option<(&'a str, usize, usize, usize)> { // Bounds check if char_idx <= self.len_chars() { match *self { RopeSlice(RSEnum::Full { node, start_info, end_info, }) => { // Get the chunk. let (chunk, chunk_start_info) = node.get_chunk_at_char(char_idx + start_info.chars as usize); // Calculate clipped start/end byte indices within the chunk. let chunk_start_byte_idx = start_info.bytes.saturating_sub(chunk_start_info.bytes); let chunk_end_byte_idx = (chunk.len() as Count).min(end_info.bytes - chunk_start_info.bytes); // Return the clipped chunk and byte offset. Some(( &chunk[chunk_start_byte_idx as usize..chunk_end_byte_idx as usize], chunk_start_info.bytes.saturating_sub(start_info.bytes) as usize, chunk_start_info.chars.saturating_sub(start_info.chars) as usize, chunk_start_info .line_breaks .saturating_sub(start_info.line_breaks) as usize, )) } RopeSlice(RSEnum::Light { text, .. }) => Some((text, 0, 0, 0)), } } else { None } } /// Non-panicking version of [`chunk_at_line_break()`](RopeSlice::chunk_at_line_break). pub fn get_chunk_at_line_break( &self, line_break_idx: usize, ) -> Option<(&'a str, usize, usize, usize)> { // Bounds check if line_break_idx <= self.len_lines() { match *self { RopeSlice(RSEnum::Full { node, start_info, end_info, }) => { // Get the chunk. let (chunk, chunk_start_info) = if line_break_idx == 0 { node.get_chunk_at_byte(start_info.bytes as usize) } else if line_break_idx == self.len_lines() { node.get_chunk_at_byte(end_info.bytes as usize) } else { node.get_chunk_at_line_break( line_break_idx + start_info.line_breaks as usize, ) }; // Calculate clipped start/end byte indices within the chunk. let chunk_start_byte_idx = start_info.bytes.saturating_sub(chunk_start_info.bytes); let chunk_end_byte_idx = (chunk.len() as Count).min(end_info.bytes - chunk_start_info.bytes); // Return the clipped chunk and byte offset. Some(( &chunk[chunk_start_byte_idx as usize..chunk_end_byte_idx as usize], chunk_start_info.bytes.saturating_sub(start_info.bytes) as usize, chunk_start_info.chars.saturating_sub(start_info.chars) as usize, chunk_start_info .line_breaks .saturating_sub(start_info.line_breaks) as usize, )) } RopeSlice(RSEnum::Light { text, .. }) => Some((text, 0, 0, 0)), } } else { None } } /// Non-panicking version of [`slice()`](RopeSlice::slice). pub fn get_slice(&self, char_range: R) -> Option> where R: RangeBounds, { let (start, end) = { let start_range = start_bound_to_num(char_range.start_bound()); let end_range = end_bound_to_num(char_range.end_bound()); // Early-out shortcut for taking a slice of the full thing. if start_range == None && end_range == None { return Some(*self); } ( start_range.unwrap_or(0), end_range.unwrap_or_else(|| self.len_chars()), ) }; // Bounds check if start <= end && end <= self.len_chars() { match *self { RopeSlice(RSEnum::Full { node, start_info, .. }) => Some(RopeSlice::new_with_range( node, start_info.chars as usize + start, start_info.chars as usize + end, )), RopeSlice(RSEnum::Light { text, .. }) => { let start_byte = char_to_byte_idx(text, start); let end_byte = char_to_byte_idx(text, end); let new_text = &text[start_byte..end_byte]; Some(RopeSlice(RSEnum::Light { text: new_text, char_count: (end - start) as Count, utf16_surrogate_count: count_utf16_surrogates(new_text) as Count, line_break_count: count_line_breaks(new_text) as Count, })) } } } else { None } } /// Non-panicking version of [`byte_slice()`](RopeSlice::byte_slice). pub fn get_byte_slice(&self, byte_range: R) -> Option> where R: RangeBounds, { self.get_byte_slice_impl(byte_range).ok() } pub(crate) fn get_byte_slice_impl(&self, byte_range: R) -> Result> where R: RangeBounds, { let start_range = start_bound_to_num(byte_range.start_bound()); let end_range = end_bound_to_num(byte_range.end_bound()); // Bounds checks. match (start_range, end_range) { (None, None) => { // Early-out shortcut for taking a slice of the full thing. return Ok(*self); } (Some(s), Some(e)) => { if s > e { return Err(Error::ByteRangeInvalid(s, e)); } else if e > self.len_bytes() { return Err(Error::ByteRangeOutOfBounds( start_range, end_range, self.len_bytes(), )); } } (Some(s), None) => { if s > self.len_bytes() { return Err(Error::ByteRangeOutOfBounds( start_range, end_range, self.len_bytes(), )); } } (None, Some(e)) => { if e > self.len_bytes() { return Err(Error::ByteRangeOutOfBounds( start_range, end_range, self.len_bytes(), )); } } } let (start, end) = ( start_range.unwrap_or(0), end_range.unwrap_or_else(|| self.len_bytes()), ); match *self { RopeSlice(RSEnum::Full { node, start_info, .. }) => RopeSlice::new_with_byte_range( node, start_info.bytes as usize + start, start_info.bytes as usize + end, ) .map_err(|e| { if let Error::ByteRangeNotCharBoundary(_, _) = e { Error::ByteRangeNotCharBoundary(start_range, end_range) } else { e } }), RopeSlice(RSEnum::Light { text, .. }) => { if !text.is_char_boundary(start) || !text.is_char_boundary(end) { return Err(Error::ByteRangeNotCharBoundary(start_range, end_range)); } let new_text = &text[start..end]; Ok(RopeSlice(RSEnum::Light { text: new_text, char_count: count_chars(new_text) as Count, utf16_surrogate_count: count_utf16_surrogates(new_text) as Count, line_break_count: count_line_breaks(new_text) as Count, })) } } } /// Non-panicking version of [`bytes_at()`](RopeSlice::bytes_at). #[inline] pub fn get_bytes_at(&self, byte_idx: usize) -> Option> { // Bounds check if byte_idx <= self.len_bytes() { match *self { RopeSlice(RSEnum::Full { node, start_info, end_info, }) => Some(Bytes::new_with_range_at( node, start_info.bytes as usize + byte_idx, (start_info.bytes as usize, end_info.bytes as usize), (start_info.chars as usize, end_info.chars as usize), ( start_info.line_breaks as usize, end_info.line_breaks as usize + 1, ), )), RopeSlice(RSEnum::Light { text, .. }) => Some(Bytes::from_str_at(text, byte_idx)), } } else { None } } /// Non-panicking version of [`chars_at()`](RopeSlice::chars_at). #[inline] pub fn get_chars_at(&self, char_idx: usize) -> Option> { // Bounds check if char_idx <= self.len_chars() { match *self { RopeSlice(RSEnum::Full { node, start_info, end_info, }) => Some(Chars::new_with_range_at( node, start_info.chars as usize + char_idx, (start_info.bytes as usize, end_info.bytes as usize), (start_info.chars as usize, end_info.chars as usize), ( start_info.line_breaks as usize, end_info.line_breaks as usize + 1, ), )), RopeSlice(RSEnum::Light { text, .. }) => Some(Chars::from_str_at(text, char_idx)), } } else { None } } /// Non-panicking version of [`lines_at()`](RopeSlice::lines_at). #[inline] pub fn get_lines_at(&self, line_idx: usize) -> Option> { // Bounds check if line_idx <= self.len_lines() { match *self { RopeSlice(RSEnum::Full { node, start_info, end_info, }) => Some(Lines::new_with_range_at( node, start_info.line_breaks as usize + line_idx, (start_info.bytes as usize, end_info.bytes as usize), ( start_info.line_breaks as usize, end_info.line_breaks as usize + 1, ), )), RopeSlice(RSEnum::Light { text, line_break_count, .. }) => Some(Lines::from_str_at( text, line_idx, line_break_count as usize + 1, )), } } else { None } } /// Non-panicking version of [`chunks_at_byte()`](RopeSlice::chunks_at_byte). #[inline] pub fn get_chunks_at_byte(&self, byte_idx: usize) -> Option<(Chunks<'a>, usize, usize, usize)> { // Bounds check if byte_idx <= self.len_bytes() { match *self { RopeSlice(RSEnum::Full { node, start_info, end_info, }) => { let (chunks, chunk_byte_idx, chunk_char_idx, chunk_line_idx) = Chunks::new_with_range_at_byte( node, byte_idx + start_info.bytes as usize, (start_info.bytes as usize, end_info.bytes as usize), (start_info.chars as usize, end_info.chars as usize), ( start_info.line_breaks as usize, end_info.line_breaks as usize + 1, ), ); Some(( chunks, chunk_byte_idx.saturating_sub(start_info.bytes as usize), chunk_char_idx.saturating_sub(start_info.chars as usize), chunk_line_idx.saturating_sub(start_info.line_breaks as usize), )) } RopeSlice(RSEnum::Light { text, char_count, line_break_count, .. }) => { let chunks = Chunks::from_str(text, byte_idx == text.len()); if byte_idx == text.len() { Some(( chunks, text.len(), char_count as usize, line_break_count as usize, )) } else { Some((chunks, 0, 0, 0)) } } } } else { None } } /// Non-panicking version of [`chunks_at_char()`](RopeSlice::chunks_at_char). #[inline] pub fn get_chunks_at_char(&self, char_idx: usize) -> Option<(Chunks<'a>, usize, usize, usize)> { // Bounds check if char_idx <= self.len_chars() { match *self { RopeSlice(RSEnum::Full { node, start_info, end_info, }) => { let (chunks, chunk_byte_idx, chunk_char_idx, chunk_line_idx) = Chunks::new_with_range_at_char( node, char_idx + start_info.chars as usize, (start_info.bytes as usize, end_info.bytes as usize), (start_info.chars as usize, end_info.chars as usize), ( start_info.line_breaks as usize, end_info.line_breaks as usize + 1, ), ); Some(( chunks, chunk_byte_idx.saturating_sub(start_info.bytes as usize), chunk_char_idx.saturating_sub(start_info.chars as usize), chunk_line_idx.saturating_sub(start_info.line_breaks as usize), )) } RopeSlice(RSEnum::Light { text, char_count, line_break_count, .. }) => { let chunks = Chunks::from_str(text, char_idx == char_count as usize); if char_idx == char_count as usize { Some(( chunks, text.len(), char_count as usize, line_break_count as usize, )) } else { Some((chunks, 0, 0, 0)) } } } } else { None } } /// Non-panicking version of [`chunks_at_line_break()`](RopeSlice::chunks_at_line_break). #[inline] pub fn get_chunks_at_line_break( &self, line_break_idx: usize, ) -> Option<(Chunks<'a>, usize, usize, usize)> { // Bounds check if line_break_idx <= self.len_lines() { match *self { RopeSlice(RSEnum::Full { node, start_info, end_info, }) => { // Get the chunk. let (chunks, chunk_byte_idx, chunk_char_idx, chunk_line_idx) = if line_break_idx == 0 { Chunks::new_with_range_at_byte( node, start_info.bytes as usize, (start_info.bytes as usize, end_info.bytes as usize), (start_info.chars as usize, end_info.chars as usize), ( start_info.line_breaks as usize, end_info.line_breaks as usize + 1, ), ) } else if line_break_idx == self.len_lines() { Chunks::new_with_range_at_byte( node, end_info.bytes as usize, (start_info.bytes as usize, end_info.bytes as usize), (start_info.chars as usize, end_info.chars as usize), ( start_info.line_breaks as usize, end_info.line_breaks as usize + 1, ), ) } else { Chunks::new_with_range_at_line_break( node, line_break_idx + start_info.line_breaks as usize, (start_info.bytes as usize, end_info.bytes as usize), (start_info.chars as usize, end_info.chars as usize), ( start_info.line_breaks as usize, end_info.line_breaks as usize + 1, ), ) }; Some(( chunks, chunk_byte_idx.saturating_sub(start_info.bytes as usize), chunk_char_idx.saturating_sub(start_info.chars as usize), chunk_line_idx.saturating_sub(start_info.line_breaks as usize), )) } RopeSlice(RSEnum::Light { text, char_count, line_break_count, .. }) => { let chunks = Chunks::from_str(text, line_break_idx == line_break_count as usize); if line_break_idx == line_break_count as usize { Some(( chunks, text.len(), char_count as usize, line_break_count as usize, )) } else { Some((chunks, 0, 0, 0)) } } } } else { None } } } //============================================================== // Conversion impls /// Creates a `RopeSlice` directly from a string slice. /// /// The useful applications of this are actually somewhat narrow. It is /// intended primarily as an aid when implementing additional functionality /// on top of Ropey, where you may already have access to a rope chunk and /// want to directly create a `RopeSlice` from it, avoiding the overhead of /// going through the slicing APIs. /// /// Although it is possible to use this to create `RopeSlice`s from /// arbitrary strings, doing so is not especially useful. For example, /// `Rope`s and `RopeSlice`s can already be directly compared for /// equality with strings and string slices. /// /// Runs in O(N) time, where N is the length of the string slice. impl<'a> From<&'a str> for RopeSlice<'a> { #[inline] fn from(text: &'a str) -> Self { RopeSlice(RSEnum::Light { text: text, char_count: count_chars(text) as Count, utf16_surrogate_count: count_utf16_surrogates(text) as Count, line_break_count: count_line_breaks(text) as Count, }) } } impl<'a> From> for String { #[inline] fn from(s: RopeSlice<'a>) -> Self { let mut text = String::with_capacity(s.len_bytes()); text.extend(s.chunks()); text } } /// Attempts to borrow the contents of the slice, but will convert to an /// owned string if the contents is not contiguous in memory. /// /// Runs in best case O(1), worst case O(N). impl<'a> From> for std::borrow::Cow<'a, str> { #[inline] fn from(s: RopeSlice<'a>) -> Self { if let Some(text) = s.as_str() { std::borrow::Cow::Borrowed(text) } else { std::borrow::Cow::Owned(String::from(s)) } } } //============================================================== // Other impls impl<'a> std::fmt::Debug for RopeSlice<'a> { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { f.debug_list().entries(self.chunks()).finish() } } impl<'a> std::fmt::Display for RopeSlice<'a> { #[inline] fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { for chunk in self.chunks() { write!(f, "{}", chunk)? } Ok(()) } } impl<'a> std::cmp::Eq for RopeSlice<'a> {} impl<'a, 'b> std::cmp::PartialEq> for RopeSlice<'a> { fn eq(&self, other: &RopeSlice<'b>) -> bool { if self.len_bytes() != other.len_bytes() { return false; } let mut chunk_itr_1 = self.chunks(); let mut chunk_itr_2 = other.chunks(); let mut chunk1 = chunk_itr_1.next().unwrap_or("").as_bytes(); let mut chunk2 = chunk_itr_2.next().unwrap_or("").as_bytes(); loop { if chunk1.len() > chunk2.len() { if &chunk1[..chunk2.len()] != chunk2 { return false; } else { chunk1 = &chunk1[chunk2.len()..]; chunk2 = &[]; } } else if &chunk2[..chunk1.len()] != chunk1 { return false; } else { chunk2 = &chunk2[chunk1.len()..]; chunk1 = &[]; } if chunk1.is_empty() { if let Some(chunk) = chunk_itr_1.next() { chunk1 = chunk.as_bytes(); } else { break; } } if chunk2.is_empty() { if let Some(chunk) = chunk_itr_2.next() { chunk2 = chunk.as_bytes(); } else { break; } } } return true; } } impl<'a, 'b> std::cmp::PartialEq<&'b str> for RopeSlice<'a> { #[inline] fn eq(&self, other: &&'b str) -> bool { match *self { RopeSlice(RSEnum::Full { .. }) => { if self.len_bytes() != other.len() { return false; } let other = other.as_bytes(); let mut idx = 0; for chunk in self.chunks() { let chunk = chunk.as_bytes(); if chunk != &other[idx..(idx + chunk.len())] { return false; } idx += chunk.len(); } return true; } RopeSlice(RSEnum::Light { text, .. }) => { return text == *other; } } } } impl<'a, 'b> std::cmp::PartialEq> for &'b str { #[inline] fn eq(&self, other: &RopeSlice<'a>) -> bool { other == self } } impl<'a> std::cmp::PartialEq for RopeSlice<'a> { #[inline] fn eq(&self, other: &str) -> bool { std::cmp::PartialEq::<&str>::eq(self, &other) } } impl<'a> std::cmp::PartialEq> for str { #[inline] fn eq(&self, other: &RopeSlice<'a>) -> bool { std::cmp::PartialEq::<&str>::eq(other, &self) } } impl<'a> std::cmp::PartialEq for RopeSlice<'a> { #[inline] fn eq(&self, other: &String) -> bool { self == other.as_str() } } impl<'a> std::cmp::PartialEq> for String { #[inline] fn eq(&self, other: &RopeSlice<'a>) -> bool { self.as_str() == other } } impl<'a, 'b> std::cmp::PartialEq> for RopeSlice<'a> { #[inline] fn eq(&self, other: &std::borrow::Cow<'b, str>) -> bool { *self == **other } } impl<'a, 'b> std::cmp::PartialEq> for std::borrow::Cow<'b, str> { #[inline] fn eq(&self, other: &RopeSlice<'a>) -> bool { **self == *other } } impl<'a> std::cmp::PartialEq for RopeSlice<'a> { #[inline] fn eq(&self, other: &Rope) -> bool { *self == other.slice(..) } } impl<'a> std::cmp::PartialEq> for Rope { #[inline] fn eq(&self, other: &RopeSlice<'a>) -> bool { self.slice(..) == *other } } impl<'a> std::cmp::Ord for RopeSlice<'a> { #[allow(clippy::op_ref)] // Erroneously thinks with can directly use a slice. fn cmp(&self, other: &RopeSlice<'a>) -> std::cmp::Ordering { let mut chunk_itr_1 = self.chunks(); let mut chunk_itr_2 = other.chunks(); let mut chunk1 = chunk_itr_1.next().unwrap_or("").as_bytes(); let mut chunk2 = chunk_itr_2.next().unwrap_or("").as_bytes(); loop { if chunk1.len() >= chunk2.len() { let compared = chunk1[..chunk2.len()].cmp(chunk2); if compared != std::cmp::Ordering::Equal { return compared; } chunk1 = &chunk1[chunk2.len()..]; chunk2 = &[]; } else { let compared = chunk1.cmp(&chunk2[..chunk1.len()]); if compared != std::cmp::Ordering::Equal { return compared; } chunk1 = &[]; chunk2 = &chunk2[chunk1.len()..]; } if chunk1.is_empty() { if let Some(chunk) = chunk_itr_1.next() { chunk1 = chunk.as_bytes(); } else { break; } } if chunk2.is_empty() { if let Some(chunk) = chunk_itr_2.next() { chunk2 = chunk.as_bytes(); } else { break; } } } self.len_bytes().cmp(&other.len_bytes()) } } impl<'a, 'b> std::cmp::PartialOrd> for RopeSlice<'a> { #[inline] fn partial_cmp(&self, other: &RopeSlice<'b>) -> Option { Some(self.cmp(other)) } } impl<'a> std::hash::Hash for RopeSlice<'a> { fn hash(&self, state: &mut H) { // `std::hash::Hasher` only guarantees the same hash output for // exactly the same calls to `Hasher::write()`. Just submitting // the same data in the same order isn't enough--it also has to // be split the same between calls. So we go to some effort here // to ensure that we always submit the text data in the same // fixed-size blocks, even if those blocks don't align with chunk // boundaries at all. // // The naive approach is to always copy to a fixed-size buffer // and submit the buffer whenever it fills up. We conceptually // follow that approach here, but we do a little better by // skipping the buffer and directly passing the data without // copying when possible. const BLOCK_SIZE: usize = 256; let mut buffer = [0u8; BLOCK_SIZE]; let mut buffer_len = 0; for chunk in self.chunks() { let mut data = chunk.as_bytes(); while !data.is_empty() { if buffer_len == 0 && data.len() >= BLOCK_SIZE { // Process data directly, skipping the buffer. let (head, tail) = data.split_at(BLOCK_SIZE); state.write(head); data = tail; } else if buffer_len == BLOCK_SIZE { // Process the filled buffer. state.write(&buffer[..]); buffer_len = 0; } else { // Append to the buffer. let n = (BLOCK_SIZE - buffer_len).min(data.len()); let (head, tail) = data.split_at(n); (&mut buffer[buffer_len..(buffer_len + n)]).copy_from_slice(head); buffer_len += n; data = tail; } } } // Write any remaining unprocessed data in the buffer. if buffer_len > 0 { state.write(&buffer[..buffer_len]); } // Same strategy as `&str` in stdlib, so that e.g. two adjacent // fields in a `#[derive(Hash)]` struct with "Hi " and "there" // vs "Hi t" and "here" give the struct a different hash. state.write_u8(0xff) } } //=========================================================== #[cfg(test)] mod tests { use crate::str_utils::{ byte_to_char_idx, byte_to_line_idx, char_to_byte_idx, char_to_line_idx, }; use crate::Rope; use std::hash::{Hash, Hasher}; // 127 bytes, 103 chars, 1 line const TEXT: &str = "Hello there! How're you doing? It's \ a fine day, isn't it? Aren't you glad \ we're alive? こんにちは、みんなさん!"; // 124 bytes, 100 chars, 4 lines const TEXT_LINES: &str = "Hello there! How're you doing?\nIt's \ a fine day, isn't it?\nAren't you glad \ we're alive?\nこんにちは、みんなさん!"; // 127 bytes, 107 chars, 111 utf16 code units, 1 line const TEXT_EMOJI: &str = "Hello there!🐸 How're you doing?🐸 It's \ a fine day, isn't it?🐸 Aren't you glad \ we're alive?🐸 こんにちは、みんなさん!"; #[test] fn len_bytes_01() { let r = Rope::from_str(TEXT); let s = r.slice(7..98); assert_eq!(s.len_bytes(), 105); } #[test] fn len_bytes_02() { let r = Rope::from_str(TEXT); let s = r.slice(43..43); assert_eq!(s.len_bytes(), 0); } #[test] fn len_chars_01() { let r = Rope::from_str(TEXT); let s = r.slice(7..98); assert_eq!(s.len_chars(), 91); } #[test] fn len_chars_02() { let r = Rope::from_str(TEXT); let s = r.slice(43..43); assert_eq!(s.len_chars(), 0); } #[test] fn len_lines_01() { let r = Rope::from_str(TEXT_LINES); let s = r.slice(34..98); assert_eq!(s.len_lines(), 3); } #[test] fn len_lines_02() { let r = Rope::from_str(TEXT_LINES); let s = r.slice(43..43); assert_eq!(s.len_lines(), 1); } #[test] fn len_lines_03() { // Make sure splitting CRLF pairs at the end works properly. let r = Rope::from_str("\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"); for i in 0..r.len_chars() { if cfg!(any(feature = "cr_lines", feature = "unicode_lines")) { assert_eq!(r.slice(..i).len_lines(), 1 + ((i + 1) / 2)); } else { assert_eq!(r.slice(..i).len_lines(), 1 + (i / 2)); } } } #[test] fn len_lines_04() { // Make sure splitting CRLF pairs at the start works properly. let r = Rope::from_str("\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"); for i in 0..r.len_chars() { assert_eq!(r.slice(i..).len_lines(), 16 - (i / 2)); } } #[test] fn len_utf16_cu_01() { let r = Rope::from_str(TEXT); let s = r.slice(..); assert_eq!(s.len_utf16_cu(), 103); } #[test] fn len_utf16_cu_02() { let r = Rope::from_str(TEXT_EMOJI); let s = r.slice(..); assert_eq!(s.len_utf16_cu(), 111); } #[test] fn len_utf16_cu_03() { let r = Rope::from_str(TEXT_EMOJI); let s = r.slice(13..33); assert_eq!(s.len_utf16_cu(), 21); } #[test] fn len_utf16_cu_04() { let r = Rope::from_str("🐸"); let s = r.slice(..); assert_eq!(s.len_utf16_cu(), 2); } #[test] fn len_utf16_cu_05() { let r = Rope::from_str(""); let s = r.slice(..); assert_eq!(s.len_utf16_cu(), 0); } #[test] fn byte_to_char_01() { let r = Rope::from_str(TEXT); let s = r.slice(88..102); // ? こんにちは、みんなさん assert_eq!(0, s.byte_to_char(0)); assert_eq!(1, s.byte_to_char(1)); assert_eq!(2, s.byte_to_char(2)); assert_eq!(3, s.byte_to_char(3)); assert_eq!(3, s.byte_to_char(4)); assert_eq!(3, s.byte_to_char(5)); assert_eq!(4, s.byte_to_char(6)); assert_eq!(4, s.byte_to_char(7)); assert_eq!(4, s.byte_to_char(8)); assert_eq!(13, s.byte_to_char(33)); assert_eq!(13, s.byte_to_char(34)); assert_eq!(13, s.byte_to_char(35)); assert_eq!(14, s.byte_to_char(36)); } #[test] fn byte_to_line_01() { let r = Rope::from_str(TEXT_LINES); let s = r.slice(34..96); // 's a fine day, isn't it?\nAren't you glad \ // we're alive?\nこんにちは、みん assert_eq!(0, s.byte_to_line(0)); assert_eq!(0, s.byte_to_line(1)); assert_eq!(0, s.byte_to_line(24)); assert_eq!(1, s.byte_to_line(25)); assert_eq!(1, s.byte_to_line(26)); assert_eq!(1, s.byte_to_line(53)); assert_eq!(2, s.byte_to_line(54)); assert_eq!(2, s.byte_to_line(57)); assert_eq!(2, s.byte_to_line(78)); } #[test] fn byte_to_line_02() { let r = Rope::from_str(TEXT_LINES); let s = r.slice(50..50); assert_eq!(0, s.byte_to_line(0)); } #[test] fn byte_to_line_03() { let r = Rope::from_str("Hi there\nstranger!"); let s = r.slice(0..9); assert_eq!(0, s.byte_to_line(0)); assert_eq!(0, s.byte_to_line(8)); assert_eq!(1, s.byte_to_line(9)); } #[test] #[should_panic] fn byte_to_line_04() { let r = Rope::from_str(TEXT_LINES); let s = r.slice(34..96); s.byte_to_line(79); } #[test] fn byte_to_line_05() { // Make sure splitting CRLF pairs at the end works properly. let r = Rope::from_str("\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"); for i in 0..r.len_bytes() { if cfg!(any(feature = "cr_lines", feature = "unicode_lines")) { assert_eq!(r.byte_slice(..i).byte_to_line(i), (i + 1) / 2); } else { assert_eq!(r.byte_slice(..i).byte_to_line(i), i / 2); } } } #[test] fn char_to_byte_01() { let r = Rope::from_str(TEXT); let s = r.slice(88..102); // ? こんにちは、みんなさん assert_eq!(0, s.char_to_byte(0)); assert_eq!(1, s.char_to_byte(1)); assert_eq!(2, s.char_to_byte(2)); assert_eq!(3, s.char_to_byte(3)); assert_eq!(6, s.char_to_byte(4)); assert_eq!(33, s.char_to_byte(13)); assert_eq!(36, s.char_to_byte(14)); } #[test] fn char_to_line_01() { let r = Rope::from_str(TEXT_LINES); let s = r.slice(34..96); // 's a fine day, isn't it?\nAren't you glad \ // we're alive?\nこんにちは、みん assert_eq!(0, s.char_to_line(0)); assert_eq!(0, s.char_to_line(1)); assert_eq!(0, s.char_to_line(24)); assert_eq!(1, s.char_to_line(25)); assert_eq!(1, s.char_to_line(26)); assert_eq!(1, s.char_to_line(53)); assert_eq!(2, s.char_to_line(54)); assert_eq!(2, s.char_to_line(55)); assert_eq!(2, s.char_to_line(62)); } #[test] fn char_to_line_02() { let r = Rope::from_str(TEXT_LINES); let s = r.slice(43..43); assert_eq!(0, s.char_to_line(0)); } #[test] fn char_to_line_03() { let r = Rope::from_str("Hi there\nstranger!"); let s = r.slice(0..9); assert_eq!(0, s.char_to_line(0)); assert_eq!(0, s.char_to_line(8)); assert_eq!(1, s.char_to_line(9)); } #[test] #[should_panic] fn char_to_line_04() { let r = Rope::from_str(TEXT_LINES); let s = r.slice(34..96); s.char_to_line(63); } #[test] fn char_to_line_05() { // Make sure splitting CRLF pairs at the end works properly. let r = Rope::from_str("\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"); for i in 0..r.len_chars() { if cfg!(any(feature = "cr_lines", feature = "unicode_lines")) { assert_eq!(r.slice(..i).char_to_line(i), (i + 1) / 2); } else { assert_eq!(r.slice(..i).char_to_line(i), i / 2); } } } #[test] fn line_to_byte_01() { let r = Rope::from_str(TEXT_LINES); let s = r.slice(34..96); // 's a fine day, isn't it?\nAren't you glad \ // we're alive?\nこんにちは、みん assert_eq!(0, s.line_to_byte(0)); assert_eq!(25, s.line_to_byte(1)); assert_eq!(54, s.line_to_byte(2)); assert_eq!(78, s.line_to_byte(3)); } #[test] fn line_to_byte_02() { let r = Rope::from_str(TEXT_LINES); let s = r.slice(43..43); assert_eq!(0, s.line_to_byte(0)); assert_eq!(0, s.line_to_byte(1)); } #[test] #[should_panic] fn line_to_byte_03() { let r = Rope::from_str(TEXT_LINES); let s = r.slice(34..96); s.line_to_byte(4); } #[test] fn line_to_byte_04() { // Make sure splitting CRLF pairs at the end works properly. let r = Rope::from_str("\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"); for i in 0..r.len_bytes() { assert_eq!(r.byte_slice(..i).line_to_byte((i + 1) / 2), i); } } #[test] fn line_to_char_01() { let r = Rope::from_str(TEXT_LINES); let s = r.slice(34..96); assert_eq!(0, s.line_to_char(0)); assert_eq!(25, s.line_to_char(1)); assert_eq!(54, s.line_to_char(2)); assert_eq!(62, s.line_to_char(3)); } #[test] fn line_to_char_02() { let r = Rope::from_str(TEXT_LINES); let s = r.slice(43..43); assert_eq!(0, s.line_to_char(0)); assert_eq!(0, s.line_to_char(1)); } #[test] #[should_panic] fn line_to_char_03() { let r = Rope::from_str(TEXT_LINES); let s = r.slice(34..96); s.line_to_char(4); } #[test] fn line_to_char_04() { // Make sure splitting CRLF pairs at the end works properly. let r = Rope::from_str("\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"); for i in 0..r.len_chars() { assert_eq!(r.slice(..i).line_to_char((i + 1) / 2), i); } } #[test] fn char_to_utf16_cu_01() { let r = Rope::from_str(""); let s = r.slice(..); assert_eq!(0, s.char_to_utf16_cu(0)); } #[test] #[should_panic] fn char_to_utf16_cu_02() { let r = Rope::from_str(""); let s = r.slice(..); s.char_to_utf16_cu(1); } #[test] fn char_to_utf16_cu_03() { let r = Rope::from_str("🐸"); let s = r.slice(..); assert_eq!(0, s.char_to_utf16_cu(0)); assert_eq!(2, s.char_to_utf16_cu(1)); } #[test] #[should_panic] fn char_to_utf16_cu_04() { let r = Rope::from_str("🐸"); let s = r.slice(..); s.char_to_utf16_cu(2); } #[test] fn char_to_utf16_cu_05() { let r = Rope::from_str(TEXT_EMOJI); let s = r.slice(..); assert_eq!(0, s.char_to_utf16_cu(0)); assert_eq!(12, s.char_to_utf16_cu(12)); assert_eq!(14, s.char_to_utf16_cu(13)); assert_eq!(33, s.char_to_utf16_cu(32)); assert_eq!(35, s.char_to_utf16_cu(33)); assert_eq!(63, s.char_to_utf16_cu(61)); assert_eq!(65, s.char_to_utf16_cu(62)); assert_eq!(95, s.char_to_utf16_cu(92)); assert_eq!(97, s.char_to_utf16_cu(93)); assert_eq!(111, s.char_to_utf16_cu(107)); } #[test] #[should_panic] fn char_to_utf16_cu_06() { let r = Rope::from_str(TEXT_EMOJI); let s = r.slice(..); s.char_to_utf16_cu(108); } #[test] fn char_to_utf16_cu_07() { let r = Rope::from_str(TEXT_EMOJI); let s = r.slice(1..106); assert_eq!(0, s.char_to_utf16_cu(0)); assert_eq!(11, s.char_to_utf16_cu(11)); assert_eq!(13, s.char_to_utf16_cu(12)); assert_eq!(32, s.char_to_utf16_cu(31)); assert_eq!(34, s.char_to_utf16_cu(32)); assert_eq!(62, s.char_to_utf16_cu(60)); assert_eq!(64, s.char_to_utf16_cu(61)); assert_eq!(94, s.char_to_utf16_cu(91)); assert_eq!(96, s.char_to_utf16_cu(92)); assert_eq!(109, s.char_to_utf16_cu(105)); } #[test] #[should_panic] fn char_to_utf16_cu_08() { let r = Rope::from_str(TEXT_EMOJI); let s = r.slice(1..106); s.char_to_utf16_cu(106); } #[test] fn utf16_cu_to_char_01() { let r = Rope::from_str(""); let s = r.slice(..); assert_eq!(0, s.utf16_cu_to_char(0)); } #[test] #[should_panic] fn utf16_cu_to_char_02() { let r = Rope::from_str(""); let s = r.slice(..); s.utf16_cu_to_char(1); } #[test] fn utf16_cu_to_char_03() { let r = Rope::from_str("🐸"); let s = r.slice(..); assert_eq!(0, s.utf16_cu_to_char(0)); assert_eq!(0, s.utf16_cu_to_char(1)); assert_eq!(1, s.utf16_cu_to_char(2)); } #[test] #[should_panic] fn utf16_cu_to_char_04() { let r = Rope::from_str("🐸"); let s = r.slice(..); s.utf16_cu_to_char(3); } #[test] fn utf16_cu_to_char_05() { let r = Rope::from_str(TEXT_EMOJI); let s = r.slice(..); assert_eq!(0, s.utf16_cu_to_char(0)); assert_eq!(12, s.utf16_cu_to_char(12)); assert_eq!(12, s.utf16_cu_to_char(13)); assert_eq!(13, s.utf16_cu_to_char(14)); assert_eq!(32, s.utf16_cu_to_char(33)); assert_eq!(32, s.utf16_cu_to_char(34)); assert_eq!(33, s.utf16_cu_to_char(35)); assert_eq!(61, s.utf16_cu_to_char(63)); assert_eq!(61, s.utf16_cu_to_char(64)); assert_eq!(62, s.utf16_cu_to_char(65)); assert_eq!(92, s.utf16_cu_to_char(95)); assert_eq!(92, s.utf16_cu_to_char(96)); assert_eq!(93, s.utf16_cu_to_char(97)); assert_eq!(107, s.utf16_cu_to_char(111)); } #[test] #[should_panic] fn utf16_cu_to_char_06() { let r = Rope::from_str(TEXT_EMOJI); let s = r.slice(..); s.utf16_cu_to_char(112); } #[test] fn utf16_cu_to_char_07() { let r = Rope::from_str(TEXT_EMOJI); let s = r.slice(1..106); assert_eq!(0, s.utf16_cu_to_char(0)); assert_eq!(11, s.utf16_cu_to_char(11)); assert_eq!(11, s.utf16_cu_to_char(12)); assert_eq!(12, s.utf16_cu_to_char(13)); assert_eq!(31, s.utf16_cu_to_char(32)); assert_eq!(31, s.utf16_cu_to_char(33)); assert_eq!(32, s.utf16_cu_to_char(34)); assert_eq!(60, s.utf16_cu_to_char(62)); assert_eq!(60, s.utf16_cu_to_char(63)); assert_eq!(61, s.utf16_cu_to_char(64)); assert_eq!(91, s.utf16_cu_to_char(94)); assert_eq!(91, s.utf16_cu_to_char(95)); assert_eq!(92, s.utf16_cu_to_char(96)); assert_eq!(105, s.utf16_cu_to_char(109)); } #[test] #[should_panic] fn utf16_cu_to_char_08() { let r = Rope::from_str(TEXT_EMOJI); let s = r.slice(1..106); s.utf16_cu_to_char(110); } #[test] fn byte_01() { let r = Rope::from_str(TEXT); let s = r.slice(34..100); assert_eq!(s.byte(0), b't'); assert_eq!(s.byte(10), b' '); // UTF-8 encoding of 'な'. assert_eq!(s.byte(s.len_bytes() - 3), 0xE3); assert_eq!(s.byte(s.len_bytes() - 2), 0x81); assert_eq!(s.byte(s.len_bytes() - 1), 0xAA); } #[test] #[should_panic] fn byte_02() { let r = Rope::from_str(TEXT); let s = r.slice(34..100); s.byte(s.len_bytes()); } #[test] #[should_panic] fn byte_03() { let r = Rope::from_str(TEXT); let s = r.slice(42..42); s.byte(0); } #[test] fn char_01() { let r = Rope::from_str(TEXT); let s = r.slice(34..100); // t's \ // a fine day, isn't it? Aren't you glad \ // we're alive? こんにちは、みんな assert_eq!(s.char(0), 't'); assert_eq!(s.char(10), ' '); assert_eq!(s.char(18), 'n'); assert_eq!(s.char(65), 'な'); } #[test] #[should_panic] fn char_02() { let r = Rope::from_str(TEXT); let s = r.slice(34..100); s.char(66); } #[test] #[should_panic] fn char_03() { let r = Rope::from_str(TEXT); let s = r.slice(43..43); s.char(0); } #[test] fn line_01() { let r = Rope::from_str(TEXT_LINES); let s = r.slice(34..96); // "'s a fine day, isn't it?\nAren't you glad \ // we're alive?\nこんにちは、みん" let l0 = s.line(0); assert_eq!(l0, "'s a fine day, isn't it?\n"); assert_eq!(l0.len_bytes(), 25); assert_eq!(l0.len_chars(), 25); assert_eq!(l0.len_lines(), 2); let l1 = s.line(1); assert_eq!(l1, "Aren't you glad we're alive?\n"); assert_eq!(l1.len_bytes(), 29); assert_eq!(l1.len_chars(), 29); assert_eq!(l1.len_lines(), 2); let l2 = s.line(2); assert_eq!(l2, "こんにちは、みん"); assert_eq!(l2.len_bytes(), 24); assert_eq!(l2.len_chars(), 8); assert_eq!(l2.len_lines(), 1); } #[test] fn line_02() { let r = Rope::from_str(TEXT_LINES); let s = r.slice(34..59); // "'s a fine day, isn't it?\n" assert_eq!(s.line(0), "'s a fine day, isn't it?\n"); assert_eq!(s.line(1), ""); } #[test] fn line_03() { let r = Rope::from_str("Hi\nHi\nHi\nHi\nHi\nHi\n"); let s = r.slice(1..17); assert_eq!(s.line(0), "i\n"); assert_eq!(s.line(1), "Hi\n"); assert_eq!(s.line(2), "Hi\n"); assert_eq!(s.line(3), "Hi\n"); assert_eq!(s.line(4), "Hi\n"); assert_eq!(s.line(5), "Hi"); } #[test] fn line_04() { let r = Rope::from_str(TEXT_LINES); let s = r.slice(43..43); assert_eq!(s.line(0), ""); } #[test] #[should_panic] fn line_05() { let r = Rope::from_str(TEXT_LINES); let s = r.slice(34..96); s.line(3); } #[test] fn line_06() { let r = Rope::from_str("1\n2\n3\n4\n5\n6\n7\n8"); let s = r.slice(1..11); // "\n2\n3\n4\n5\n6" assert_eq!(s.line(0).len_lines(), 2); assert_eq!(s.line(1).len_lines(), 2); assert_eq!(s.line(2).len_lines(), 2); assert_eq!(s.line(3).len_lines(), 2); assert_eq!(s.line(4).len_lines(), 2); assert_eq!(s.line(5).len_lines(), 1); } #[test] fn chunk_at_byte() { let r = Rope::from_str(TEXT_LINES); let s = r.slice(34..96); let text = &TEXT_LINES[34..112]; // "'s a fine day, isn't it?\nAren't you glad \ // we're alive?\nこんにちは、みん" let mut t = text; let mut prev_chunk = ""; for i in 0..s.len_bytes() { let (chunk, b, c, l) = s.chunk_at_byte(i); assert_eq!(c, byte_to_char_idx(text, b)); assert_eq!(l, byte_to_line_idx(text, b)); if chunk != prev_chunk { assert_eq!(chunk, &t[..chunk.len()]); t = &t[chunk.len()..]; prev_chunk = chunk; } let c1 = { let i2 = byte_to_char_idx(text, i); text.chars().nth(i2).unwrap() }; let c2 = { let i2 = i - b; let i3 = byte_to_char_idx(chunk, i2); chunk.chars().nth(i3).unwrap() }; assert_eq!(c1, c2); } assert_eq!(t.len(), 0); } #[test] fn chunk_at_char() { let r = Rope::from_str(TEXT_LINES); let s = r.slice(34..96); let text = &TEXT_LINES[34..112]; // "'s a fine day, isn't it?\nAren't you glad \ // we're alive?\nこんにちは、みん" let mut t = text; let mut prev_chunk = ""; for i in 0..s.len_chars() { let (chunk, b, c, l) = s.chunk_at_char(i); assert_eq!(b, char_to_byte_idx(text, c)); assert_eq!(l, char_to_line_idx(text, c)); if chunk != prev_chunk { assert_eq!(chunk, &t[..chunk.len()]); t = &t[chunk.len()..]; prev_chunk = chunk; } let c1 = text.chars().nth(i).unwrap(); let c2 = { let i2 = i - c; chunk.chars().nth(i2).unwrap() }; assert_eq!(c1, c2); } assert_eq!(t.len(), 0); } #[test] fn chunk_at_line_break() { let r = Rope::from_str(TEXT_LINES); let s = r.slice(34..96); let text = &TEXT_LINES[34..112]; // "'s a fine day, isn't it?\nAren't you glad \ // we're alive?\nこんにちは、みん" // First chunk { let (chunk, b, c, l) = s.chunk_at_line_break(0); assert_eq!(chunk, &text[..chunk.len()]); assert_eq!(b, 0); assert_eq!(c, 0); assert_eq!(l, 0); } // Middle chunks for i in 1..s.len_lines() { let (chunk, b, c, l) = s.chunk_at_line_break(i); assert_eq!(chunk, &text[b..(b + chunk.len())]); assert_eq!(c, byte_to_char_idx(text, b)); assert_eq!(l, byte_to_line_idx(text, b)); assert!(l < i); assert!(i <= byte_to_line_idx(text, b + chunk.len())); } // Last chunk { let (chunk, b, c, l) = s.chunk_at_line_break(s.len_lines()); assert_eq!(chunk, &text[(text.len() - chunk.len())..]); assert_eq!(chunk, &text[b..]); assert_eq!(c, byte_to_char_idx(text, b)); assert_eq!(l, byte_to_line_idx(text, b)); } } #[test] fn slice_01() { let r = Rope::from_str(TEXT); let s1 = r.slice(..); let s2 = s1.slice(..); assert_eq!(TEXT, s2); } #[test] fn slice_02() { let r = Rope::from_str(TEXT); let s1 = r.slice(5..43); let s2 = s1.slice(3..25); assert_eq!(&TEXT[8..30], s2); } #[test] fn slice_03() { let r = Rope::from_str(TEXT); let s1 = r.slice(31..97); let s2 = s1.slice(7..64); assert_eq!(&TEXT[38..103], s2); } #[test] fn slice_04() { let r = Rope::from_str(TEXT); let s1 = r.slice(5..43); let s2 = s1.slice(21..21); assert!(s2.is_light()); assert_eq!("", s2); } #[test] fn slice_05() { let r = Rope::from_str(TEXT); let s1 = r.slice(5..98); for i in 0..(s1.len_chars() - 1) { let s2 = s1.slice(i..(i + 1)); assert!(s2.is_light()); } } #[test] #[should_panic] fn slice_06() { let r = Rope::from_str(TEXT); let s = r.slice(5..43); #[allow(clippy::reversed_empty_ranges)] s.slice(21..20); // Wrong ordering on purpose. } #[test] #[should_panic] fn slice_07() { let r = Rope::from_str(TEXT); let s = r.slice(5..43); s.slice(37..39); } #[test] fn byte_slice_01() { let r = Rope::from_str(TEXT); let s1 = r.byte_slice(..); let s2 = s1.byte_slice(..); assert_eq!(TEXT, s2); } #[test] fn byte_slice_02() { let r = Rope::from_str(TEXT); let s1 = r.byte_slice(50..118); let s2 = s1.byte_slice(3..25); assert_eq!(&TEXT[53..75], s2); } #[test] fn byte_slice_03() { let r = Rope::from_str(TEXT); let s1 = r.byte_slice(50..118); let s2 = s1.byte_slice(7..65); assert_eq!(&TEXT[57..115], s2); } #[test] fn byte_slice_04() { let r = Rope::from_str(TEXT); let s1 = r.byte_slice(50..118); let s2 = s1.byte_slice(21..21); assert!(s2.is_light()); assert_eq!("", s2); } #[test] fn byte_slice_05() { let r = Rope::from_str(TEXT); let s1 = r.byte_slice(4..86); for i in 0..(s1.len_bytes() - 1) { let s2 = s1.byte_slice(i..(i + 1)); assert!(s2.is_light()); } } #[test] #[should_panic] fn byte_slice_06() { let r = Rope::from_str(TEXT); let s = r.byte_slice(50..118); #[allow(clippy::reversed_empty_ranges)] s.byte_slice(21..20); // Wrong ordering on purpose. } #[test] #[should_panic] fn byte_slice_07() { let r = Rope::from_str(TEXT); let s = r.byte_slice(50..85); s.byte_slice(35..36); } #[test] #[should_panic] fn byte_slice_08() { let r = Rope::from_str(TEXT); let s = r.byte_slice(50..118); // Not a char boundary. s.byte_slice(..43); } #[test] #[should_panic] fn byte_slice_09() { let r = Rope::from_str(TEXT); let s = r.byte_slice(50..118); // Not a char boundary. s.byte_slice(43..); } #[test] fn eq_str_01() { let r = Rope::from_str(TEXT); let slice = r.slice(..); assert_eq!(slice, TEXT); assert_eq!(TEXT, slice); } #[test] fn eq_str_02() { let r = Rope::from_str(TEXT); let slice = r.slice(0..20); assert_ne!(slice, TEXT); assert_ne!(TEXT, slice); } #[test] fn eq_str_03() { let mut r = Rope::from_str(TEXT); r.remove(20..21); r.insert(20, "z"); let slice = r.slice(..); assert_ne!(slice, TEXT); assert_ne!(TEXT, slice); } #[test] fn eq_str_04() { let r = Rope::from_str(TEXT); let slice = r.slice(..); let s: String = TEXT.into(); assert_eq!(slice, s); assert_eq!(s, slice); } #[test] fn eq_rope_slice_01() { let r = Rope::from_str(TEXT); let s = r.slice(43..43); assert_eq!(s, s); } #[test] fn eq_rope_slice_02() { let r = Rope::from_str(TEXT); let s1 = r.slice(43..97); let s2 = r.slice(43..97); assert_eq!(s1, s2); } #[test] fn eq_rope_slice_03() { let r = Rope::from_str(TEXT); let s1 = r.slice(43..43); let s2 = r.slice(43..45); assert_ne!(s1, s2); } #[test] fn eq_rope_slice_04() { let r = Rope::from_str(TEXT); let s1 = r.slice(43..45); let s2 = r.slice(43..43); assert_ne!(s1, s2); } #[test] fn eq_rope_slice_05() { let r = Rope::from_str(""); let s = r.slice(0..0); assert_eq!(s, s); } #[test] fn cmp_rope_slice_01() { let r1 = Rope::from_str("abcdefghijklmnopqrstuvwxyz"); let r2 = Rope::from_str("abcdefghijklmnopqrstuvwxyz"); let s1 = r1.slice(..); let s2 = r2.slice(..); assert_eq!(s1.cmp(&s2), std::cmp::Ordering::Equal); assert_eq!(s1.slice(..24).cmp(&s2), std::cmp::Ordering::Less); assert_eq!(s1.cmp(&s2.slice(..24)), std::cmp::Ordering::Greater); } #[test] fn cmp_rope_slice_02() { let r1 = Rope::from_str("abcdefghijklmnzpqrstuvwxyz"); let r2 = Rope::from_str("abcdefghijklmnopqrstuvwxyz"); let s1 = r1.slice(..); let s2 = r2.slice(..); assert_eq!(s1.cmp(&s2), std::cmp::Ordering::Greater); assert_eq!(s2.cmp(&s1), std::cmp::Ordering::Less); } #[test] fn to_string_01() { let r = Rope::from_str(TEXT); let slc = r.slice(..); let s: String = slc.into(); assert_eq!(r, s); assert_eq!(slc, s); } #[test] fn to_string_02() { let r = Rope::from_str(TEXT); let slc = r.slice(0..24); let s: String = slc.into(); assert_eq!(slc, s); } #[test] fn to_string_03() { let r = Rope::from_str(TEXT); let slc = r.slice(13..89); let s: String = slc.into(); assert_eq!(slc, s); } #[test] fn to_string_04() { let r = Rope::from_str(TEXT); let slc = r.slice(13..41); let s: String = slc.into(); assert_eq!(slc, s); } #[test] fn to_cow_01() { use std::borrow::Cow; let r = Rope::from_str(TEXT); let s = r.slice(13..83); let cow: Cow = s.into(); assert_eq!(s, cow); } #[test] fn to_cow_02() { use std::borrow::Cow; let r = Rope::from_str(TEXT); let s = r.slice(13..14); let cow: Cow = r.slice(13..14).into(); // Make sure it's borrowed. if let Cow::Owned(_) = cow { panic!("Small Cow conversions should result in a borrow."); } assert_eq!(s, cow); } #[test] fn hash_01() { let mut h1 = std::collections::hash_map::DefaultHasher::new(); let mut h2 = std::collections::hash_map::DefaultHasher::new(); let r = Rope::from_str("Hello there!"); let s = r.slice(..); r.hash(&mut h1); s.hash(&mut h2); assert_eq!(h1.finish(), h2.finish()); } // Iterator tests are in the iter module } ropey-1.6.1/src/str_utils.rs000064400000000000000000000421701046102023000141600ustar 00000000000000//! Utility functions for utf8 string slices. //! //! This module provides various utility functions that operate on string //! slices in ways compatible with Ropey. They may be useful when building //! additional functionality on top of Ropey. pub(crate) use str_indices::chars::count as count_chars; pub use str_indices::chars::from_byte_idx as byte_to_char_idx; pub use str_indices::chars::to_byte_idx as char_to_byte_idx; pub(crate) use str_indices::utf16::count_surrogates as count_utf16_surrogates; // Determine which line implementation to use. #[cfg(feature = "unicode_lines")] use str_indices::lines; #[cfg(all(feature = "cr_lines", not(feature = "unicode_lines")))] use str_indices::lines_crlf as lines; #[cfg(not(any(feature = "cr_lines", feature = "unicode_lines")))] use str_indices::lines_lf as lines; pub(crate) use self::lines::count_breaks as count_line_breaks; pub use self::lines::from_byte_idx as byte_to_line_idx; pub use self::lines::to_byte_idx as line_to_byte_idx; /// Converts from char-index to line-index in a string slice. /// /// This is equivalent to counting the line endings before the given char. /// /// Any past-the-end index will return the last line index. /// /// Runs in O(N) time. #[inline] pub fn char_to_line_idx(text: &str, char_idx: usize) -> usize { lines::from_byte_idx(text, str_indices::chars::to_byte_idx(text, char_idx)) } /// Converts from line-index to char-index in a string slice. /// /// More specifically, this returns the index of the first char of the given line. /// /// Any past-the-end index will return the one-past-the-end char index. /// /// Runs in O(N) time. #[inline] pub fn line_to_char_idx(text: &str, line_idx: usize) -> usize { str_indices::chars::from_byte_idx(text, lines::to_byte_idx(text, line_idx)) } //------------------------------------------------------------- pub(crate) fn byte_to_utf16_surrogate_idx(text: &str, byte_idx: usize) -> usize { let mut i = byte_idx; while !text.is_char_boundary(i) { i -= 1; } str_indices::utf16::count_surrogates(&text[..i]) } pub(crate) fn utf16_code_unit_to_char_idx(text: &str, utf16_idx: usize) -> usize { str_indices::chars::from_byte_idx(text, str_indices::utf16::to_byte_idx(text, utf16_idx)) } /// Returns the byte index of the start of the last line of the passed text. /// /// Note: if the text ends in a line break, that means the last line is /// an empty line that starts at the end of the text. pub(crate) fn last_line_start_byte_idx(text: &str) -> usize { let mut itr = text.bytes().enumerate().rev(); while let Some((idx, byte)) = itr.next() { match byte { 0x0A => { return idx + 1; } 0x0D => { #[cfg(any(feature = "cr_lines", feature = "unicode_lines"))] return idx + 1; } 0x0B | 0x0C => { #[cfg(feature = "unicode_lines")] return idx + 1; } 0x85 => { #[cfg(feature = "unicode_lines")] if let Some((_, 0xC2)) = itr.next() { return idx + 1; } } 0xA8 | 0xA9 => { #[cfg(feature = "unicode_lines")] if let Some((_, 0x80)) = itr.next() { if let Some((_, 0xE2)) = itr.next() { return idx + 1; } } } _ => {} } } return 0; } /// Trims a single trailing line break (if any) off the end of the passed string. /// /// If the string doesn't end in a line break, returns the string unchanged. #[inline] pub(crate) fn trim_line_break(text: &str) -> &str { if text.is_empty() { return ""; } // Find the starting boundary of the last codepoint. let mut i = text.len() - 1; while !text.is_char_boundary(i) { i -= 1; } let tail = &text[i..]; // Check if it's one of the fancy unicode line breaks. #[cfg(feature = "unicode_lines")] if matches!( tail, "\u{000B}" | "\u{000C}" | "\u{0085}" | "\u{2028}" | "\u{2029}" ) { return &text[..i]; } #[cfg(feature = "cr_lines")] if tail == "\u{000D}" { return &text[..i]; } if tail == "\u{000A}" { #[cfg(feature = "cr_lines")] if i > 0 && text.as_bytes()[i - 1] == 0xd { return &text[..(i - 1)]; } return &text[..i]; } return text; } /// Returns whether the given string ends in a line break or not. #[inline] pub(crate) fn ends_with_line_break(text: &str) -> bool { if text.is_empty() { return false; } // Find the starting boundary of the last codepoint. let mut i = text.len() - 1; while !text.is_char_boundary(i) { i -= 1; } // Check if the last codepoint is a line break. #[cfg(feature = "unicode_lines")] return matches!( &text[i..], "\u{000A}" | "\u{000B}" | "\u{000C}" | "\u{000D}" | "\u{0085}" | "\u{2028}" | "\u{2029}" ); #[cfg(all(feature = "cr_lines", not(feature = "unicode_lines")))] return matches!(&text[i..], "\u{000A}" | "\u{000D}"); #[cfg(not(any(feature = "cr_lines", feature = "unicode_lines")))] return &text[i..] == "\u{000A}"; } //====================================================================== #[cfg(test)] mod tests { use super::*; #[cfg(not(any(feature = "cr_lines", feature = "unicode_lines")))] #[test] fn last_line_start_byte_idx_lf_01() { assert_eq!(0, last_line_start_byte_idx("")); assert_eq!(0, last_line_start_byte_idx("Hi")); assert_eq!(3, last_line_start_byte_idx("Hi\u{000A}there.")); assert_eq!(0, last_line_start_byte_idx("Hi\u{000B}there.")); assert_eq!(0, last_line_start_byte_idx("Hi\u{000C}there.")); assert_eq!(0, last_line_start_byte_idx("Hi\u{000D}there.")); assert_eq!(0, last_line_start_byte_idx("Hi\u{0085}there.")); assert_eq!(0, last_line_start_byte_idx("Hi\u{2028}there.")); assert_eq!(0, last_line_start_byte_idx("Hi\u{2029}there.")); } #[cfg(not(any(feature = "cr_lines", feature = "unicode_lines")))] #[test] fn last_line_start_byte_idx_lf_02() { let mut text = "\u{000A}Hello\u{000D}\u{000A}\u{000D}せ\u{000B}か\u{000C}い\u{0085}. \ There\u{2028}is something.\u{2029}"; assert_eq!(48, text.len()); text = &text[..last_line_start_byte_idx(trim_line_break(text))]; assert_eq!(8, text.len()); text = &text[..last_line_start_byte_idx(trim_line_break(text))]; assert_eq!(1, text.len()); text = &text[..last_line_start_byte_idx(trim_line_break(text))]; assert_eq!(0, text.len()); } #[cfg(all(feature = "cr_lines", not(feature = "unicode_lines")))] #[test] fn last_line_start_byte_idx_crlf_01() { assert_eq!(0, last_line_start_byte_idx("")); assert_eq!(0, last_line_start_byte_idx("Hi")); assert_eq!(3, last_line_start_byte_idx("Hi\u{000A}there.")); assert_eq!(0, last_line_start_byte_idx("Hi\u{000B}there.")); assert_eq!(0, last_line_start_byte_idx("Hi\u{000C}there.")); assert_eq!(3, last_line_start_byte_idx("Hi\u{000D}there.")); assert_eq!(0, last_line_start_byte_idx("Hi\u{0085}there.")); assert_eq!(0, last_line_start_byte_idx("Hi\u{2028}there.")); assert_eq!(0, last_line_start_byte_idx("Hi\u{2029}there.")); } #[cfg(all(feature = "cr_lines", not(feature = "unicode_lines")))] #[test] fn last_line_start_byte_idx_crlf_02() { let mut text = "\u{000A}Hello\u{000D}\u{000A}\u{000D}せ\u{000B}か\u{000C}い\u{0085}. \ There\u{2028}is something.\u{2029}"; assert_eq!(48, text.len()); text = &text[..last_line_start_byte_idx(trim_line_break(text))]; assert_eq!(9, text.len()); text = &text[..last_line_start_byte_idx(trim_line_break(text))]; assert_eq!(8, text.len()); text = &text[..last_line_start_byte_idx(trim_line_break(text))]; assert_eq!(1, text.len()); text = &text[..last_line_start_byte_idx(trim_line_break(text))]; assert_eq!(0, text.len()); } #[cfg(feature = "unicode_lines")] #[test] fn last_line_start_byte_idx_unicode_01() { assert_eq!(0, last_line_start_byte_idx("")); assert_eq!(0, last_line_start_byte_idx("Hi")); assert_eq!(3, last_line_start_byte_idx("Hi\u{000A}there.")); assert_eq!(3, last_line_start_byte_idx("Hi\u{000B}there.")); assert_eq!(3, last_line_start_byte_idx("Hi\u{000C}there.")); assert_eq!(3, last_line_start_byte_idx("Hi\u{000D}there.")); assert_eq!(4, last_line_start_byte_idx("Hi\u{0085}there.")); assert_eq!(5, last_line_start_byte_idx("Hi\u{2028}there.")); assert_eq!(5, last_line_start_byte_idx("Hi\u{2029}there.")); } #[cfg(feature = "unicode_lines")] #[test] fn last_line_start_byte_idx_unicode_02() { let mut text = "\u{000A}Hello\u{000D}\u{000A}\u{000D}せ\u{000B}か\u{000C}い\u{0085}. \ There\u{2028}is something.\u{2029}"; assert_eq!(48, text.len()); text = &text[..last_line_start_byte_idx(trim_line_break(text))]; assert_eq!(32, text.len()); text = &text[..last_line_start_byte_idx(trim_line_break(text))]; assert_eq!(22, text.len()); text = &text[..last_line_start_byte_idx(trim_line_break(text))]; assert_eq!(17, text.len()); text = &text[..last_line_start_byte_idx(trim_line_break(text))]; assert_eq!(13, text.len()); text = &text[..last_line_start_byte_idx(trim_line_break(text))]; assert_eq!(9, text.len()); text = &text[..last_line_start_byte_idx(trim_line_break(text))]; assert_eq!(8, text.len()); text = &text[..last_line_start_byte_idx(trim_line_break(text))]; assert_eq!(1, text.len()); text = &text[..last_line_start_byte_idx(trim_line_break(text))]; assert_eq!(0, text.len()); } #[cfg(not(any(feature = "cr_lines", feature = "unicode_lines")))] #[test] fn trim_line_break_lf_01() { assert_eq!("", trim_line_break("")); assert_eq!("Hi", trim_line_break("Hi")); assert_eq!("Hi", trim_line_break("Hi\u{000A}")); assert_eq!("Hi\u{000B}", trim_line_break("Hi\u{000B}")); assert_eq!("Hi\u{000C}", trim_line_break("Hi\u{000C}")); assert_eq!("Hi\u{000D}", trim_line_break("Hi\u{000D}")); assert_eq!("Hi\u{0085}", trim_line_break("Hi\u{0085}")); assert_eq!("Hi\u{2028}", trim_line_break("Hi\u{2028}")); assert_eq!("Hi\u{2029}", trim_line_break("Hi\u{2029}")); assert_eq!("\r", trim_line_break("\r\n")); assert_eq!("Hi\r", trim_line_break("Hi\r\n")); } #[cfg(all(feature = "cr_lines", not(feature = "unicode_lines")))] #[test] fn trim_line_break_crlf_01() { assert_eq!("", trim_line_break("")); assert_eq!("Hi", trim_line_break("Hi")); assert_eq!("Hi", trim_line_break("Hi\u{000A}")); assert_eq!("Hi\u{000B}", trim_line_break("Hi\u{000B}")); assert_eq!("Hi\u{000C}", trim_line_break("Hi\u{000C}")); assert_eq!("Hi", trim_line_break("Hi\u{000D}")); assert_eq!("Hi\u{0085}", trim_line_break("Hi\u{0085}")); assert_eq!("Hi\u{2028}", trim_line_break("Hi\u{2028}")); assert_eq!("Hi\u{2029}", trim_line_break("Hi\u{2029}")); assert_eq!("", trim_line_break("\r\n")); assert_eq!("Hi", trim_line_break("Hi\r\n")); } #[cfg(feature = "unicode_lines")] #[test] fn trim_line_break_unicode_01() { assert_eq!("", trim_line_break("")); assert_eq!("Hi", trim_line_break("Hi")); assert_eq!("Hi", trim_line_break("Hi\u{000A}")); assert_eq!("Hi", trim_line_break("Hi\u{000B}")); assert_eq!("Hi", trim_line_break("Hi\u{000C}")); assert_eq!("Hi", trim_line_break("Hi\u{000D}")); assert_eq!("Hi", trim_line_break("Hi\u{0085}")); assert_eq!("Hi", trim_line_break("Hi\u{2028}")); assert_eq!("Hi", trim_line_break("Hi\u{2029}")); assert_eq!("", trim_line_break("\r\n")); assert_eq!("Hi", trim_line_break("Hi\r\n")); } #[test] fn ends_with_line_break_01() { assert!(ends_with_line_break("\n")); #[cfg(any(feature = "cr_lines", feature = "unicode_lines"))] assert!(ends_with_line_break("\r")); #[cfg(feature = "unicode_lines")] { assert!(ends_with_line_break("\u{000A}")); assert!(ends_with_line_break("\u{000B}")); assert!(ends_with_line_break("\u{000C}")); assert!(ends_with_line_break("\u{000D}")); assert!(ends_with_line_break("\u{0085}")); assert!(ends_with_line_break("\u{2028}")); assert!(ends_with_line_break("\u{2029}")); } } #[test] fn ends_with_line_break_02() { assert!(ends_with_line_break("Hi there!\n")); #[cfg(any(feature = "cr_lines", feature = "unicode_lines"))] assert!(ends_with_line_break("Hi there!\r")); #[cfg(feature = "unicode_lines")] { assert!(ends_with_line_break("Hi there!\u{000A}")); assert!(ends_with_line_break("Hi there!\u{000B}")); assert!(ends_with_line_break("Hi there!\u{000C}")); assert!(ends_with_line_break("Hi there!\u{000D}")); assert!(ends_with_line_break("Hi there!\u{0085}")); assert!(ends_with_line_break("Hi there!\u{2028}")); assert!(ends_with_line_break("Hi there!\u{2029}")); } } #[test] fn ends_with_line_break_03() { assert!(!ends_with_line_break("")); assert!(!ends_with_line_break("a")); assert!(!ends_with_line_break("Hi there!")); } #[test] fn ends_with_line_break_04() { assert!(!ends_with_line_break("\na")); assert!(!ends_with_line_break("\ra")); assert!(!ends_with_line_break("\u{000A}a")); assert!(!ends_with_line_break("\u{000B}a")); assert!(!ends_with_line_break("\u{000C}a")); assert!(!ends_with_line_break("\u{000D}a")); assert!(!ends_with_line_break("\u{0085}a")); assert!(!ends_with_line_break("\u{2028}a")); assert!(!ends_with_line_break("\u{2029}a")); } #[test] fn char_to_line_idx_01() { let text = "\u{000A}Hello\u{000D}\u{000A}\u{000D}せ\u{000B}か\u{000C}い\u{0085}. \ There\u{2028}is something.\u{2029}"; #[cfg(not(any(feature = "cr_lines", feature = "unicode_lines")))] { assert_eq!(0, char_to_line_idx(text, 0)); assert_eq!(1, char_to_line_idx(text, 1)); assert_eq!(2, char_to_line_idx(text, 8)); assert_eq!(2, char_to_line_idx(text, 38)); } #[cfg(all(feature = "cr_lines", not(feature = "unicode_lines")))] { assert_eq!(0, char_to_line_idx(text, 0)); assert_eq!(1, char_to_line_idx(text, 1)); assert_eq!(2, char_to_line_idx(text, 8)); assert_eq!(3, char_to_line_idx(text, 9)); assert_eq!(3, char_to_line_idx(text, 38)); } #[cfg(feature = "unicode_lines")] { assert_eq!(0, char_to_line_idx(text, 0)); assert_eq!(1, char_to_line_idx(text, 1)); assert_eq!(2, char_to_line_idx(text, 8)); assert_eq!(3, char_to_line_idx(text, 9)); assert_eq!(4, char_to_line_idx(text, 11)); assert_eq!(5, char_to_line_idx(text, 13)); assert_eq!(6, char_to_line_idx(text, 15)); assert_eq!(7, char_to_line_idx(text, 23)); assert_eq!(8, char_to_line_idx(text, 37)); assert_eq!(8, char_to_line_idx(text, 38)); } } #[test] fn line_to_char_idx_01() { let text = "\u{000A}Hello\u{000D}\u{000A}\u{000D}せ\u{000B}か\u{000C}い\u{0085}. \ There\u{2028}is something.\u{2029}"; #[cfg(not(any(feature = "cr_lines", feature = "unicode_lines")))] { assert_eq!(0, line_to_char_idx(text, 0)); assert_eq!(1, line_to_char_idx(text, 1)); assert_eq!(8, line_to_char_idx(text, 2)); assert_eq!(37, line_to_char_idx(text, 3)); } #[cfg(all(feature = "cr_lines", not(feature = "unicode_lines")))] { assert_eq!(0, line_to_char_idx(text, 0)); assert_eq!(1, line_to_char_idx(text, 1)); assert_eq!(8, line_to_char_idx(text, 2)); assert_eq!(9, line_to_char_idx(text, 3)); assert_eq!(37, line_to_char_idx(text, 4)); } #[cfg(feature = "unicode_lines")] { assert_eq!(0, line_to_char_idx(text, 0)); assert_eq!(1, line_to_char_idx(text, 1)); assert_eq!(8, line_to_char_idx(text, 2)); assert_eq!(9, line_to_char_idx(text, 3)); assert_eq!(11, line_to_char_idx(text, 4)); assert_eq!(13, line_to_char_idx(text, 5)); assert_eq!(15, line_to_char_idx(text, 6)); assert_eq!(23, line_to_char_idx(text, 7)); assert_eq!(37, line_to_char_idx(text, 8)); assert_eq!(37, line_to_char_idx(text, 9)); } } } ropey-1.6.1/src/tree/mod.rs000064400000000000000000000103431046102023000136430ustar 00000000000000mod node; mod node_children; mod node_text; mod text_info; pub(crate) use self::node::Node; pub(crate) use self::node_children::NodeChildren; pub(crate) use self::node_text::NodeText; pub(crate) use self::text_info::TextInfo; // Type used for storing tree metadata, such as byte and char length. pub(crate) type Count = u64; // Real constants used in release builds. #[cfg(not(any(test, feature = "small_chunks")))] mod constants { use super::{Node, TextInfo}; use smallvec::SmallVec; use std::{ mem::{align_of, size_of}, sync::Arc, }; // Because stdlib's max is not const for some reason. // TODO: replace with stdlib max once it's const. const fn cmax(a: usize, b: usize) -> usize { if a > b { a } else { b } } // Aim for Node + Arc counters to be 1024 bytes. Keeping the nodes // multiples of large powers of two makes it easier for the memory // allocator to avoid fragmentation. const TARGET_TOTAL_SIZE: usize = 1024; // Space that the strong and weak Arc counters take up in `ArcInner`. const ARC_COUNTERS_SIZE: usize = size_of::() * 2; // Misc useful info that we need below. const NODE_CHILDREN_ALIGN: usize = cmax(align_of::>(), align_of::()); const NODE_TEXT_ALIGN: usize = align_of::>(); const START_OFFSET: usize = { const NODE_INNER_ALIGN: usize = cmax(NODE_CHILDREN_ALIGN, NODE_TEXT_ALIGN); // The +NODE_INNER_ALIGN is because of Node's enum discriminant. ARC_COUNTERS_SIZE + NODE_INNER_ALIGN }; // Node maximums. #[doc(hidden)] // NOT PART OF THE PUBLIC API! pub const MAX_CHILDREN: usize = { let node_list_align = align_of::>(); let info_list_align = align_of::(); let field_gap = if node_list_align >= info_list_align { 0 } else { // This is over-conservative, because in reality it depends // on the number of elements. But handling that is probably // more complexity than it's worth. info_list_align - node_list_align }; // The -NODE_CHILDREN_ALIGN is for the `len` field in `NodeChildrenInternal`. let target_size = TARGET_TOTAL_SIZE - START_OFFSET - NODE_CHILDREN_ALIGN - field_gap; target_size / (size_of::>() + size_of::()) }; #[doc(hidden)] // NOT PART OF THE PUBLIC API! pub const MAX_BYTES: usize = { let smallvec_overhead = size_of::>() - 16; TARGET_TOTAL_SIZE - START_OFFSET - smallvec_overhead }; // Node minimums. // Note: MIN_BYTES is intentionally a little smaller than half // MAX_BYTES, to give a little wiggle room when on the edge of // merging/splitting. #[doc(hidden)] // NOT PART OF THE PUBLIC API! pub const MIN_CHILDREN: usize = MAX_CHILDREN / 2; #[doc(hidden)] // NOT PART OF THE PUBLIC API! pub const MIN_BYTES: usize = (MAX_BYTES / 2) - (MAX_BYTES / 32); // Compile-time assertion. const _: () = { assert!( (ARC_COUNTERS_SIZE + size_of::()) == TARGET_TOTAL_SIZE, "`Node` is not the target size in memory.", ); }; } // Smaller constants used in debug builds. These are different from release // in order to trigger deeper trees without having to use huge text data in // the tests. #[cfg(any(test, feature = "small_chunks"))] mod test_constants { #[doc(hidden)] // NOT PART OF THE PUBLIC API! pub const MAX_CHILDREN: usize = 5; #[doc(hidden)] // NOT PART OF THE PUBLIC API! pub const MIN_CHILDREN: usize = MAX_CHILDREN / 2; // MAX_BYTES must be >= 4 to allow for 4-byte utf8 characters. #[doc(hidden)] // NOT PART OF THE PUBLIC API! pub const MAX_BYTES: usize = 9; // Note: can't be 8, because 3-byte characters. #[doc(hidden)] // NOT PART OF THE PUBLIC API! pub const MIN_BYTES: usize = (MAX_BYTES / 2) - (MAX_BYTES / 32); } #[cfg(not(any(test, feature = "small_chunks")))] pub use self::constants::{MAX_BYTES, MAX_CHILDREN, MIN_BYTES, MIN_CHILDREN}; #[cfg(any(test, feature = "small_chunks"))] pub use self::test_constants::{MAX_BYTES, MAX_CHILDREN, MIN_BYTES, MIN_CHILDREN}; ropey-1.6.1/src/tree/node.rs000064400000000000000000001226261046102023000140210ustar 00000000000000use std::sync::Arc; use crate::str_utils::{ byte_to_char_idx, byte_to_line_idx, byte_to_utf16_surrogate_idx, char_to_byte_idx, }; use crate::tree::node_text::fix_segment_seam; use crate::tree::{ Count, NodeChildren, NodeText, TextInfo, MAX_BYTES, MAX_CHILDREN, MIN_BYTES, MIN_CHILDREN, }; #[derive(Debug, Clone)] #[repr(u8, C)] pub(crate) enum Node { Leaf(NodeText), Internal(NodeChildren), } impl Node { /// Creates an empty node. #[inline(always)] pub fn new() -> Self { Node::Leaf(NodeText::from_str("")) } /// Total number of bytes in the Rope. #[inline(always)] pub fn byte_count(&self) -> usize { self.text_info().bytes as usize } /// Total number of chars in the Rope. #[inline(always)] pub fn char_count(&self) -> usize { self.text_info().chars as usize } /// Total number of line breaks in the Rope. #[inline(always)] pub fn line_break_count(&self) -> usize { self.text_info().line_breaks as usize } /// Total number of line breaks in the Rope. #[inline(always)] pub fn utf16_surrogate_count(&self) -> usize { self.text_info().utf16_surrogates as usize } /// Fetches a chunk mutably, and allows it to be edited via a closure. /// /// There are three parameters: /// - char_idx: the chunk that contains this char is fetched, /// - node_info: this is the text info of the node it's being called on. /// This makes it a little awkward to call, but is needed since /// it's actually the parent node that contains the text info, /// so the info needs to be passed in. /// - edit: the closure that receives the chunk and does the edits. /// /// The closure is effectively the termination case for the recursion, /// and takes essentially same parameters and returns the same things as /// the method itself. In particular, the closure receives the char offset /// of char_idx within the given chunk and the TextInfo of the chunk. /// The main difference is that it receives a NodeText instead of a node. /// /// The closure is expected to return the updated text info of the node, /// and if the node had to be split, then it also returns the right-hand /// node along with its TextInfo as well. /// /// The main method call will then return the total updated TextInfo for /// the whole tree, and a new node only if the whole tree had to be split. /// It is up to the caller to check for that new node, and handle it by /// creating a new root with both the original node and the new node as /// children. pub fn edit_chunk_at_char( &mut self, char_idx: usize, node_info: TextInfo, mut edit: F, ) -> (TextInfo, Option<(TextInfo, Arc)>) where F: FnMut(usize, TextInfo, &mut NodeText) -> (TextInfo, Option<(TextInfo, Arc)>), { match *self { Node::Leaf(ref mut leaf_text) => edit(char_idx, node_info, leaf_text), Node::Internal(ref mut children) => { // Compact leaf children if we're very close to maximum leaf // fragmentation. This basically guards against excessive memory // ballooning when repeatedly appending to the end of a rope. // The constant here was arrived at experimentally, and is otherwise // fairly arbitrary. const FRAG_MIN_BYTES: usize = (MAX_BYTES * MIN_CHILDREN) + (MAX_BYTES / 32); if children.is_full() && children.nodes()[0].is_leaf() && (children.combined_info().bytes as usize) < FRAG_MIN_BYTES { children.compact_leaves(); } // Find the child we care about. let (child_i, acc_char_idx) = children.search_char_idx_only(char_idx); let info = children.info()[child_i]; // Recurse into the child. let (l_info, residual) = Arc::make_mut(&mut children.nodes_mut()[child_i]) .edit_chunk_at_char(char_idx - acc_char_idx, info, edit); children.info_mut()[child_i] = l_info; // Handle the residual node if there is one and return. if let Some((r_info, r_node)) = residual { if children.len() < MAX_CHILDREN { children.insert(child_i + 1, (r_info, r_node)); (node_info - info + l_info + r_info, None) } else { let r = children.insert_split(child_i + 1, (r_info, r_node)); let r_info = r.combined_info(); ( children.combined_info(), Some((r_info, Arc::new(Node::Internal(r)))), ) } } else { (node_info - info + l_info, None) } } } } /// Removes chars in the range `start_idx..end_idx`. /// /// Returns (in this order): /// - The updated TextInfo for the node. /// - Whether there's a possible CRLF seam that needs fixing. /// - Whether fix_tree_seam() needs to be run after this. /// /// WARNING: does not correctly handle all text being removed. That /// should be special-cased in calling code. pub fn remove_char_range( &mut self, start_idx: usize, end_idx: usize, node_info: TextInfo, ) -> (TextInfo, bool, bool) { if start_idx == end_idx { return (node_info, false, false); } match *self { // If it's a leaf Node::Leaf(ref mut leaf_text) => { let byte_start = char_to_byte_idx(leaf_text, start_idx); let byte_end = byte_start + char_to_byte_idx(&leaf_text[byte_start..], end_idx - start_idx); // Remove text and calculate new info & seam info if byte_start > 0 || byte_end < leaf_text.len() { let seam = (byte_start == 0 && leaf_text.as_bytes()[byte_end] == 0x0A) || (byte_end == leaf_text.len() && leaf_text.as_bytes()[byte_start - 1] == 0x0D); let seg_len = byte_end - byte_start; // Length of removal segement if seg_len < (leaf_text.len() - seg_len) { #[allow(unused_mut)] let mut info = node_info - TextInfo::from_str(&leaf_text[byte_start..byte_end]); // Check for CRLF pairs on the removal seams, and // adjust line break counts accordingly. #[cfg(any(feature = "cr_lines", feature = "unicode_lines"))] { if byte_end < leaf_text.len() && leaf_text.as_bytes()[byte_end - 1] == 0x0D && leaf_text.as_bytes()[byte_end] == 0x0A { info.line_breaks += 1; } if byte_start > 0 && leaf_text.as_bytes()[byte_start - 1] == 0x0D { if leaf_text.as_bytes()[byte_start] == 0x0A { info.line_breaks += 1; } if byte_end < leaf_text.len() && leaf_text.as_bytes()[byte_end] == 0x0A { info.line_breaks -= 1; } } } // Remove the text leaf_text.remove_range(byte_start, byte_end); (info, seam, false) } else { // Remove the text leaf_text.remove_range(byte_start, byte_end); (TextInfo::from_str(leaf_text), seam, false) } } else { // Remove all of the text leaf_text.remove_range(byte_start, byte_end); (TextInfo::new(), true, false) } } // If it's internal, it's much more complicated Node::Internal(ref mut children) => { // Shared code for handling children. // Returns (in this order): // - Whether there's a possible CRLF seam that needs fixing. // - Whether the tree may need invariant fixing. // - Updated TextInfo of the node. let handle_child = |children: &mut NodeChildren, child_i: usize, c_char_acc: usize| -> (bool, bool, TextInfo) { // Recurse into child let tmp_info = children.info()[child_i]; let tmp_chars = children.info()[child_i].chars as usize; let (new_info, seam, needs_fix) = Arc::make_mut(&mut children.nodes_mut()[child_i]).remove_char_range( start_idx - c_char_acc.min(start_idx), (end_idx - c_char_acc).min(tmp_chars), tmp_info, ); // Handle result if new_info.bytes == 0 { children.remove(child_i); } else { children.info_mut()[child_i] = new_info; } (seam, needs_fix, new_info) }; // Shared code for merging children let merge_child = |children: &mut NodeChildren, child_i: usize| { if child_i < children.len() && children.len() > 1 && children.nodes()[child_i].is_undersized() { if child_i == 0 { children.merge_distribute(child_i, child_i + 1); } else { children.merge_distribute(child_i - 1, child_i); } } }; // Get child info for the two char indices let ((l_child_i, l_char_acc), (r_child_i, r_char_acc)) = children.search_char_idx_range(start_idx, end_idx); // Both indices point into the same child if l_child_i == r_child_i { let info = children.info()[l_child_i]; let (seam, mut needs_fix, new_info) = handle_child(children, l_child_i, l_char_acc); if children.len() > 0 { merge_child(children, l_child_i); // If we couldn't get all children >= minimum size, then // we'll need to fix that later. if children.nodes()[l_child_i.min(children.len() - 1)].is_undersized() { needs_fix = true; } } return (node_info - info + new_info, seam, needs_fix); } // We're dealing with more than one child. else { let mut needs_fix = false; // Calculate the start..end range of nodes to be removed. let r_child_exists: bool; let start_i = l_child_i + 1; let end_i = if r_char_acc + children.info()[r_child_i].chars as usize == end_idx { r_child_exists = false; r_child_i + 1 } else { r_child_exists = true; r_child_i }; // Remove the children for _ in start_i..end_i { children.remove(start_i); } // Handle right child if r_child_exists { let (_, fix, _) = handle_child(children, l_child_i + 1, r_char_acc); needs_fix |= fix; } // Handle left child let (seam, fix, _) = handle_child(children, l_child_i, l_char_acc); needs_fix |= fix; if children.len() > 0 { // Handle merging let merge_extent = 1 + if r_child_exists { 1 } else { 0 }; for i in (l_child_i..(l_child_i + merge_extent)).rev() { merge_child(children, i); } // If we couldn't get all children >= minimum size, then // we'll need to fix that later. if children.nodes()[l_child_i.min(children.len() - 1)].is_undersized() { needs_fix = true; } } // Return return (children.combined_info(), seam, needs_fix); } } } } pub fn append_at_depth(&mut self, other: Arc, depth: usize) -> Option> { if depth == 0 { match *self { Node::Leaf(_) => { if !other.is_leaf() { panic!("Tree-append siblings have differing types."); } else { return Some(other); } } Node::Internal(ref mut children_l) => { let mut other = other; if let Node::Internal(ref mut children_r) = *Arc::make_mut(&mut other) { if (children_l.len() + children_r.len()) <= MAX_CHILDREN { for _ in 0..children_r.len() { children_l.push(children_r.remove(0)); } return None; } else { children_l.distribute_with(children_r); // Return lower down, to avoid borrow-checker. } } else { panic!("Tree-append siblings have differing types."); } return Some(other); } } } else if let Node::Internal(ref mut children) = *self { let last_i = children.len() - 1; let residual = Arc::make_mut(&mut children.nodes_mut()[last_i]).append_at_depth(other, depth - 1); children.update_child_info(last_i); if let Some(extra_node) = residual { if children.len() < MAX_CHILDREN { children.push((extra_node.text_info(), extra_node)); return None; } else { let r_children = children.push_split((extra_node.text_info(), extra_node)); return Some(Arc::new(Node::Internal(r_children))); } } else { return None; } } else { panic!("Reached leaf before getting to target depth."); } } pub fn prepend_at_depth(&mut self, other: Arc, depth: usize) -> Option> { if depth == 0 { match *self { Node::Leaf(_) => { if !other.is_leaf() { panic!("Tree-append siblings have differing types."); } else { return Some(other); } } Node::Internal(ref mut children_r) => { let mut other = other; if let Node::Internal(ref mut children_l) = *Arc::make_mut(&mut other) { if (children_l.len() + children_r.len()) <= MAX_CHILDREN { for _ in 0..children_l.len() { children_r.insert(0, children_l.pop()); } return None; } else { children_l.distribute_with(children_r); // Return lower down, to avoid borrow-checker. } } else { panic!("Tree-append siblings have differing types."); } return Some(other); } } } else if let Node::Internal(ref mut children) = *self { let residual = Arc::make_mut(&mut children.nodes_mut()[0]).prepend_at_depth(other, depth - 1); children.update_child_info(0); if let Some(extra_node) = residual { if children.len() < MAX_CHILDREN { children.insert(0, (extra_node.text_info(), extra_node)); return None; } else { let mut r_children = children.insert_split(0, (extra_node.text_info(), extra_node)); std::mem::swap(children, &mut r_children); return Some(Arc::new(Node::Internal(r_children))); } } else { return None; } } else { panic!("Reached leaf before getting to target depth."); } } /// Splits the `Node` at char index `char_idx`, returning /// the right side of the split. pub fn split(&mut self, char_idx: usize) -> Node { debug_assert!(char_idx != 0); debug_assert!(char_idx != (self.text_info().chars as usize)); match *self { Node::Leaf(ref mut text) => { let byte_idx = char_to_byte_idx(text, char_idx); Node::Leaf(text.split_off(byte_idx)) } Node::Internal(ref mut children) => { let (child_i, acc_info) = children.search_char_idx(char_idx); let child_info = children.info()[child_i]; if char_idx == acc_info.chars as usize { Node::Internal(children.split_off(child_i)) } else if char_idx == (acc_info.chars as usize + child_info.chars as usize) { Node::Internal(children.split_off(child_i + 1)) } else { let mut r_children = children.split_off(child_i + 1); // Recurse let r_node = Arc::make_mut(&mut children.nodes_mut()[child_i]) .split(char_idx - acc_info.chars as usize); r_children.insert(0, (r_node.text_info(), Arc::new(r_node))); children.update_child_info(child_i); r_children.update_child_info(0); Node::Internal(r_children) } } } } /// Returns the chunk that contains the given byte, and the TextInfo /// corresponding to the start of the chunk. pub fn get_chunk_at_byte(&self, byte_idx: usize) -> (&str, TextInfo) { let mut node = self; let mut byte_idx = byte_idx; let mut info = TextInfo::new(); loop { match *node { Node::Leaf(ref text) => { return (text, info); } Node::Internal(ref children) => { let (child_i, acc_info) = children.search_byte_idx(byte_idx); info += acc_info; node = &*children.nodes()[child_i]; byte_idx -= acc_info.bytes as usize; } } } } /// Returns the chunk that contains the given char, and the TextInfo /// corresponding to the start of the chunk. pub fn get_chunk_at_char(&self, char_idx: usize) -> (&str, TextInfo) { let mut node = self; let mut char_idx = char_idx; let mut info = TextInfo::new(); loop { match *node { Node::Leaf(ref text) => { return (text, info); } Node::Internal(ref children) => { let (child_i, acc_info) = children.search_char_idx(char_idx); info += acc_info; node = &*children.nodes()[child_i]; char_idx -= acc_info.chars as usize; } } } } /// Returns the chunk that contains the given utf16 code unit, and the /// TextInfo corresponding to the start of the chunk. pub fn get_chunk_at_utf16_code_unit(&self, utf16_idx: usize) -> (&str, TextInfo) { let mut node = self; let mut utf16_idx = utf16_idx; let mut info = TextInfo::new(); loop { match *node { Node::Leaf(ref text) => { return (text, info); } Node::Internal(ref children) => { let (child_i, acc_info) = children.search_utf16_code_unit_idx(utf16_idx); info += acc_info; node = &*children.nodes()[child_i]; utf16_idx -= (acc_info.chars + acc_info.utf16_surrogates) as usize; } } } } /// Returns the chunk that contains the given line break, and the TextInfo /// corresponding to the start of the chunk. /// /// Note: for convenience, both the beginning and end of the rope are /// considered line breaks for indexing. pub fn get_chunk_at_line_break(&self, line_break_idx: usize) -> (&str, TextInfo) { let mut node = self; let mut line_break_idx = line_break_idx; let mut info = TextInfo::new(); loop { match *node { Node::Leaf(ref text) => { return (text, info); } Node::Internal(ref children) => { let (child_i, acc_info) = children.search_line_break_idx(line_break_idx); info += acc_info; node = &*children.nodes()[child_i]; line_break_idx -= acc_info.line_breaks as usize; } } } } /// Returns the TextInfo at the given char index. #[inline(always)] pub fn char_to_text_info(&self, char_idx: usize) -> TextInfo { let (chunk, info) = self.get_chunk_at_char(char_idx); let bi = char_to_byte_idx(chunk, char_idx - info.chars as usize); TextInfo { bytes: info.bytes + bi as Count, chars: char_idx as Count, utf16_surrogates: info.utf16_surrogates + byte_to_utf16_surrogate_idx(chunk, bi) as Count, line_breaks: info.line_breaks + byte_to_line_idx(chunk, bi) as Count, } } /// Returns the TextInfo at the given byte index. #[inline(always)] pub fn byte_to_text_info(&self, byte_idx: usize) -> TextInfo { let (chunk, info) = self.get_chunk_at_byte(byte_idx); let bi = byte_idx - info.bytes as usize; let ci = byte_to_char_idx(chunk, byte_idx - info.bytes as usize); TextInfo { bytes: byte_idx as Count, chars: info.chars + ci as Count, utf16_surrogates: info.utf16_surrogates + byte_to_utf16_surrogate_idx(chunk, bi) as Count, line_breaks: info.line_breaks + byte_to_line_idx(chunk, bi) as Count, } } pub fn text_info(&self) -> TextInfo { match *self { Node::Leaf(ref text) => TextInfo::from_str(text), Node::Internal(ref children) => children.combined_info(), } } pub fn is_char_boundary(&self, byte_idx: usize) -> bool { let (chunk, info) = self.get_chunk_at_byte(byte_idx); chunk.is_char_boundary(byte_idx - info.bytes as usize) } #[cfg(any(feature = "cr_lines", feature = "unicode_lines"))] pub fn is_crlf_split(&self, char_idx: usize) -> bool { let (chunk, info) = self.get_chunk_at_char(char_idx); let idx = char_to_byte_idx(chunk, char_idx - info.chars as usize); if idx == 0 || idx == chunk.len() { false } else { let chunk = chunk.as_bytes(); chunk[idx - 1] == 0x0D && chunk[idx] == 0x0A } } //----------------------------------------- pub fn child_count(&self) -> usize { if let Node::Internal(ref children) = *self { children.len() } else { panic!() } } pub fn children(&self) -> &NodeChildren { match *self { Node::Internal(ref children) => children, _ => panic!(), } } pub fn children_mut(&mut self) -> &mut NodeChildren { match *self { Node::Internal(ref mut children) => children, _ => panic!(), } } pub fn leaf_text(&self) -> &str { if let Node::Leaf(ref text) = *self { text } else { panic!() } } pub fn leaf_text_mut(&mut self) -> &mut NodeText { if let Node::Leaf(ref mut text) = *self { text } else { panic!() } } pub fn is_leaf(&self) -> bool { match *self { Node::Leaf(_) => true, Node::Internal(_) => false, } } pub fn is_undersized(&self) -> bool { match *self { Node::Leaf(ref text) => text.len() < MIN_BYTES, Node::Internal(ref children) => children.len() < MIN_CHILDREN, } } /// How many nodes deep the tree is. /// /// This counts root and leafs. For example, a single leaf node /// has depth 1. pub fn depth(&self) -> usize { let mut node = self; let mut depth = 0; loop { match *node { Node::Leaf(_) => return depth, Node::Internal(ref children) => { depth += 1; node = &*children.nodes()[0]; } } } } /// Debugging tool to make sure that all of the meta-data of the /// tree is consistent with the actual data. pub fn assert_integrity(&self) { match *self { Node::Leaf(_) => {} Node::Internal(ref children) => { for (info, node) in children.iter() { if *info != node.text_info() { assert_eq!(*info, node.text_info()); } node.assert_integrity(); } } } } /// Checks that the entire tree is the same height everywhere. pub fn assert_balance(&self) -> usize { // Depth, child count, and leaf node emptiness match *self { Node::Leaf(_) => 1, Node::Internal(ref children) => { let first_depth = children.nodes()[0].assert_balance(); for node in &children.nodes()[1..] { assert_eq!(node.assert_balance(), first_depth); } first_depth + 1 } } } /// Checks that all internal nodes have the minimum number of /// children and all non-root leaf nodes are non-empty. pub fn assert_node_size(&self, is_root: bool) { match *self { Node::Leaf(ref text) => { // Leaf size if !is_root { assert!(text.len() > 0); } } Node::Internal(ref children) => { // Child count if is_root { assert!(children.len() > 1); } else { assert!(children.len() >= MIN_CHILDREN); } for node in children.nodes() { node.assert_node_size(false); } } } } /// Checks to make sure that a boundary between leaf nodes (given as a byte /// position in the rope) doesn't split a CRLF pair, and fixes it if it does. /// /// If `must_be_boundary` is true, panics if the given byte position is /// not on the boundary between two leaf nodes. /// /// TODO: theoretically this can leave an internal node with fewer than /// MIN_CHILDREN children, although it is exceedingly unlikely with any /// remotely sane text. In the mean time, right now no code actually /// depends on there being at least MIN_CHILDREN in an internal node. /// But this should nevertheless get addressed at some point. /// Probably the most straight-forward way to address this is via the /// `fix_info_*` methods below, but I'm not totally sure. pub fn fix_crlf_seam(&mut self, byte_pos: Count, must_be_boundary: bool) { if let Node::Internal(ref mut children) = *self { if byte_pos == 0 { // Special-case 1 Arc::make_mut(&mut children.nodes_mut()[0]) .fix_crlf_seam(byte_pos, must_be_boundary); } else if byte_pos == children.combined_info().bytes { // Special-case 2 let (info, nodes) = children.data_mut(); Arc::make_mut(nodes.last_mut().unwrap()) .fix_crlf_seam(info.last().unwrap().bytes, must_be_boundary); } else { // Find the child to navigate into let (child_i, start_info) = children.search_byte_idx(byte_pos as usize); let start_byte = start_info.bytes; let pos_in_child = byte_pos - start_byte; let child_len = children.info()[child_i].bytes; if pos_in_child == 0 || pos_in_child == child_len { // Left or right edge, get neighbor and fix seam let l_child_i = if pos_in_child == 0 { debug_assert!(child_i != 0); child_i - 1 } else { debug_assert!(child_i < children.len()); child_i }; // Scope for borrow { // Fetch the two children let (l_child, r_child) = children.get_two_mut(l_child_i, l_child_i + 1); let l_child_bytes = l_child.0.bytes; let l_child = Arc::make_mut(l_child.1); let r_child = Arc::make_mut(r_child.1); // Get the text of the two children and fix // the seam between them. // Scope for borrow. { let (l_text, l_offset) = l_child.get_chunk_at_byte_mut(l_child_bytes as usize); let (r_text, r_offset) = r_child.get_chunk_at_byte_mut(0); if must_be_boundary { assert!(l_offset == 0 || l_offset == l_text.len()); assert!(r_offset == 0 || r_offset == r_text.len()); } fix_segment_seam(l_text, r_text); } // Fix up the children's metadata after the change // to their text. l_child.fix_info_right(); r_child.fix_info_left(); } // Fix up this node's metadata for those // two children. children.update_child_info(l_child_i); children.update_child_info(l_child_i + 1); // Remove the children if empty. if children.info()[l_child_i + 1].bytes == 0 { children.remove(l_child_i + 1); } else if children.info()[l_child_i].bytes == 0 { children.remove(l_child_i); } } else { // Internal to child Arc::make_mut(&mut children.nodes_mut()[child_i]) .fix_crlf_seam(pos_in_child, must_be_boundary); children.update_child_info(child_i); if children.info()[child_i].bytes == 0 { children.remove(child_i); } } } } } /// Returns the chunk that contains the given byte, and the offset /// of that byte within the chunk. pub fn get_chunk_at_byte_mut(&mut self, byte_idx: usize) -> (&mut NodeText, usize) { match *self { Node::Leaf(ref mut text) => return (text, byte_idx), Node::Internal(ref mut children) => { let (child_i, acc_info) = children.search_byte_idx(byte_idx); Arc::make_mut(&mut children.nodes_mut()[child_i]) .get_chunk_at_byte_mut(byte_idx - acc_info.bytes as usize) } } } /// Updates the tree meta-data down the left side of the tree, and removes empty /// children as it goes as well. fn fix_info_left(&mut self) { match *self { Node::Leaf(_) => {} Node::Internal(ref mut children) => { Arc::make_mut(&mut children.nodes_mut()[0]).fix_info_left(); children.update_child_info(0); if children.info()[0].bytes == 0 { children.remove(0); } } } } /// Updates the tree meta-data down the right side of the tree, and removes empty /// children as it goes as well. fn fix_info_right(&mut self) { match *self { Node::Leaf(_) => {} Node::Internal(ref mut children) => { let idx = children.len() - 1; Arc::make_mut(&mut children.nodes_mut()[idx]).fix_info_right(); children.update_child_info(idx); if children.info()[idx].bytes == 0 { children.remove(idx); } } } } /// Fixes dangling nodes down the left side of the tree. /// /// Returns whether it did anything or not that would affect the /// parent. pub fn zip_fix_left(&mut self) -> bool { if let Node::Internal(ref mut children) = *self { let mut did_stuff = false; loop { let do_merge = (children.len() > 1) && match *children.nodes()[0] { Node::Leaf(ref text) => text.len() < MIN_BYTES, Node::Internal(ref children2) => children2.len() < MIN_CHILDREN, }; if do_merge { did_stuff |= children.merge_distribute(0, 1); } if !Arc::make_mut(&mut children.nodes_mut()[0]).zip_fix_left() { break; } } did_stuff } else { false } } /// Fixes dangling nodes down the right side of the tree. /// /// Returns whether it did anything or not that would affect the /// parent. True: did stuff, false: didn't do stuff pub fn zip_fix_right(&mut self) -> bool { if let Node::Internal(ref mut children) = *self { let mut did_stuff = false; loop { let last_i = children.len() - 1; let do_merge = (children.len() > 1) && match *children.nodes()[last_i] { Node::Leaf(ref text) => text.len() < MIN_BYTES, Node::Internal(ref children2) => children2.len() < MIN_CHILDREN, }; if do_merge { did_stuff |= children.merge_distribute(last_i - 1, last_i); } if !Arc::make_mut(children.nodes_mut().last_mut().unwrap()).zip_fix_right() { break; } } did_stuff } else { false } } /// Fixes up the tree after remove_char_range() or Rope::append(). /// /// Takes the char index of the start of the removal range. /// /// Returns whether it did anything or not that would affect the /// parent. True: did stuff, false: didn't do stuff pub fn fix_tree_seam(&mut self, char_idx: usize) -> bool { if let Node::Internal(ref mut children) = *self { let mut did_stuff = false; loop { // Do merging if children.len() > 1 { let (child_i, start_info) = children.search_char_idx(char_idx); let mut do_merge = match *children.nodes()[child_i] { Node::Leaf(ref text) => text.len() < MIN_BYTES, Node::Internal(ref children2) => children2.len() < MIN_CHILDREN, }; if child_i == 0 { if do_merge { did_stuff |= children.merge_distribute(0, 1); } } else { do_merge = do_merge || (start_info.chars as usize == char_idx && match *children.nodes()[child_i - 1] { Node::Leaf(ref text) => text.len() < MIN_BYTES, Node::Internal(ref children2) => children2.len() < MIN_CHILDREN, }); if do_merge { let res = children.merge_distribute(child_i - 1, child_i); did_stuff |= res } } } // Do recursion let (child_i, start_info) = children.search_char_idx(char_idx); if start_info.chars as usize == char_idx && child_i != 0 { let tmp = children.info()[child_i - 1].chars as usize; let effect_1 = Arc::make_mut(&mut children.nodes_mut()[child_i - 1]).fix_tree_seam(tmp); let effect_2 = Arc::make_mut(&mut children.nodes_mut()[child_i]).fix_tree_seam(0); if (!effect_1) && (!effect_2) { break; } } else if !Arc::make_mut(&mut children.nodes_mut()[child_i]) .fix_tree_seam(char_idx - start_info.chars as usize) { break; } } debug_assert!(children.is_info_accurate()); did_stuff } else { false } } } //=========================================================================== #[cfg(test)] mod tests { use crate::Rope; // 133 chars, 209 bytes const TEXT: &str = "\r\nHello there! How're you doing? It's a fine day, \ isn't it? Aren't you glad we're alive?\r\n\ こんにちは!元気ですか?日はいいですね。\ 私たちが生きだって嬉しいではないか?\r\n"; #[test] fn line_to_byte_01() { let r = Rope::from_str(TEXT); assert_eq!(3, r.root.line_break_count()); assert_eq!(0, r.line_to_byte(0)); assert_eq!(2, r.line_to_byte(1)); assert_eq!(93, r.line_to_byte(2)); assert_eq!(209, r.line_to_byte(3)); } #[test] fn line_to_char_01() { let r = Rope::from_str(TEXT); assert_eq!(3, r.root.line_break_count()); assert_eq!(0, r.line_to_char(0)); assert_eq!(2, r.line_to_char(1)); assert_eq!(93, r.line_to_char(2)); assert_eq!(133, r.line_to_char(3)); } #[test] fn crlf_corner_case_01() { use super::Node; use crate::tree::{NodeChildren, NodeText, MAX_BYTES}; use std::sync::Arc; // Construct the corner case let nodel = Node::Leaf(NodeText::from_str(&"\n".repeat(MAX_BYTES - 1))); let noder = Node::Leaf(NodeText::from_str(&"\n".repeat(MAX_BYTES))); let mut children = NodeChildren::new(); children.push((nodel.text_info(), Arc::new(nodel))); children.push((noder.text_info(), Arc::new(noder))); let root = Node::Internal(children); let mut rope = Rope { root: Arc::new(root), }; assert_eq!(rope.char(0), '\n'); assert_eq!(rope.len_chars(), MAX_BYTES * 2 - 1); // Do the potentially problematic insertion rope.insert(MAX_BYTES - 1, "\r"); } #[test] fn crlf_corner_case_02() { use super::Node; use crate::tree::{NodeChildren, NodeText, MAX_BYTES}; use std::sync::Arc; // Construct the corner case let nodel = Node::Leaf(NodeText::from_str(&"\r".repeat(MAX_BYTES))); let noder = Node::Leaf(NodeText::from_str(&"\r".repeat(MAX_BYTES - 1))); let mut children = NodeChildren::new(); children.push((nodel.text_info(), Arc::new(nodel))); children.push((noder.text_info(), Arc::new(noder))); let root = Node::Internal(children); let mut rope = Rope { root: Arc::new(root), }; assert_eq!(rope.char(0), '\r'); assert_eq!(rope.len_chars(), MAX_BYTES * 2 - 1); // Do the potentially problematic insertion rope.insert(MAX_BYTES, "\n"); } } ropey-1.6.1/src/tree/node_children.rs000064400000000000000000001045141046102023000156650ustar 00000000000000use std::fmt; use std::iter::{Iterator, Zip}; use std::slice; use std::sync::Arc; use crate::crlf; use crate::tree::{self, Node, TextInfo, MAX_BYTES}; const MAX_LEN: usize = tree::MAX_CHILDREN; /// A fixed-capacity vec of child Arc-pointers and child metadata. /// /// The unsafe guts of this are implemented in NodeChildrenInternal /// lower down in this file. #[derive(Clone)] #[repr(C)] pub(crate) struct NodeChildren(inner::NodeChildrenInternal); impl NodeChildren { /// Creates a new empty array. pub fn new() -> Self { NodeChildren(inner::NodeChildrenInternal::new()) } /// Current length of the array. pub fn len(&self) -> usize { self.0.len() as usize } /// Returns whether the array is full or not. pub fn is_full(&self) -> bool { self.len() == MAX_LEN } /// Access to the nodes array. pub fn nodes(&self) -> &[Arc] { self.0.nodes() } /// Mutable access to the nodes array. pub fn nodes_mut(&mut self) -> &mut [Arc] { self.0.nodes_mut() } /// Access to the info array. pub fn info(&self) -> &[TextInfo] { self.0.info() } /// Mutable access to the info array. pub fn info_mut(&mut self) -> &mut [TextInfo] { self.0.info_mut() } /// Mutable access to both the info and nodes arrays simultaneously. pub fn data_mut(&mut self) -> (&mut [TextInfo], &mut [Arc]) { self.0.data_mut() } /// Updates the text info of the child at `idx`. pub fn update_child_info(&mut self, idx: usize) { let (info, nodes) = self.0.data_mut(); info[idx] = nodes[idx].text_info(); } /// Pushes an item into the end of the array. /// /// Increases length by one. Panics if already full. pub fn push(&mut self, item: (TextInfo, Arc)) { self.0.push(item) } /// Pushes an element onto the end of the array, and then splits it in half, /// returning the right half. /// /// This works even when the array is full. pub fn push_split(&mut self, new_child: (TextInfo, Arc)) -> Self { let r_count = (self.len() + 1) / 2; let l_count = (self.len() + 1) - r_count; let mut right = self.split_off(l_count); right.push(new_child); right } /// Attempts to merge two nodes, and if it's too much data to merge /// equi-distributes it between the two. /// /// Returns: /// /// - True: merge was successful. /// - False: merge failed, equidistributed instead. pub fn merge_distribute(&mut self, idx1: usize, idx2: usize) -> bool { assert!(idx1 < idx2); assert!(idx2 < self.len()); let remove_right = { let ((_, node1), (_, node2)) = self.get_two_mut(idx1, idx2); let node1 = Arc::make_mut(node1); let node2 = Arc::make_mut(node2); match *node1 { Node::Leaf(ref mut text1) => { if let Node::Leaf(ref mut text2) = *node2 { if (text1.len() + text2.len()) <= tree::MAX_BYTES { text1.push_str(text2); true } else { let right = text1.push_str_split(text2); *text2 = right; false } } else { panic!("Siblings have different node types"); } } Node::Internal(ref mut children1) => { if let Node::Internal(ref mut children2) = *node2 { if (children1.len() + children2.len()) <= MAX_LEN { for _ in 0..children2.len() { children1.push(children2.remove(0)); } true } else { children1.distribute_with(children2); false } } else { panic!("Siblings have different node types"); } } } }; if remove_right { self.remove(idx2); self.update_child_info(idx1); return true; } else { self.update_child_info(idx1); self.update_child_info(idx2); return false; } } /// Equi-distributes the children between the two child arrays, /// preserving ordering. pub fn distribute_with(&mut self, other: &mut Self) { let r_target_len = (self.len() + other.len()) / 2; while other.len() < r_target_len { other.insert(0, self.pop()); } while other.len() > r_target_len { self.push(other.remove(0)); } } /// If the children are leaf nodes, compacts them to take up the fewest /// nodes. pub fn compact_leaves(&mut self) { if !self.nodes()[0].is_leaf() || self.len() < 2 { return; } let mut i = 1; while i < self.len() { if (self.nodes()[i - 1].leaf_text().len() + self.nodes()[i].leaf_text().len()) <= MAX_BYTES { // Scope to contain borrows { let ((_, node_l), (_, node_r)) = self.get_two_mut(i - 1, i); let text_l = Arc::make_mut(node_l).leaf_text_mut(); let text_r = node_r.leaf_text(); text_l.push_str(text_r); } self.remove(i); } else if self.nodes()[i - 1].leaf_text().len() < MAX_BYTES { // Scope to contain borrows { let ((_, node_l), (_, node_r)) = self.get_two_mut(i - 1, i); let text_l = Arc::make_mut(node_l).leaf_text_mut(); let text_r = Arc::make_mut(node_r).leaf_text_mut(); let split_idx_r = crlf::prev_break(MAX_BYTES - text_l.len(), text_r.as_bytes()); text_l.push_str(&text_r[..split_idx_r]); text_r.truncate_front(split_idx_r); } i += 1; } else { i += 1; } } for i in 0..self.len() { self.update_child_info(i); } } /// Pops an item off the end of the array and returns it. /// /// Decreases length by one. Panics if already empty. pub fn pop(&mut self) -> (TextInfo, Arc) { self.0.pop() } /// Inserts an item into the the array at the given index. /// /// Increases length by one. Panics if already full. Preserves ordering /// of the other items. pub fn insert(&mut self, idx: usize, item: (TextInfo, Arc)) { self.0.insert(idx, item) } /// Inserts an element into a the array, and then splits it in half, returning /// the right half. /// /// This works even when the array is full. pub fn insert_split(&mut self, idx: usize, item: (TextInfo, Arc)) -> Self { assert!(self.len() > 0); assert!(idx <= self.len()); let extra = if idx < self.len() { let extra = self.pop(); self.insert(idx, item); extra } else { item }; self.push_split(extra) } /// Removes the item at the given index from the the array. /// /// Decreases length by one. Preserves ordering of the other items. pub fn remove(&mut self, idx: usize) -> (TextInfo, Arc) { self.0.remove(idx) } /// Splits the array in two at `idx`, returning the right part of the split. /// /// TODO: implement this more efficiently. pub fn split_off(&mut self, idx: usize) -> Self { assert!(idx <= self.len()); let mut other = NodeChildren::new(); let count = self.len() - idx; for _ in 0..count { other.push(self.remove(idx)); } other } /// Fetches two children simultaneously, returning mutable references /// to their info and nodes. /// /// `idx1` must be less than `idx2`. pub fn get_two_mut( &mut self, idx1: usize, idx2: usize, ) -> ( (&mut TextInfo, &mut Arc), (&mut TextInfo, &mut Arc), ) { assert!(idx1 < idx2); assert!(idx2 < self.len()); let split_idx = idx1 + 1; let (info, nodes) = self.data_mut(); let (info1, info2) = info.split_at_mut(split_idx); let (nodes1, nodes2) = nodes.split_at_mut(split_idx); ( (&mut info1[idx1], &mut nodes1[idx1]), (&mut info2[idx2 - split_idx], &mut nodes2[idx2 - split_idx]), ) } /// Creates an iterator over the array's items. pub fn iter(&self) -> Zip, slice::Iter>> { Iterator::zip(self.info().iter(), self.nodes().iter()) } #[allow(clippy::needless_range_loop)] pub fn combined_info(&self) -> TextInfo { let info = self.info(); let mut acc = TextInfo::new(); // Doing this with an explicit loop is notably faster than // using an iterator in this case. for i in 0..info.len() { acc += info[i]; } acc } /// Returns the child index and left-side-accumulated text info of the /// first child that matches the given predicate. /// /// If no child matches the predicate, the last child is returned. #[inline(always)] pub fn search_by(&self, pred: F) -> (usize, TextInfo) where // (left-accumulated start info, left-accumulated end info) F: Fn(TextInfo, TextInfo) -> bool, { debug_assert!(self.len() > 0); let mut accum = TextInfo::new(); let mut idx = 0; for info in self.info()[0..(self.len() - 1)].iter() { let next_accum = accum + *info; if pred(accum, next_accum) { break; } accum = next_accum; idx += 1; } (idx, accum) } /// Returns the child index and left-side-accumulated text info of the /// child that contains the given byte. /// /// One-past-the end is valid, and will return the last child. pub fn search_byte_idx(&self, byte_idx: usize) -> (usize, TextInfo) { let (idx, accum) = self.search_by(|_, end| byte_idx < end.bytes as usize); debug_assert!( byte_idx <= (accum.bytes + self.info()[idx].bytes) as usize, "Index out of bounds." ); (idx, accum) } /// Returns the child index and left-side-accumulated text info of the /// child that contains the given char. /// /// One-past-the end is valid, and will return the last child. pub fn search_char_idx(&self, char_idx: usize) -> (usize, TextInfo) { let (idx, accum) = self.search_by(|_, end| char_idx < end.chars as usize); debug_assert!( char_idx <= (accum.chars + self.info()[idx].chars) as usize, "Index out of bounds." ); (idx, accum) } /// Returns the child index and left-side-accumulated text info of the /// child that contains the given utf16 code unit offset. /// /// One-past-the end is valid, and will return the last child. pub fn search_utf16_code_unit_idx(&self, utf16_idx: usize) -> (usize, TextInfo) { let (idx, accum) = self.search_by(|_, end| utf16_idx < (end.chars + end.utf16_surrogates) as usize); debug_assert!( utf16_idx <= (accum.chars + accum.utf16_surrogates + self.info()[idx].chars + self.info()[idx].utf16_surrogates) as usize, "Index out of bounds." ); (idx, accum) } /// Same as `search_char_idx()` above, except that it only calulates the /// left-side-accumulated _char_ index rather than the full text info. /// /// Return is (child_index, left_acc_char_index) /// /// One-past-the end is valid, and will return the last child. #[inline(always)] pub fn search_char_idx_only(&self, char_idx: usize) -> (usize, usize) { debug_assert!(self.len() > 0); let mut accum_char_idx = 0; let mut idx = 0; for info in self.info()[0..(self.len() - 1)].iter() { let next_accum = accum_char_idx + info.chars as usize; if char_idx < next_accum { break; } accum_char_idx = next_accum; idx += 1; } debug_assert!( char_idx <= (accum_char_idx + self.info()[idx].chars as usize) as usize, "Index out of bounds." ); (idx, accum_char_idx) } /// Returns the child index and left-side-accumulated text info of the /// child that contains the given line break. /// /// Beginning of the rope is considered index 0, although is not /// considered a line break for the returned left-side-accumulated /// text info. /// /// One-past-the end is valid, and will return the last child. pub fn search_line_break_idx(&self, line_break_idx: usize) -> (usize, TextInfo) { let (idx, accum) = self.search_by(|_, end| line_break_idx <= end.line_breaks as usize); debug_assert!( line_break_idx <= (accum.line_breaks + self.info()[idx].line_breaks + 1) as usize, "Index out of bounds." ); (idx, accum) } /// Returns the child indices at the start and end of the given char /// range, and returns their left-side-accumulated char indices as well. /// /// Return is: /// ( /// (left_node_index, left_acc_left_side_char_index), /// (right_node_index, right_acc_left_side_char_index), /// ) /// /// One-past-the end is valid, and corresponds to the last child. #[inline(always)] pub fn search_char_idx_range( &self, start_idx: usize, end_idx: usize, ) -> ((usize, usize), (usize, usize)) { debug_assert!(start_idx <= end_idx); debug_assert!(self.len() > 0); let mut accum_char_idx = 0; let mut idx = 0; // Find left child and info for info in self.info()[..(self.len() - 1)].iter() { let next_accum = accum_char_idx + info.chars as usize; if start_idx < next_accum { break; } accum_char_idx = next_accum; idx += 1; } let l_child_i = idx; let l_acc_info = accum_char_idx; // Find right child and info for info in self.info()[idx..(self.len() - 1)].iter() { let next_accum = accum_char_idx + info.chars as usize; if end_idx <= next_accum { break; } accum_char_idx = next_accum; idx += 1; } #[cfg(any(test, debug_assertions))] assert!( end_idx <= accum_char_idx + self.info()[idx].chars as usize, "Index out of bounds." ); ((l_child_i, l_acc_info), (idx, accum_char_idx)) } // Debug function, to help verify tree integrity pub fn is_info_accurate(&self) -> bool { for (info, node) in self.info().iter().zip(self.nodes().iter()) { if *info != node.text_info() { return false; } } true } } impl fmt::Debug for NodeChildren { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("NodeChildren") .field("len", &self.len()) .field("info", &&self.info()) .field("nodes", &&self.nodes()) .finish() } } //=========================================================================== /// The unsafe guts of NodeChildren, exposed through a safe API. /// /// Try to keep this as small as possible, and implement functionality on /// NodeChildren via the safe APIs whenever possible. /// /// It's split out this way because it was too easy to accidentally access the /// fixed size arrays directly, leading to memory-unsafety bugs when accidentally /// accessing elements that are semantically out of bounds. This happened once, /// and it was a pain to track down--as memory safety bugs often are. mod inner { use super::{Node, TextInfo, MAX_LEN}; use std::mem; use std::mem::MaybeUninit; use std::ptr; use std::sync::Arc; /// This is essentially a fixed-capacity, stack-allocated `Vec`. However, /// it actually containts _two_ arrays rather than just one, but which /// share a length. #[repr(C)] pub(crate) struct NodeChildrenInternal { /// An array of the child nodes. /// INVARIANT: The nodes from 0..len must be initialized nodes: [MaybeUninit>; MAX_LEN], /// An array of the child node text infos /// INVARIANT: The nodes from 0..len must be initialized info: [MaybeUninit; MAX_LEN], len: u8, } impl NodeChildrenInternal { /// Creates a new empty array. #[inline(always)] pub fn new() -> NodeChildrenInternal { // SAFETY: Uninit data is valid for arrays of MaybeUninit. // len is zero, so it's ok for all of them to be uninit NodeChildrenInternal { nodes: unsafe { MaybeUninit::uninit().assume_init() }, info: unsafe { MaybeUninit::uninit().assume_init() }, len: 0, } } /// Current length of the array. #[inline(always)] pub fn len(&self) -> usize { self.len as usize } /// Access to the nodes array. #[inline(always)] pub fn nodes(&self) -> &[Arc] { // SAFETY: MaybeUninit is layout compatible with T, and // the nodes from 0..len are guaranteed to be initialized unsafe { mem::transmute(&self.nodes[..(self.len())]) } } /// Mutable access to the nodes array. #[inline(always)] pub fn nodes_mut(&mut self) -> &mut [Arc] { // SAFETY: MaybeUninit is layout compatible with T, and // the nodes from 0..len are guaranteed to be initialized unsafe { mem::transmute(&mut self.nodes[..(self.len as usize)]) } } /// Access to the info array. #[inline(always)] pub fn info(&self) -> &[TextInfo] { // SAFETY: MaybeUninit is layout compatible with T, and // the info from 0..len are guaranteed to be initialized unsafe { mem::transmute(&self.info[..(self.len())]) } } /// Mutable access to the info array. #[inline(always)] pub fn info_mut(&mut self) -> &mut [TextInfo] { // SAFETY: MaybeUninit is layout compatible with T, and // the info from 0..len are guaranteed to be initialized unsafe { mem::transmute(&mut self.info[..(self.len as usize)]) } } /// Mutable access to both the info and nodes arrays simultaneously. #[inline(always)] pub fn data_mut(&mut self) -> (&mut [TextInfo], &mut [Arc]) { // SAFETY: MaybeUninit is layout compatible with T, and // the info from 0..len are guaranteed to be initialized ( unsafe { mem::transmute(&mut self.info[..(self.len as usize)]) }, unsafe { mem::transmute(&mut self.nodes[..(self.len as usize)]) }, ) } /// Pushes an item into the end of the array. /// /// Increases length by one. Panics if already full. #[inline(always)] pub fn push(&mut self, item: (TextInfo, Arc)) { assert!(self.len() < MAX_LEN); self.info[self.len()] = MaybeUninit::new(item.0); self.nodes[self.len as usize] = MaybeUninit::new(item.1); // We have just initialized both info and node and 0..=len, so we can increase it self.len += 1; } /// Pops an item off the end of the array and returns it. /// /// Decreases length by one. Panics if already empty. #[inline(always)] pub fn pop(&mut self) -> (TextInfo, Arc) { assert!(self.len() > 0); self.len -= 1; // SAFETY: before this, len was long enough to guarantee that both must be init // We just decreased the length, guaranteeing that the elements will never be read again (unsafe { self.info[self.len()].assume_init() }, unsafe { ptr::read(&self.nodes[self.len()]).assume_init() }) } /// Inserts an item into the the array at the given index. /// /// Increases length by one. Panics if already full. Preserves ordering /// of the other items. #[inline(always)] pub fn insert(&mut self, idx: usize, item: (TextInfo, Arc)) { assert!(idx <= self.len()); assert!(self.len() < MAX_LEN); let len = self.len(); // This unsafe code simply shifts the elements of the arrays over // to make space for the new inserted value. The `.info` array // shifting can be done with a safe call to `copy_within()`. // However, the `.nodes` array shift cannot, because of the // specific drop semantics needed for safety. unsafe { let ptr = self.nodes.as_mut_ptr(); ptr::copy(ptr.add(idx), ptr.add(idx + 1), len - idx); } self.info.copy_within(idx..len, idx + 1); // We have just made space for the two new elements, so insert them self.info[idx] = MaybeUninit::new(item.0); self.nodes[idx] = MaybeUninit::new(item.1); // Now that all elements from 0..=len are initialized, we can increase the length self.len += 1; } /// Removes the item at the given index from the the array. /// /// Decreases length by one. Preserves ordering of the other items. #[inline(always)] pub fn remove(&mut self, idx: usize) -> (TextInfo, Arc) { assert!(self.len() > 0); assert!(idx < self.len()); // Read out the elements, they must not be touched again. We copy the elements // after them into them, and decrease the length at the end let item = (unsafe { self.info[idx].assume_init() }, unsafe { ptr::read(&self.nodes[idx]).assume_init() }); let len = self.len(); // This unsafe code simply shifts the elements of the arrays over // to fill in the gap left by the removed element. The `.info` // array shifting can be done with a safe call to `copy_within()`. // However, the `.nodes` array shift cannot, because of the // specific drop semantics needed for safety. unsafe { let ptr = self.nodes.as_mut_ptr(); ptr::copy(ptr.add(idx + 1), ptr.add(idx), len - idx - 1); } self.info.copy_within((idx + 1)..len, idx); // Now that the gap is filled, decrease the length self.len -= 1; return item; } } impl Drop for NodeChildrenInternal { fn drop(&mut self) { // The `.nodes` array contains `MaybeUninit` wrappers, which need // to be manually dropped if valid. We drop only the valid ones // here. for node in &mut self.nodes[..self.len as usize] { unsafe { ptr::drop_in_place(node.as_mut_ptr()) }; } } } impl Clone for NodeChildrenInternal { fn clone(&self) -> NodeChildrenInternal { // Create an empty NodeChildrenInternal first, then fill it let mut clone_array = NodeChildrenInternal::new(); // Copy nodes... carefully. for (clone_arc, arc) in Iterator::zip( clone_array.nodes[..self.len()].iter_mut(), self.nodes[..self.len()].iter(), ) { *clone_arc = MaybeUninit::new(Arc::clone(unsafe { &*arc.as_ptr() })); } // Copy TextInfo for (clone_info, info) in Iterator::zip( clone_array.info[..self.len()].iter_mut(), self.info[..self.len()].iter(), ) { *clone_info = *info; } // Set length clone_array.len = self.len; // Some sanity checks for debug builds #[cfg(debug_assertions)] { for (a, b) in Iterator::zip( (&clone_array.info[..clone_array.len()]).iter(), (&self.info[..self.len()]).iter(), ) { assert_eq!(unsafe { a.assume_init() }, unsafe { b.assume_init() },); } for (a, b) in Iterator::zip( (&clone_array.nodes[..clone_array.len()]).iter(), (&self.nodes[..clone_array.len()]).iter(), ) { assert!(Arc::ptr_eq(unsafe { &*a.as_ptr() }, unsafe { &*b.as_ptr() },)); } } clone_array } } } //=========================================================================== #[cfg(test)] mod tests { use super::*; use crate::tree::{Node, NodeText, TextInfo}; use std::sync::Arc; #[test] fn search_char_idx_01() { let mut children = NodeChildren::new(); children.push(( TextInfo::new(), Arc::new(Node::Leaf(NodeText::from_str("Hello "))), )); children.push(( TextInfo::new(), Arc::new(Node::Leaf(NodeText::from_str("there "))), )); children.push(( TextInfo::new(), Arc::new(Node::Leaf(NodeText::from_str("world!"))), )); children.update_child_info(0); children.update_child_info(1); children.update_child_info(2); assert_eq!(0, children.search_char_idx(0).0); assert_eq!(0, children.search_char_idx(1).0); assert_eq!(0, children.search_char_idx(0).1.chars); assert_eq!(0, children.search_char_idx(1).1.chars); assert_eq!(0, children.search_char_idx(5).0); assert_eq!(1, children.search_char_idx(6).0); assert_eq!(0, children.search_char_idx(5).1.chars); assert_eq!(6, children.search_char_idx(6).1.chars); assert_eq!(1, children.search_char_idx(11).0); assert_eq!(2, children.search_char_idx(12).0); assert_eq!(6, children.search_char_idx(11).1.chars); assert_eq!(12, children.search_char_idx(12).1.chars); assert_eq!(2, children.search_char_idx(17).0); assert_eq!(2, children.search_char_idx(18).0); assert_eq!(12, children.search_char_idx(17).1.chars); assert_eq!(12, children.search_char_idx(18).1.chars); } #[test] #[should_panic] #[cfg(debug_assertions)] fn search_char_idx_02() { let mut children = NodeChildren::new(); children.push(( TextInfo::new(), Arc::new(Node::Leaf(NodeText::from_str("Hello "))), )); children.push(( TextInfo::new(), Arc::new(Node::Leaf(NodeText::from_str("there "))), )); children.push(( TextInfo::new(), Arc::new(Node::Leaf(NodeText::from_str("world!"))), )); children.update_child_info(0); children.update_child_info(1); children.update_child_info(2); children.search_char_idx(19); } #[test] fn search_char_idx_range_01() { let mut children = NodeChildren::new(); children.push(( TextInfo::new(), Arc::new(Node::Leaf(NodeText::from_str("Hello "))), )); children.push(( TextInfo::new(), Arc::new(Node::Leaf(NodeText::from_str("there "))), )); children.push(( TextInfo::new(), Arc::new(Node::Leaf(NodeText::from_str("world!"))), )); children.update_child_info(0); children.update_child_info(1); children.update_child_info(2); let at_0_0 = children.search_char_idx_range(0, 0); let at_6_6 = children.search_char_idx_range(6, 6); let at_12_12 = children.search_char_idx_range(12, 12); let at_18_18 = children.search_char_idx_range(18, 18); assert_eq!(0, (at_0_0.0).0); assert_eq!(0, (at_0_0.1).0); assert_eq!(0, (at_0_0.0).1); assert_eq!(0, (at_0_0.1).1); assert_eq!(1, (at_6_6.0).0); assert_eq!(1, (at_6_6.1).0); assert_eq!(6, (at_6_6.0).1); assert_eq!(6, (at_6_6.1).1); assert_eq!(2, (at_12_12.0).0); assert_eq!(2, (at_12_12.1).0); assert_eq!(12, (at_12_12.0).1); assert_eq!(12, (at_12_12.1).1); assert_eq!(2, (at_18_18.0).0); assert_eq!(2, (at_18_18.1).0); assert_eq!(12, (at_18_18.0).1); assert_eq!(12, (at_18_18.1).1); let at_0_6 = children.search_char_idx_range(0, 6); let at_6_12 = children.search_char_idx_range(6, 12); let at_12_18 = children.search_char_idx_range(12, 18); assert_eq!(0, (at_0_6.0).0); assert_eq!(0, (at_0_6.1).0); assert_eq!(0, (at_0_6.0).1); assert_eq!(0, (at_0_6.1).1); assert_eq!(1, (at_6_12.0).0); assert_eq!(1, (at_6_12.1).0); assert_eq!(6, (at_6_12.0).1); assert_eq!(6, (at_6_12.1).1); assert_eq!(2, (at_12_18.0).0); assert_eq!(2, (at_12_18.1).0); assert_eq!(12, (at_12_18.0).1); assert_eq!(12, (at_12_18.1).1); let at_5_7 = children.search_char_idx_range(5, 7); let at_11_13 = children.search_char_idx_range(11, 13); assert_eq!(0, (at_5_7.0).0); assert_eq!(1, (at_5_7.1).0); assert_eq!(0, (at_5_7.0).1); assert_eq!(6, (at_5_7.1).1); assert_eq!(1, (at_11_13.0).0); assert_eq!(2, (at_11_13.1).0); assert_eq!(6, (at_11_13.0).1); assert_eq!(12, (at_11_13.1).1); } #[test] #[should_panic] fn search_char_idx_range_02() { let mut children = NodeChildren::new(); children.push(( TextInfo::new(), Arc::new(Node::Leaf(NodeText::from_str("Hello "))), )); children.push(( TextInfo::new(), Arc::new(Node::Leaf(NodeText::from_str("there "))), )); children.push(( TextInfo::new(), Arc::new(Node::Leaf(NodeText::from_str("world!"))), )); children.update_child_info(0); children.update_child_info(1); children.update_child_info(2); children.search_char_idx_range(18, 19); } #[test] fn search_line_break_idx_01() { let mut children = NodeChildren::new(); children.push(( TextInfo::new(), Arc::new(Node::Leaf(NodeText::from_str("Hello\n"))), )); children.push(( TextInfo::new(), Arc::new(Node::Leaf(NodeText::from_str("\nthere\n"))), )); children.push(( TextInfo::new(), Arc::new(Node::Leaf(NodeText::from_str("world!\n"))), )); children.update_child_info(0); children.update_child_info(1); children.update_child_info(2); assert_eq!(0, children.search_line_break_idx(0).0); assert_eq!(0, children.search_line_break_idx(0).1.line_breaks); assert_eq!(0, children.search_line_break_idx(1).0); assert_eq!(0, children.search_line_break_idx(1).1.line_breaks); assert_eq!(1, children.search_line_break_idx(2).0); assert_eq!(1, children.search_line_break_idx(2).1.line_breaks); assert_eq!(1, children.search_line_break_idx(3).0); assert_eq!(1, children.search_line_break_idx(3).1.line_breaks); assert_eq!(2, children.search_line_break_idx(4).0); assert_eq!(3, children.search_line_break_idx(4).1.line_breaks); assert_eq!(2, children.search_line_break_idx(5).0); assert_eq!(3, children.search_line_break_idx(5).1.line_breaks); } #[test] fn search_line_break_idx_02() { let mut children = NodeChildren::new(); children.push(( TextInfo::new(), Arc::new(Node::Leaf(NodeText::from_str("Hello\n"))), )); children.push(( TextInfo::new(), Arc::new(Node::Leaf(NodeText::from_str("there"))), )); children.push(( TextInfo::new(), Arc::new(Node::Leaf(NodeText::from_str("world!"))), )); children.update_child_info(0); children.update_child_info(1); children.update_child_info(2); assert_eq!(0, children.search_line_break_idx(0).0); assert_eq!(0, children.search_line_break_idx(0).1.line_breaks); assert_eq!(0, children.search_line_break_idx(1).0); assert_eq!(0, children.search_line_break_idx(1).1.line_breaks); assert_eq!(2, children.search_line_break_idx(2).0); assert_eq!(1, children.search_line_break_idx(2).1.line_breaks); } #[test] fn search_line_break_idx_03() { let mut children = NodeChildren::new(); children.push(( TextInfo::new(), Arc::new(Node::Leaf(NodeText::from_str(""))), )); children.update_child_info(0); assert_eq!(0, children.search_line_break_idx(0).0); assert_eq!(0, children.search_line_break_idx(0).1.line_breaks); assert_eq!(0, children.search_line_break_idx(1).0); assert_eq!(0, children.search_line_break_idx(1).1.line_breaks); } #[test] #[should_panic] #[cfg(debug_assertions)] fn search_line_break_idx_04() { let mut children = NodeChildren::new(); children.push(( TextInfo::new(), Arc::new(Node::Leaf(NodeText::from_str(""))), )); children.update_child_info(0); assert_eq!(0, children.search_line_break_idx(0).0); assert_eq!(0, children.search_line_break_idx(0).1.line_breaks); assert_eq!(0, children.search_line_break_idx(1).0); assert_eq!(0, children.search_line_break_idx(1).1.line_breaks); assert_eq!(0, children.search_line_break_idx(2).0); assert_eq!(0, children.search_line_break_idx(2).1.line_breaks); } } ropey-1.6.1/src/tree/node_text.rs000064400000000000000000000341351046102023000150620ustar 00000000000000use std::borrow::Borrow; use std::ops::Deref; use std::str; use crate::crlf; /// A custom small string. The unsafe guts of this are in `NodeSmallString` /// further down in this file. #[derive(Clone, Default)] #[repr(C)] pub(crate) struct NodeText(inner::NodeSmallString); impl NodeText { /// Creates a new empty `NodeText` #[inline(always)] pub fn new() -> Self { NodeText(inner::NodeSmallString::new()) } /// Creates a new `NodeText` with the same contents as the given `&str`. pub fn from_str(string: &str) -> Self { NodeText(inner::NodeSmallString::from_str(string)) } /// Inserts a `&str` at byte offset `byte_idx`. pub fn insert_str(&mut self, byte_idx: usize, string: &str) { self.0.insert_str(byte_idx, string); } /// Inserts `string` at `byte_idx` and splits the resulting string in half, /// returning the right half. /// /// Only splits on code point boundaries and will never split CRLF pairs, /// so if the whole string is a single code point or CRLF pair, the split /// will fail and the returned string will be empty. pub fn insert_str_split(&mut self, byte_idx: usize, string: &str) -> Self { debug_assert!(self.is_char_boundary(byte_idx)); let tot_len = self.len() + string.len(); let mid_idx = tot_len / 2; let a = byte_idx; let b = byte_idx + string.len(); // Figure out the split index, accounting for code point // boundaries and CRLF pairs. // We first copy the bytes in the area of the proposed split point into // a small 8-byte buffer. We then use that buffer to look for the // real split point. let split_idx = { let mut buf = [0u8; 8]; let start = mid_idx - 4.min(mid_idx); let end = (mid_idx + 4).min(tot_len); for i in start..end { buf[i - start] = if i < a { self.as_bytes()[i] } else if i < b { string.as_bytes()[i - a] } else { self.as_bytes()[i - string.len()] }; } crlf::nearest_internal_break(mid_idx - start, &buf[..(end - start)]) + start }; let mut right = NodeText::new(); if split_idx <= a { right.push_str(&self[split_idx..a]); right.push_str(string); right.push_str(&self[a..]); self.truncate(split_idx); } else if split_idx <= b { right.push_str(&string[(split_idx - a)..]); right.push_str(&self[a..]); self.truncate(a); self.push_str(&string[..(split_idx - a)]); } else { right.push_str(&self[(split_idx - string.len())..]); self.truncate(split_idx - string.len()); self.insert_str(a, string); } self.0.inline_if_possible(); right } /// Appends a `&str` to end the of the `NodeText`. pub fn push_str(&mut self, string: &str) { let len = self.len(); self.0.insert_str(len, string); } /// Appends a `&str` and splits the resulting string in half, returning /// the right half. /// /// Only splits on code point boundaries and will never split CRLF pairs, /// so if the whole string is a single code point or CRLF pair, the split /// will fail and the returned string will be empty. pub fn push_str_split(&mut self, string: &str) -> Self { let len = self.len(); self.insert_str_split(len, string) } /// Drops the text after byte index `byte_idx`. pub fn truncate(&mut self, byte_idx: usize) { self.0.truncate(byte_idx); self.0.inline_if_possible(); } /// Drops the text before byte index `byte_idx`, shifting the /// rest of the text to fill in the space. pub fn truncate_front(&mut self, byte_idx: usize) { self.0.remove_range(0, byte_idx); self.0.inline_if_possible(); } /// Removes the text in the byte index interval `[byte_start, byte_end)`. pub fn remove_range(&mut self, byte_start: usize, byte_end: usize) { self.0.remove_range(byte_start, byte_end); self.0.inline_if_possible(); } /// Splits the `NodeText` at `byte_idx`. /// /// The left part remains in the original, and the right part is /// returned in a new `NodeText`. pub fn split_off(&mut self, byte_idx: usize) -> Self { let other = NodeText(self.0.split_off(byte_idx)); self.0.inline_if_possible(); other } } impl std::cmp::PartialEq for NodeText { fn eq(&self, other: &Self) -> bool { let (s1, s2): (&str, &str) = (self, other); s1 == s2 } } impl<'a> PartialEq for &'a str { fn eq(&self, other: &NodeText) -> bool { *self == (other as &str) } } impl<'a> PartialEq<&'a str> for NodeText { fn eq(&self, other: &&'a str) -> bool { (self as &str) == *other } } impl std::fmt::Display for NodeText { fn fmt(&self, fm: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { NodeText::deref(self).fmt(fm) } } impl std::fmt::Debug for NodeText { fn fmt(&self, fm: &mut std::fmt::Formatter) -> std::fmt::Result { NodeText::deref(self).fmt(fm) } } impl<'a> From<&'a str> for NodeText { fn from(s: &str) -> Self { Self::from_str(s) } } impl Deref for NodeText { type Target = str; fn deref(&self) -> &str { self.0.as_str() } } impl AsRef for NodeText { fn as_ref(&self) -> &str { self.0.as_str() } } impl Borrow for NodeText { fn borrow(&self) -> &str { self.0.as_str() } } //======================================================================= /// Takes two `NodeText`s and mends the CRLF break between them, if any. /// /// Note: this will leave one of the strings empty if the entire composite string /// is a single CRLF pair. pub(crate) fn fix_segment_seam(l: &mut NodeText, r: &mut NodeText) { // Early out, if there's nothing to do. if crlf::seam_is_break(l.as_bytes(), r.as_bytes()) { return; } let tot_len = l.len() + r.len(); // Find the new split position, if any. let new_split_pos = { let l_split = crlf::prev_break(l.len(), l.as_bytes()); let r_split = l.len() + crlf::next_break(0, r.as_bytes()); if l_split != 0 && (r_split == tot_len || l.len() > r.len()) { l_split } else { r_split } }; // Move the bytes to create the new split if new_split_pos < l.len() { r.insert_str(0, &l[new_split_pos..]); l.truncate(new_split_pos); } else { let pos = new_split_pos - l.len(); l.push_str(&r[..pos]); r.truncate_front(pos); } } //======================================================================= /// The unsafe guts of NodeText, exposed through a safe API. /// /// Try to keep this as small as possible, and implement functionality on /// NodeText via the safe APIs whenever possible. mod inner { use crate::tree::MAX_BYTES; use smallvec::{Array, SmallVec}; use std::str; /// The backing internal buffer type for `NodeText`. #[derive(Copy, Clone)] struct BackingArray([u8; MAX_BYTES]); /// We need a very specific size of array, which is not necessarily /// supported directly by the impls in the smallvec crate. We therefore /// have to implement this unsafe trait for our specific array size. /// TODO: once integer const generics land, and smallvec updates its APIs /// to use them, switch over and get rid of this unsafe impl. unsafe impl Array for BackingArray { type Item = u8; fn size() -> usize { MAX_BYTES } } /// Internal small string for `NodeText`. #[derive(Clone, Default)] #[repr(C)] pub struct NodeSmallString { buffer: SmallVec, } impl NodeSmallString { #[inline(always)] pub fn new() -> Self { NodeSmallString { buffer: SmallVec::new(), } } #[inline(always)] pub fn with_capacity(capacity: usize) -> Self { NodeSmallString { buffer: SmallVec::with_capacity(capacity), } } #[inline(always)] pub fn from_str(string: &str) -> Self { let mut nodetext = NodeSmallString::with_capacity(string.len()); nodetext.insert_str(0, string); nodetext } #[inline(always)] pub fn len(&self) -> usize { self.buffer.len() } #[inline(always)] pub fn as_str(&self) -> &str { // NodeSmallString's methods don't allow `buffer` to become invalid // utf8, so this is safe. unsafe { str::from_utf8_unchecked(self.buffer.as_ref()) } } /// Inserts `string` at `byte_idx`. /// /// Panics on out-of-bounds or of `byte_idx` isn't a char boundary. #[inline(always)] pub fn insert_str(&mut self, byte_idx: usize, string: &str) { assert!(self.as_str().is_char_boundary(byte_idx)); // Copy bytes from `string` into the appropriate space in the // buffer. self.buffer.insert_from_slice(byte_idx, string.as_bytes()); } /// Removes text in range `[start_byte_idx, end_byte_idx)` /// /// Panics on out-of-bounds or non-char-boundary indices. #[inline(always)] pub fn remove_range(&mut self, start_byte_idx: usize, end_byte_idx: usize) { assert!(start_byte_idx <= end_byte_idx); // Already checked by copy_within/is_char_boundary. debug_assert!(end_byte_idx <= self.len()); assert!(self.as_str().is_char_boundary(start_byte_idx)); assert!(self.as_str().is_char_boundary(end_byte_idx)); let len = self.len(); let amt = end_byte_idx - start_byte_idx; self.buffer.copy_within(end_byte_idx..len, start_byte_idx); self.buffer.truncate(len - amt); } /// Removes text after `byte_idx`. #[inline(always)] pub fn truncate(&mut self, byte_idx: usize) { // Already checked by is_char_boundary. debug_assert!(byte_idx <= self.len()); assert!(self.as_str().is_char_boundary(byte_idx)); self.buffer.truncate(byte_idx); } /// Splits at `byte_idx`, returning the right part and leaving the /// left part in the original. /// /// Panics on out-of-bounds or of `byte_idx` isn't a char boundary. #[inline(always)] pub fn split_off(&mut self, byte_idx: usize) -> Self { // Already checked by is_char_boundary. debug_assert!(byte_idx <= self.len()); assert!(self.as_str().is_char_boundary(byte_idx)); let len = self.len(); let mut other = NodeSmallString::with_capacity(len - byte_idx); other.buffer.extend_from_slice(&self.buffer[byte_idx..]); self.buffer.truncate(byte_idx); other } /// Re-inlines the data if it's been heap allocated but can /// fit inline. #[inline(always)] pub fn inline_if_possible(&mut self) { if self.buffer.spilled() && (self.buffer.len() <= self.buffer.inline_size()) { self.buffer.shrink_to_fit(); } } } //----------------------------------------------------------------------- #[cfg(test)] mod tests { use super::*; #[test] fn small_string_basics() { let s = NodeSmallString::from_str("Hello!"); assert_eq!("Hello!", s.as_str()); assert_eq!(6, s.len()); } #[test] fn insert_str_01() { let mut s = NodeSmallString::from_str("Hello!"); s.insert_str(3, "oz"); assert_eq!("Helozlo!", s.as_str()); } #[test] #[should_panic] fn insert_str_02() { let mut s = NodeSmallString::from_str("Hello!"); s.insert_str(7, "oz"); } #[test] #[should_panic] fn insert_str_03() { let mut s = NodeSmallString::from_str("こんにちは"); s.insert_str(4, "oz"); } #[test] fn remove_range_01() { let mut s = NodeSmallString::from_str("Hello!"); s.remove_range(2, 4); assert_eq!("Heo!", s.as_str()); } #[test] #[should_panic] fn remove_range_02() { let mut s = NodeSmallString::from_str("Hello!"); s.remove_range(4, 2); } #[test] #[should_panic] fn remove_range_03() { let mut s = NodeSmallString::from_str("Hello!"); s.remove_range(2, 7); } #[test] #[should_panic] fn remove_range_04() { let mut s = NodeSmallString::from_str("こんにちは"); s.remove_range(2, 4); } #[test] fn truncate_01() { let mut s = NodeSmallString::from_str("Hello!"); s.truncate(4); assert_eq!("Hell", s.as_str()); } #[test] #[should_panic] fn truncate_02() { let mut s = NodeSmallString::from_str("Hello!"); s.truncate(7); } #[test] #[should_panic] fn truncate_03() { let mut s = NodeSmallString::from_str("こんにちは"); s.truncate(4); } #[test] fn split_off_01() { let mut s1 = NodeSmallString::from_str("Hello!"); let s2 = s1.split_off(4); assert_eq!("Hell", s1.as_str()); assert_eq!("o!", s2.as_str()); } #[test] #[should_panic] fn split_off_02() { let mut s1 = NodeSmallString::from_str("Hello!"); s1.split_off(7); } #[test] #[should_panic] fn split_off_03() { let mut s1 = NodeSmallString::from_str("こんにちは"); s1.split_off(4); } } } ropey-1.6.1/src/tree/text_info.rs000064400000000000000000000035151046102023000150660ustar 00000000000000use std::ops::{Add, AddAssign, Sub, SubAssign}; use crate::str_utils::{count_chars, count_line_breaks, count_utf16_surrogates}; use crate::tree::Count; #[derive(Debug, Copy, Clone, PartialEq)] pub struct TextInfo { pub(crate) bytes: Count, pub(crate) chars: Count, pub(crate) utf16_surrogates: Count, pub(crate) line_breaks: Count, } impl TextInfo { #[inline] pub fn new() -> TextInfo { TextInfo { bytes: 0, chars: 0, utf16_surrogates: 0, line_breaks: 0, } } #[inline] pub fn from_str(text: &str) -> TextInfo { TextInfo { bytes: text.len() as Count, chars: count_chars(text) as Count, utf16_surrogates: count_utf16_surrogates(text) as Count, line_breaks: count_line_breaks(text) as Count, } } } impl Add for TextInfo { type Output = Self; #[inline] fn add(self, rhs: TextInfo) -> TextInfo { TextInfo { bytes: self.bytes + rhs.bytes, chars: self.chars + rhs.chars, utf16_surrogates: self.utf16_surrogates + rhs.utf16_surrogates, line_breaks: self.line_breaks + rhs.line_breaks, } } } impl AddAssign for TextInfo { #[inline] fn add_assign(&mut self, other: TextInfo) { *self = *self + other; } } impl Sub for TextInfo { type Output = Self; #[inline] fn sub(self, rhs: TextInfo) -> TextInfo { TextInfo { bytes: self.bytes - rhs.bytes, chars: self.chars - rhs.chars, utf16_surrogates: self.utf16_surrogates - rhs.utf16_surrogates, line_breaks: self.line_breaks - rhs.line_breaks, } } } impl SubAssign for TextInfo { #[inline] fn sub_assign(&mut self, other: TextInfo) { *self = *self - other; } } ropey-1.6.1/tests/clone_rope.rs000064400000000000000000000020701046102023000146230ustar 00000000000000extern crate ropey; use std::iter::Iterator; use ropey::Rope; const TEXT: &str = include_str!("test_text.txt"); #[test] #[cfg_attr(miri, ignore)] fn clone_rope() { let mut rope1 = Rope::from_str(TEXT); let mut rope2 = rope1.clone(); // Do identical insertions into both ropes rope1.insert(432, "Hello "); rope1.insert(2345, "world! "); rope1.insert(5256, "How are "); rope1.insert(53, "you "); rope1.insert(768, "doing?\r\n"); rope2.insert(432, "Hello "); rope2.insert(2345, "world! "); rope2.insert(5256, "How are "); rope2.insert(53, "you "); rope2.insert(768, "doing?\r\n"); // Make sure they match let matches = Iterator::zip(rope1.chars(), rope2.chars()) .map(|(a, b)| a == b) .all(|n| n); assert!(matches); // Insert something into the clone, and make sure they don't match // afterwards. rope2.insert(3891, "I'm doing fine, thanks!"); let matches = Iterator::zip(rope1.chars(), rope2.chars()) .map(|(a, b)| a == b) .all(|n| n); assert!(!matches); } ropey-1.6.1/tests/clone_rope_to_thread.rs000064400000000000000000000035011046102023000166540ustar 00000000000000extern crate ropey; use std::sync::mpsc; use std::thread; use std::iter::Iterator; use ropey::Rope; const TEXT: &str = include_str!("test_text.txt"); #[test] #[cfg_attr(miri, ignore)] fn clone_rope_to_thread() { let mut rope1 = Rope::from_str(TEXT); let rope2 = rope1.clone(); // Spawn a thread for modifying the clone let (tx1, rx1) = mpsc::channel::(); let (tx2, rx2) = mpsc::channel::(); thread::spawn(move || { // Modify rope2 let mut rope = rx1.recv().unwrap(); rope.insert(432, "Hello "); rope.insert(2345, "world! "); rope.insert(5256, "How are "); rope.insert(53, "you "); rope.insert(768, "doing?\r\n"); // Send it back tx2.send(rope).unwrap(); // Modify it again let mut rope = rx1.recv().unwrap(); rope.insert(3891, "I'm doing fine, thanks!"); tx2.send(rope).unwrap(); }); // Send the clone to the other thread for modification tx1.send(rope2).unwrap(); // Make identical modifications to rope1 as are being made // to rope2 in the other thread. rope1.insert(432, "Hello "); rope1.insert(2345, "world! "); rope1.insert(5256, "How are "); rope1.insert(53, "you "); rope1.insert(768, "doing?\r\n"); // Get rope2 back and make sure they match let rope2 = rx2.recv().unwrap(); let matches = Iterator::zip(rope1.chars(), rope2.chars()) .map(|(a, b)| a == b) .all(|n| n); assert!(matches); // Send rope2 to the other thread again for more modifications. tx1.send(rope2).unwrap(); // Get rope2 back again and make sure they don't match now. let rope2 = rx2.recv().unwrap(); let matches = Iterator::zip(rope1.chars(), rope2.chars()) .map(|(a, b)| a == b) .all(|n| n); assert!(!matches); } ropey-1.6.1/tests/crlf.rs000064400000000000000000000047701046102023000134350ustar 00000000000000//! Randomized tests to try to catch crlf seam errors. extern crate rand; extern crate ropey; use rand::Rng; use ropey::Rope; #[test] #[cfg_attr(miri, ignore)] fn crlf_inserts() { let mut rng = rand::thread_rng(); let mut tree = Rope::new(); // Do a bunch of random incoherent inserts of CRLF // pairs. for _ in 0..(1 << 12) { let len = tree.len_chars().max(1); tree.insert(rng.gen::() % len, "\r\n\r\n"); tree.insert(rng.gen::() % len, "\n\r\n\r"); tree.insert(rng.gen::() % len, "\r\n\r\n"); tree.insert(rng.gen::() % len, "\n\r\n\r"); tree.insert(rng.gen::() % len, "\r\n\r\n"); tree.insert(rng.gen::() % len, "こんいちは、"); tree.insert(rng.gen::() % len, "\n\r\n\r"); tree.insert(rng.gen::() % len, "\r\n\r\n"); tree.insert(rng.gen::() % len, "\n\r\n\r"); tree.insert(rng.gen::() % len, "\r\n\r\n"); tree.insert(rng.gen::() % len, "\n\r\n\r"); tree.insert(rng.gen::() % len, "みんなさん!"); // Make sure the tree is sound tree.assert_invariants(); } } #[test] #[cfg_attr(miri, ignore)] fn crlf_removals() { let mut rng = rand::thread_rng(); let mut tree = Rope::new(); // Build tree. for _ in 0..(1 << 9) { let len = tree.len_chars().max(1); tree.insert(rng.gen::() % len, "\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nこんいちは、\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nこんいちは、r\n\r\n\r\n\r\nみんなさん!\n\r\n\r\n\r\nこんいちは、\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nみんなさん!\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\r\n\r\n\r\n\r\n\r\n\r\nみんなさん!\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\rみんなさん!\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r"); } // Do a bunch of random incoherent removals for _ in 0..(1 << 11) { let start = rng.gen::() % tree.len_chars().max(1); let end = (start + 5).min(tree.len_chars()); tree.remove(start..end); let start = rng.gen::() % tree.len_chars().max(1); let end = (start + 9).min(tree.len_chars()); tree.remove(start..end); // Make sure the tree is sound tree.assert_invariants(); } } ropey-1.6.1/tests/fix_tree.rs000064400000000000000000000006261046102023000143100ustar 00000000000000extern crate ropey; use ropey::Rope; const MEDIUM_TEXT: &str = include_str!("medium.txt"); #[test] #[cfg_attr(miri, ignore)] fn remove_at_chunk_boundery() { let mut r = Rope::from_str(MEDIUM_TEXT); // remove exactly at a chunk boundry // to trigger an edgecase in fix_tree_seam r.remove(31354..58881); // Verify rope integrity r.assert_integrity(); r.assert_invariants(); } ropey-1.6.1/tests/from_reader.rs000064400000000000000000000025241046102023000147670ustar 00000000000000extern crate rand; extern crate ropey; use std::io::Cursor; use ropey::Rope; const TEXT: &str = include_str!("test_text.txt"); #[test] #[cfg_attr(miri, ignore)] fn from_reader_01() { // Make a reader from our in-memory text let text_reader = Cursor::new(TEXT); let rope = Rope::from_reader(text_reader).unwrap(); assert_eq!(rope, TEXT); // Make sure the tree is sound rope.assert_integrity(); rope.assert_invariants(); } #[test] #[cfg_attr(miri, ignore)] fn from_reader_02() { // Make a reader from blank text let text_reader = Cursor::new(""); let rope = Rope::from_reader(text_reader).unwrap(); assert_eq!(rope, ""); // Make sure the tree is sound rope.assert_integrity(); rope.assert_invariants(); } #[test] #[cfg_attr(miri, ignore)] fn from_reader_03() { // Make text with a utf8-invalid byte sequence in it. let mut text = Vec::new(); text.extend(TEXT.as_bytes()); text[6132] = 0b1100_0000; text[6133] = 0b0100_0000; // Make a reader from the invalid data let text_reader = Cursor::new(text); // Try to read the data, and verify that we get the right error. if let Err(e) = Rope::from_reader(text_reader) { assert_eq!(e.kind(), std::io::ErrorKind::InvalidData); } else { panic!("Should have returned an invalid data error.") } } ropey-1.6.1/tests/from_str.rs000064400000000000000000000005541046102023000143360ustar 00000000000000extern crate ropey; use ropey::Rope; const TEXT: &str = include_str!("test_text.txt"); #[test] #[cfg_attr(miri, ignore)] fn from_str() { // Build rope from file contents let rope = Rope::from_str(TEXT); // Verify rope integrity rope.assert_integrity(); rope.assert_invariants(); // Verify that they match assert_eq!(rope, TEXT); } ropey-1.6.1/tests/hash.rs000064400000000000000000000072451046102023000134320ustar 00000000000000extern crate ropey; use std::hash::{Hash, Hasher}; use ropey::RopeBuilder; const SMALL_TEXT: &str = include_str!("small_ascii.txt"); /// This is an example `Hasher` to demonstrate a property guaranteed by /// the documentation that is not exploited by the default `Hasher` (SipHash) /// Relevant excerpt from the `Hasher` documentation: /// > Nor can you assume that adjacent /// > `write` calls are merged, so it's possible, for example, that /// > ``` /// > # fn foo(hasher: &mut impl std::hash::Hasher) { /// > hasher.write(&[1, 2]); /// > hasher.write(&[3, 4, 5, 6]); /// > # } /// > ``` /// > and /// > ``` /// > # fn foo(hasher: &mut impl std::hash::Hasher) { /// > hasher.write(&[1, 2, 3, 4]); /// > hasher.write(&[5, 6]); /// > # } /// > ``` /// > end up producing different hashes. /// /// This dummy hasher simply collects all bytes and inserts a separator byte (0xFF) at the end of `write`. /// While this hasher might seem a little silly, it is perfectly inline with the std documentation. /// Many other commonly used high performance `Hasher`s (fxhash, ahash, fnvhash) exploit the same property /// to improve the performance of `write`, so violating this property will cause issues in practice. #[derive(Default)] struct TestHasher(std::collections::hash_map::DefaultHasher); impl Hasher for TestHasher { fn finish(&self) -> u64 { self.0.finish() } fn write(&mut self, bytes: &[u8]) { self.0.write(bytes); self.0.write_u8(0xFF); } } #[test] #[cfg_attr(miri, ignore)] fn hash_1() { // Build two ropes with the same contents but different chunk boundaries. let r1 = { let mut b = RopeBuilder::new(); b._append_chunk("Hello w"); b._append_chunk("orld"); b._finish_no_fix() }; let r2 = { let mut b = RopeBuilder::new(); b._append_chunk("Hell"); b._append_chunk("o world"); b._finish_no_fix() }; let mut hasher1 = TestHasher::default(); let mut hasher2 = TestHasher::default(); r1.hash(&mut hasher1); r2.hash(&mut hasher2); assert_eq!(hasher1.finish(), hasher2.finish()); } #[test] #[cfg_attr(miri, ignore)] fn hash_2() { // Build two ropes with the same contents but different chunk boundaries. let r1 = { let mut b = RopeBuilder::new(); for chunk in SMALL_TEXT.as_bytes().chunks(5) { b._append_chunk(std::str::from_utf8(chunk).unwrap()); } b._finish_no_fix() }; let r2 = { let mut b = RopeBuilder::new(); for chunk in SMALL_TEXT.as_bytes().chunks(7) { b._append_chunk(std::str::from_utf8(chunk).unwrap()); } b._finish_no_fix() }; for (l1, l2) in r1.lines().zip(r2.lines()) { let mut hasher1 = TestHasher::default(); let mut hasher2 = TestHasher::default(); l1.hash(&mut hasher1); l2.hash(&mut hasher2); assert_eq!(hasher1.finish(), hasher2.finish()); } } #[test] #[cfg_attr(miri, ignore)] fn hash_3() { // Build two ropes with the same contents but different chunk boundaries. let r1 = { let mut b = RopeBuilder::new(); for chunk in SMALL_TEXT.as_bytes().chunks(521) { b._append_chunk(std::str::from_utf8(chunk).unwrap()); } b._finish_no_fix() }; let r2 = { let mut b = RopeBuilder::new(); for chunk in SMALL_TEXT.as_bytes().chunks(547) { b._append_chunk(std::str::from_utf8(chunk).unwrap()); } b._finish_no_fix() }; let mut hasher1 = TestHasher::default(); let mut hasher2 = TestHasher::default(); r1.hash(&mut hasher1); r2.hash(&mut hasher2); assert_eq!(hasher1.finish(), hasher2.finish()); } ropey-1.6.1/tests/lifetimes.rs000064400000000000000000000040331046102023000144600ustar 00000000000000//! This test file ensures that all of the lifetimes work the way we //! want, and that there are no regressions. It's a "does this compile?" //! test. extern crate ropey; use ropey::{Rope, RopeSlice}; const TEXT: &str = include_str!("test_text.txt"); fn main() { if cfg!(miri) { return; } let rope = Rope::from_str(TEXT); let (a, b, c, d, e, f, g, count, line, string) = { // The lifetimes of intermediate slices shouldn't matter. The // lifetimes of the things produced by the calls below should be // tied to the lifetime of the original rope, not the lifetimes of // the slices they were created from. Therefore, this should all // compile. let a = rope.slice(4..500).slice(4..400).slice(4..300); let b = rope.slice(4..500).slice(4..400).as_str(); let c = rope.slice(4..500).slice(4..400).line(1); let d = rope.line(1).slice(4..20).slice(4..10); let e = rope.slice(4..500).slice(4..400).chunk_at_byte(50); let f = rope.slice(4..500).slice(4..400).chunk_at_char(50); let g = rope.slice(4..500).slice(4..400).chunk_at_line_break(3); // Same for iterators. In addition, the items _yielded_ by the // iterators should also be tied to the lifetime of the original // rope, not to the iterators or slices they came from. let mut count = 0; for _ in rope.slice(4..500).slice(4..400).bytes() { count += 1; } for _ in rope.slice(4..500).slice(4..400).chars() { count += 1; } let mut line: RopeSlice = "".into(); for l in rope.slice(4..500).slice(4..400).lines() { line = l; } line = line.slice(..).slice(..); let mut string = ""; for c in rope.slice(4..500).slice(4..400).chunks() { string = c; } (a, b, c, d, e, f, g, count, line, string) }; println!( "{} {:?} {} {} {:?} {:?} {:?} {} {} {}", a, b, c, d, e, f, g, count, line, string ); } ropey-1.6.1/tests/medium.txt000064400000000000000000006540471046102023000141720ustar 00000000000000Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante.ropey-1.6.1/tests/non_ascii.txt000064400000000000000000000047741046102023000146500ustar 00000000000000_____________ ______________ㅇ_________ㅇㅇㅇ____ㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇ__________ ________________ _____________________________________________ ____________________________________________ _______________ _____________________________________ ____________________________________________________________________ ______________________ ________________________ _________________________________________________ ______________________________________________________________ ____________________ ______________________________________________ _________________ ________________ __________________________________________________________ ____________________________________________________ _______________ ________________________________________________ ______________________ ________________________ ________________________________________________________________________ ______________________________ ____________________ __________________________________________ __________________ ____________________________________________ ________________ __________________ _____________________ _____________________________________________________ ____________________________________________ _________________ ___________________________________________ __________________________________________ ___________________________________________________ ___________________________________________________________________ _______________________ _______________ _____________ ______________ㅇㅇㅇㅇㅇㅇ____ㅇ_ㅇㅇㅇ____ ______________ㅇㅇㅇㅇ_____________ㅇㅇㅇㅇㅇㅇㅇㅇ____ ____________________________________ ________________________________________________________________________________ ______________________________________ ______________ ______________ㅇㅇㅇㅇ_______ㅇㅇㅇㅇㅇㅇ_______ㅇㅇ_____________ㅇㅇㅇㅇㅇ____ ________________________________ __________________________________________________________________ _________________________ ______________ __________________________________________________________________ _______________________________________________ ______________________________________ ________________________________________________ _______________________________________________ __________________________________________________________________ ___________ _________ _______________________ ________________________________________________ _______ ______ ________________________________ _______________________________________ ______ ropey-1.6.1/tests/non_ascii_comparison.rs000064400000000000000000000017161046102023000167000ustar 00000000000000extern crate ropey; use ropey::Rope; const TEXT1: &str = include_str!("non_ascii.txt"); #[test] #[allow(clippy::cmp_owned)] #[cfg_attr(miri, ignore)] fn non_ascii_eq() { // Build rope from file contents let rope1 = Rope::from_str(TEXT1); let mut rope2 = Rope::from_str(TEXT1); rope2.remove(1467..1827); for line1 in rope1.lines() { for line2 in rope2.lines() { println!("lines1: {line1} line2: {line2}"); println!("{}", line1.to_string() == line2); println!("{}", line1 == line2); } } } #[test] #[cfg_attr(miri, ignore)] fn non_ascii_ord() { // Build rope from file contents let rope1 = Rope::from_str(TEXT1); let mut rope2 = Rope::from_str(TEXT1); rope2.remove(1467..1827); for line1 in rope1.lines() { for line2 in rope2.lines() { println!("lines1: {line1} line2: {line2}"); println!("{:?}", line2.partial_cmp(&line1)); } } } ropey-1.6.1/tests/proptest_tests.proptest-regressions000064400000000000000000000063561046102023000214100ustar 00000000000000# Seeds for failure cases proptest has generated in the past. It is # automatically read and these particular cases re-run before any # novel cases are generated. # # It is recommended to check this file in to source control so that # everyone who runs the test benefits from these saved cases. xs 3540557325 1860947506 820813434 2332024384 # shrinks to ref char_idxs = [] xs 3233894212 2188982698 1827697686 2168070367 # shrinks to ref char_idxs = [0] xs 3424326027 3025228200 341421083 1156727702 # shrinks to ref text = "0𑊏טּ𑰀®00𑙐AA 🢐0Aⶠ🇦 A0A ຍ \u{11da0} ", idx = 80 xs 3459659596 2914190641 2301470235 1246147682 # shrinks to ref char_idxs = [] xs 1766171267 3090483113 1561813983 1680450853 # shrinks to idx1 = 0, idx2 = 18268 xs 3414034384 4148684142 2271524827 1599993371 # shrinks to idx = 11953 cc 4eb27f71f7d1f5c5c97eaaac4b74805ef091f7205b89fe8fee4eb666a7072913 # shrinks to range = (16564, 17055) cc c263569742fedaf3cdfee5398cbe1c0d761179ff67d54984695e17b8ac601e12 # shrinks to ref text = "\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n", idx = 0 cc 92cbac03b8c705ff1c62e10d689efaee7363e8ca27da4dee474f80e9e9fdb7cc # shrinks to char_idx = 17630, ref ins_text = "A" cc 530b4c5540b55e91808e710667a15d18081849b99ce32f42aa5c0739a362c7d0 # shrinks to idx = 10494 cc 3f41757544c527c968ee0bd9c6941025b092acab605a2d228176209af312862e # shrinks to idx = 16928 ropey-1.6.1/tests/proptest_tests.rs000064400000000000000000001531341046102023000156100ustar 00000000000000#![cfg(not(miri))] #[macro_use] extern crate proptest; extern crate ropey; use proptest::collection::vec; use proptest::test_runner::Config; use ropey::{ str_utils::{byte_to_char_idx, byte_to_line_idx, char_to_byte_idx, char_to_line_idx}, Rope, MAX_BYTES, }; fn string_insert(text: &mut String, char_idx: usize, text_ins: &str) { let byte_idx = char_to_byte_idx(text, char_idx); text.insert_str(byte_idx, text_ins); } fn string_remove(text: &mut String, char_start: usize, char_end: usize) { let byte_start = char_to_byte_idx(text, char_start); let byte_end = char_to_byte_idx(text, char_end); let text_r = text.split_off(byte_end); text.truncate(byte_start); text.push_str(&text_r); } fn string_slice(text: &str, char_start: usize, char_end: usize) -> &str { let byte_start = char_to_byte_idx(text, char_start); let text = &text[byte_start..]; let byte_end = char_to_byte_idx(text, char_end - char_start); &text[..byte_end] } //=========================================================================== proptest! { #![proptest_config(Config::with_cases(512))] #[test] fn pt_from_str(ref text in "\\PC{0,200}") { let rope = Rope::from_str(text); rope.assert_integrity(); rope.assert_invariants(); assert_eq!(rope, text.as_str()); } #[test] fn pt_from_str_crlf(ref text in "[\\u{000A}\\u{000D}]{0,200}") { let rope = Rope::from_str(text); rope.assert_integrity(); rope.assert_invariants(); assert_eq!(rope, text.as_str()); } #[test] fn pt_insert(char_idx in 0usize..(CHAR_LEN+1), ref ins_text in "\\PC*") { let mut rope = Rope::from_str(TEXT); let mut text = String::from(TEXT); let len = rope.len_chars(); rope.insert(char_idx % (len + 1), ins_text); string_insert(&mut text, char_idx % (len + 1), ins_text); rope.assert_integrity(); rope.assert_invariants(); assert_eq!(rope, text); } #[test] fn pt_remove(range in (0usize..(CHAR_LEN+1), 0usize..(CHAR_LEN+1))) { let mut rope = Rope::from_str(TEXT); let mut text = String::from(TEXT); let mut idx1 = range.0 % (rope.len_chars() + 1); let mut idx2 = range.1 % (rope.len_chars() + 1); if idx1 > idx2 { std::mem::swap(&mut idx1, &mut idx2) }; rope.remove(idx1..idx2); string_remove(&mut text, idx1, idx2); rope.assert_integrity(); rope.assert_invariants(); assert_eq!(rope, text); } #[test] fn pt_split_off_and_append(mut idx in 0usize..(CHAR_LEN+1)) { let mut rope = Rope::from_str(TEXT); idx %= rope.len_chars() + 1; let rope2 = rope.split_off(idx); rope.assert_integrity(); rope.assert_invariants(); rope2.assert_integrity(); rope2.assert_invariants(); rope.append(rope2); rope.assert_integrity(); rope.assert_invariants(); assert_eq!(rope, TEXT); } #[test] fn pt_shrink_to_fit_01(ref char_idxs in vec(0usize..1000000, 0..1000)) { let mut rope = Rope::new(); for idx in char_idxs.iter() { let len = rope.len_chars(); rope.insert(idx % (len + 1), "Hello world!") } let capacity_before = rope.capacity(); let rope_clone = rope.clone(); rope.shrink_to_fit(); rope.assert_integrity(); rope.assert_invariants(); assert_eq!(rope, rope_clone); assert!((rope.capacity() - rope.len_bytes()) <= MAX_BYTES); assert!(rope.capacity() <= capacity_before); } #[test] fn pt_shrink_to_fit_02(ref char_idxs in vec(0usize..1000000, 0..1000)) { let mut rope = Rope::new(); let ins_text = "AT̴̷͚͖̜͈̪͎͔̝̫̦̹͔̻̮͂ͬͬ̌ͣ̿ͤ͌ͥ͑̀̂ͬ̚͘͜͞ô̵͚̤̯̹͖̅̌̈́̑̏̕͘͝A"; for idx in char_idxs.iter() { let len = rope.len_chars(); rope.insert(idx % (len + 1), ins_text); } let rope_clone = rope.clone(); rope.shrink_to_fit(); rope.assert_integrity(); rope.assert_invariants(); assert_eq!(rope, rope_clone); let max_diff = MAX_BYTES + ((rope.len_bytes() / MAX_BYTES) * ins_text.len()); assert!((rope.capacity() - rope.len_bytes()) <= max_diff); } #[test] fn pt_chunk_at_byte(ref text in "\\PC*\\n?\\PC*\\n?\\PC*") { let r = Rope::from_str(text); let mut t = &text[..]; let mut last_chunk = ""; for i in 0..r.len_bytes() { let (chunk, b, c, l) = r.chunk_at_byte(i); assert_eq!(c, byte_to_char_idx(text, b)); assert_eq!(l, byte_to_line_idx(text, b)); if chunk != last_chunk { assert_eq!(chunk, &t[..chunk.len()]); t = &t[chunk.len()..]; last_chunk = chunk; } let c1 = { let i2 = byte_to_char_idx(text, i); text.chars().nth(i2).unwrap() }; let c2 = { let i2 = i - b; let i3 = byte_to_char_idx(chunk, i2); chunk.chars().nth(i3).unwrap() }; assert_eq!(c1, c2); } assert_eq!(t.len(), 0); } #[test] fn pt_chunk_at_char(ref text in "\\PC*\\n?\\PC*\\n?\\PC*") { let r = Rope::from_str(text); let mut t = &text[..]; let mut last_chunk = ""; for i in 0..r.len_chars() { let (chunk, b, c, l) = r.chunk_at_char(i); assert_eq!(b, char_to_byte_idx(text, c)); assert_eq!(l, char_to_line_idx(text, c)); if chunk != last_chunk { assert_eq!(chunk, &t[..chunk.len()]); t = &t[chunk.len()..]; last_chunk = chunk; } let c1 = text.chars().nth(i).unwrap(); let c2 = { let i2 = i - c; chunk.chars().nth(i2).unwrap() }; assert_eq!(c1, c2); } assert_eq!(t.len(), 0); } #[test] fn pt_chunk_at_line_break(ref text in "\\PC*\\n?\\PC*\\n?\\PC*") { let r = Rope::from_str(text); // First chunk { let (chunk, b, c, l) = r.chunk_at_line_break(0); assert_eq!(chunk, &text[..chunk.len()]); assert_eq!(b, 0); assert_eq!(c, 0); assert_eq!(l, 0); } // Middle chunks for i in 1..r.len_lines() { let (chunk, b, c, l) = r.chunk_at_line_break(i); assert_eq!(chunk, &text[b..(b + chunk.len())]); assert_eq!(c, byte_to_char_idx(text, b)); assert_eq!(l, byte_to_line_idx(text, b)); assert!(l < i); assert!(i <= byte_to_line_idx(text, b + chunk.len())); } // Last chunk { let (chunk, b, c, l) = r.chunk_at_line_break(r.len_lines()); assert_eq!(chunk, &text[(text.len() - chunk.len())..]); assert_eq!(chunk, &text[b..]); assert_eq!(c, byte_to_char_idx(text, b)); assert_eq!(l, byte_to_line_idx(text, b)); } } #[test] fn pt_chunk_at_byte_slice(ref gen_text in "\\PC*\\n?\\PC*\\n?\\PC*", range in (0usize..1000000, 0usize..1000000)) { let r = Rope::from_str(gen_text); let mut idx1 = range.0 % (r.len_chars() + 1); let mut idx2 = range.1 % (r.len_chars() + 1); if idx1 > idx2 { std::mem::swap(&mut idx1, &mut idx2) }; let s = r.slice(idx1..idx2); let text = string_slice(gen_text, idx1, idx2); let mut t = text; let mut prev_chunk = ""; for i in 0..s.len_bytes() { let (chunk, b, c, l) = s.chunk_at_byte(i); assert_eq!(c, byte_to_char_idx(text, b)); assert_eq!(l, byte_to_line_idx(text, b)); if chunk != prev_chunk { assert_eq!(chunk, &t[..chunk.len()]); t = &t[chunk.len()..]; prev_chunk = chunk; } let c1 = { let i2 = byte_to_char_idx(text, i); text.chars().nth(i2).unwrap() }; let c2 = { let i2 = i - b; let i3 = byte_to_char_idx(chunk, i2); chunk.chars().nth(i3).unwrap() }; assert_eq!(c1, c2); } assert_eq!(t.len(), 0); } #[test] fn pt_chunk_at_char_slice(ref gen_text in "\\PC*\\n?\\PC*\\n?\\PC*", range in (0usize..1000000, 0usize..1000000)) { let r = Rope::from_str(gen_text); let mut idx1 = range.0 % (r.len_chars() + 1); let mut idx2 = range.1 % (r.len_chars() + 1); if idx1 > idx2 { std::mem::swap(&mut idx1, &mut idx2) }; let s = r.slice(idx1..idx2); let text = string_slice(gen_text, idx1, idx2); let mut t = text; let mut prev_chunk = ""; for i in 0..s.len_chars() { let (chunk, b, c, l) = s.chunk_at_char(i); assert_eq!(b, char_to_byte_idx(text, c)); assert_eq!(l, char_to_line_idx(text, c)); if chunk != prev_chunk { assert_eq!(chunk, &t[..chunk.len()]); t = &t[chunk.len()..]; prev_chunk = chunk; } let c1 = text.chars().nth(i).unwrap(); let c2 = { let i2 = i - c; chunk.chars().nth(i2).unwrap() }; assert_eq!(c1, c2); } assert_eq!(t.len(), 0); } #[test] fn pt_chunk_at_line_break_slice(ref gen_text in "\\PC*\\n?\\PC*\\n?\\PC*", range in (0usize..1000000, 0usize..1000000)) { let r = Rope::from_str(gen_text); let mut idx1 = range.0 % (r.len_chars() + 1); let mut idx2 = range.1 % (r.len_chars() + 1); if idx1 > idx2 { std::mem::swap(&mut idx1, &mut idx2) }; let s = r.slice(idx1..idx2); let text = string_slice(gen_text, idx1, idx2); // First chunk { let (chunk, b, c, l) = s.chunk_at_line_break(0); assert_eq!(chunk, &text[..chunk.len()]); assert_eq!(b, 0); assert_eq!(c, 0); assert_eq!(l, 0); } // Middle chunks for i in 1..s.len_lines() { let (chunk, b, c, l) = s.chunk_at_line_break(i); assert_eq!(chunk, &text[b..(b + chunk.len())]); assert_eq!(c, byte_to_char_idx(text, b)); assert_eq!(l, byte_to_line_idx(text, b)); assert!(l < i); assert!(i <= byte_to_line_idx(text, b + chunk.len())); } // Last chunk { let (chunk, b, c, l) = s.chunk_at_line_break(s.len_lines()); assert_eq!(chunk, &text[(text.len() - chunk.len())..]); assert_eq!(chunk, &text[b..]); assert_eq!(c, byte_to_char_idx(text, b)); assert_eq!(l, byte_to_line_idx(text, b)); } } #[test] fn pt_slice(ref text in "\\PC*", range in (0usize..1000000, 0usize..1000000)) { let rope = Rope::from_str(text); let mut idx1 = range.0 % (rope.len_chars() + 1); let mut idx2 = range.1 % (rope.len_chars() + 1); if idx1 > idx2 { std::mem::swap(&mut idx1, &mut idx2) }; let slice = rope.slice(idx1..idx2); let text_slice = string_slice(text, idx1, idx2); assert_eq!(slice, text_slice); assert_eq!(slice.len_bytes(), text_slice.len()); assert_eq!(slice.len_chars(), text_slice.chars().count()); } #[test] fn pt_get_byte_slice(ref text in "\\PC*", range in (0usize..1000000, 0usize..1000000)) { let rope = Rope::from_str(text); let mut idx1 = range.0 % (rope.len_bytes() + 1); let mut idx2 = range.1 % (rope.len_bytes() + 1); if idx1 > idx2 { std::mem::swap(&mut idx1, &mut idx2) }; if let Some(slice) = rope.get_byte_slice(idx1..idx2) { let text_slice = &text[idx1..idx2]; assert_eq!(slice, text_slice); assert_eq!(slice.len_bytes(), text_slice.len()); assert_eq!(slice.len_chars(), text_slice.chars().count()); } } #[test] fn pt_cmp(ref text1 in "\\PC*", ref text2 in "\\PC*") { let r1 = Rope::from_str(text1); let r2 = Rope::from_str(text2); assert_eq!(r1.cmp(&r2), text1.cmp(text2)); assert_eq!(r2.cmp(&r1), text2.cmp(text1)); } #[test] fn pt_bytes_iter_next(ref text in "\\PC{0,200}", idx1 in 0usize..20000, idx2 in 0usize..20000, ) { let len_chars = byte_to_char_idx(text, text.len()); let idx1 = if len_chars == 0 { 0 } else { idx1 % len_chars }; let idx2 = if len_chars == 0 { 0 } else { idx2 % len_chars }; let start = idx1.min(idx2); let end = idx1.max(idx2); let r = Rope::from_str(text); let text = string_slice(text, start, end); let s = r.slice(start..end); for (idx, byte) in s.bytes().enumerate() { assert_eq!(byte, text.as_bytes()[idx]); } } #[test] fn pt_bytes_iter_prev( ref directions in vec(0u8..2, 0..1000), idx1 in 0usize..CHAR_LEN, idx2 in 0usize..CHAR_LEN, ) { let start = idx1.min(idx2); let end = idx1.max(idx2); let r = Rope::from_str(TEXT); let s = r.slice(start..end); let mut itr = s.bytes(); let mut bytes = Vec::new(); for i in directions { if *i == 0 { assert_eq!(itr.prev(), bytes.pop()); } else if let Some(byte) = itr.next() { bytes.push(byte); } } } #[test] fn pt_chars_iter_next(ref text in "\\PC{0,200}", idx1 in 0usize..20000, idx2 in 0usize..20000, ) { let len_chars = byte_to_char_idx(text, text.len()); let idx1 = if len_chars == 0 { 0 } else { idx1 % len_chars }; let idx2 = if len_chars == 0 { 0 } else { idx2 % len_chars }; let start = idx1.min(idx2); let end = idx1.max(idx2); let r = Rope::from_str(text); let text = string_slice(text, start, end); let s = r.slice(start..end); for (c1, c2) in s.chars().zip(text.chars()) { assert_eq!(c1, c2); } } #[test] fn pt_chars_iter_prev( ref directions in vec(0u8..2, 0..1000), idx1 in 0usize..CHAR_LEN, idx2 in 0usize..CHAR_LEN, ) { let start = idx1.min(idx2); let end = idx1.max(idx2); let r = Rope::from_str(TEXT); let s = r.slice(start..end); let mut itr = s.chars(); let mut chars = Vec::new(); for i in directions { if *i == 0 { assert_eq!(itr.prev(), chars.pop()); } else if let Some(c) = itr.next() { chars.push(c); } } } #[test] fn pt_chunks_iter_next_01(ref text in "\\PC{0,200}", idx1 in 0usize..20000, idx2 in 0usize..20000, ) { let len_chars = byte_to_char_idx(text, text.len()); let idx1 = if len_chars == 0 { 0 } else { idx1 % len_chars }; let idx2 = if len_chars == 0 { 0 } else { idx2 % len_chars }; let start = idx1.min(idx2); let end = idx1.max(idx2); let r = Rope::from_str(text); let text = string_slice(text, start, end); let s = r.slice(start..end); let mut idx = 0; for chunk in s.chunks() { assert_eq!(chunk, &text[idx..(idx + chunk.len())]); idx += chunk.len(); } } #[test] fn pt_chunks_iter_next_02(idx1 in 0usize..CHAR_LEN, idx2 in 0usize..CHAR_LEN) { let start = idx1.min(idx2); let end = idx1.max(idx2); let r = Rope::from_str(TEXT); let text = string_slice(TEXT, start, end); let s = r.slice(start..end); let mut idx = 0; for chunk in s.chunks() { assert_eq!(chunk, &text[idx..(idx + chunk.len())]); idx += chunk.len(); } } #[test] fn pt_chunks_iter_prev_01(ref text in "\\PC{0,200}", ref directions in vec(0u8..2, 0..1000), idx1 in 0usize..20000, idx2 in 0usize..20000, ) { let r = Rope::from_str(text); let idx1 = if r.len_chars() == 0 { 0 } else { idx1 % r.len_chars() }; let idx2 = if r.len_chars() == 0 { 0 } else { idx2 % r.len_chars() }; let start = idx1.min(idx2); let end = idx1.max(idx2); let s = r.slice(start..end); let mut itr = s.chunks(); let mut chunks = Vec::new(); for i in directions { if *i == 0 { assert_eq!(itr.prev(), chunks.pop()); } else if let Some(chunk) = itr.next() { chunks.push(chunk); } } } #[test] fn pt_chunks_iter_prev_02( ref directions in vec(0u8..2, 0..1000), idx1 in 0usize..CHAR_LEN, idx2 in 0usize..CHAR_LEN, ) { let start = idx1.min(idx2); let end = idx1.max(idx2); let r = Rope::from_str(TEXT); let s = r.slice(start..end); let mut itr = s.chunks(); let mut chunks = Vec::new(); for i in directions { if *i == 0 { assert_eq!(itr.prev(), chunks.pop()); } else if let Some(chunk) = itr.next() { chunks.push(chunk); } } } #[test] fn pt_lines_iter_01(ref text in "\n{0,2}\\PC{0,200}\n{0,2}\\PC{0,10}\n{0,2}\\PC{0,200}\n{0,2}", idx1 in 0usize..CHAR_LEN, idx2 in 0usize..CHAR_LEN, ref directions in vec(0u8..2, 1..50), ) { let r = Rope::from_str(text); let idx1 = if r.len_chars() == 0 { 0 } else { idx1 % r.len_chars() }; let idx2 = if r.len_chars() == 0 { 0 } else { idx2 % r.len_chars() }; let start = idx1.min(idx2); let end = idx1.max(idx2); let s = r.slice(start..end); let text = string_slice(text, start, end); let mut itr1 = ropey::iter::Lines::from_str_pt(text); let mut itr2 = s.lines(); for &dir in directions { if dir == 0 { assert_eq!(itr1.next(), itr2.next()); } else { assert_eq!(itr1.prev(), itr2.prev()); } } } #[test] fn pt_bytes_at_01(idx in 0usize..TEXT.len()) { let r = Rope::from_str(TEXT); let mut bytes_r = r.bytes_at(idx); let text_bytes = TEXT.as_bytes(); #[allow(clippy::needless_range_loop)] for i in idx..r.len_bytes() { assert_eq!(bytes_r.next(), Some(text_bytes[i])); } } #[test] fn pt_bytes_at_02(idx in 0usize..TEXT.len()) { let r = Rope::from_str(TEXT); let mut bytes_r = r.bytes_at(idx + 1); let text_bytes = TEXT.as_bytes(); let mut i = idx + 1; while i > 0 { i -= 1; assert_eq!(bytes_r.prev(), Some(text_bytes[i])); } } #[test] fn pt_chars_at_01(idx in 0usize..CHAR_LEN) { let r = Rope::from_str(TEXT); let mut chars_r = r.chars_at(idx); let chars_t = (&TEXT[char_to_byte_idx(TEXT, idx)..]).chars(); for c in chars_t { assert_eq!(chars_r.next(), Some(c)); } } #[test] fn pt_chars_at_02(idx in 0usize..CHAR_LEN) { let r = Rope::from_str(TEXT); let mut chars_r = r.chars_at(idx); let mut chars_t = (&TEXT[..char_to_byte_idx(TEXT, idx)]).chars(); while let Some(c) = chars_t.next_back() { assert_eq!(chars_r.prev(), Some(c)); } } #[test] fn pt_bytes_iter_exact_01(idx in 1024usize..(CHAR_LEN - 1024)) { let r = Rope::from_str(TEXT); let s = r.slice(idx..(idx + 373)); // Forward { let mut byte_count = s.len_bytes(); let mut bytes = s.bytes(); assert_eq!(byte_count, bytes.len()); while let Some(_) = bytes.next() { byte_count -= 1; assert_eq!(byte_count, bytes.len()); } assert_eq!(byte_count, 0); assert_eq!(bytes.len(), 0); } // Backward { let mut byte_count = 0; let mut bytes = s.bytes_at(s.len_bytes()); assert_eq!(byte_count, bytes.len()); while bytes.prev().is_some() { byte_count += 1; assert_eq!(byte_count, bytes.len()); } assert_eq!(byte_count, s.len_bytes()); assert_eq!(bytes.len(), s.len_bytes()); bytes.prev(); assert_eq!(bytes.len(), s.len_bytes()); } } #[test] fn pt_chars_iter_exact_01(idx in 1024usize..(CHAR_LEN - 1024)) { let r = Rope::from_str(TEXT); let s = r.slice(idx..(idx + 373)); // Forward let mut char_count = s.len_chars(); let mut chars = s.chars(); assert_eq!(char_count, chars.len()); while let Some(_) = chars.next() { char_count -= 1; assert_eq!(char_count, chars.len()); } assert_eq!(char_count, 0); assert_eq!(chars.len(), 0); // Backward let mut char_count = 0; let mut chars = s.chars_at(s.len_chars()); assert_eq!(char_count, chars.len()); while chars.prev().is_some() { char_count += 1; assert_eq!(char_count, chars.len()); } assert_eq!(char_count, s.len_chars()); assert_eq!(chars.len(), s.len_chars()); chars.prev(); assert_eq!(chars.len(), s.len_chars()); } #[test] fn pt_lines_iter_exact_01(idx in 1024usize..(CHAR_LEN - 1024)) { let r = Rope::from_str(TEXT); let s = r.slice(idx..(idx + 373)); // Forward let mut line_count = s.len_lines(); let mut lines = s.lines(); assert_eq!(line_count, lines.len()); while let Some(_) = lines.next() { line_count -= 1; assert_eq!(line_count, lines.len()); } assert_eq!(line_count, 0); assert_eq!(lines.len(), 0); // Backward let mut line_count = 0; let mut lines = s.lines_at(s.len_lines()); assert_eq!(line_count, lines.len()); while lines.prev().is_some() { line_count += 1; assert_eq!(line_count, lines.len()); } assert_eq!(line_count, s.len_lines()); assert_eq!(lines.len(), s.len_lines()); lines.prev(); assert_eq!(lines.len(), s.len_lines()); } } //=========================================================================== // Char count of TEXT, below const CHAR_LEN: usize = 18267; // 31539 bytes, 18267 chars, 95 lines // Contains many long graphemes. const TEXT: &str = " T̴̷͚͖̜͈̪͎͔̝̫̦̹͔̻̮͂ͬͬ̌ͣ̿ͤ͌ͥ͑̀̂ͬ̚͘͜͞ô̵͚̤̯̹͖͍̦̼̦̖̞̺͕̳̬͇͕̟̜̅̌̈́̑̏̕͘͝ ͍̼̗̫͈̭̦̱̬͚̱̞͓̜̭̼͇̰̞ͮ͗ͣ́ͪ̔ͪ̍̑̏́̀̽̍̔͘͜͜͝ȋ̐̽ͦ̓̔̅͏̧̢̖̭̝̳̹̯̤͈̫͔͔̠͓͉̠͖̠͜ͅn̷̯̗̗̠̱̥͕͉̥͉̳̫̙̅͗̌̒͂̏͑̎̌̌̊͌͘͘ͅͅv̧̜͕͍͙͍̬͕͍̳͉̠͍̹̮̻̜ͨ̏͒̍ͬ̈́͒̈ͥ͗ͣ̄̃ͤ͊̌͆̓o̸̧̎̓͂̊͢҉͍̼̘͇̱̪̠͎̥̹ķ̈́͗͆ͥ͐͑̆̎́͌ͩͯ̊̓͐ͬ̇̕҉̢͏͚̲̰̗̦e̿̀͐̽ͪ̈ͤͬ҉́͟͏̵̫̲̱̻̰̲̦͇̭̟̺͈̞̫̰̜͕͖ͅ ̡̰͎͓͚͓͉͈̮̻̣̮̟̩̬̮̈̋̊͆ͪ̄ͪ͒ͨͧ̇ͪ̇̑̚t̷̬̟͎̞͈̯͙̹̜ͩ̓ͪ͛͐̐ͤ̾̄̈͒̽̈́̑͒̏h̨̢̳͇͓͉̝ͫ̐̓̆̓ͮ̔̓̈́̇ͫe̟̬̣̗͚̬̾̉͋̽ͯ̌ͯͬ̂ͯͭ̓͛́̚͡ ̨̭̱͉̭͈̈̽̆̂͒͗̀ͥͩ͡h̻̼̱̹̗͖̙̗̲̤͓͇͚͚̻̞̥ͥ͛͌ͧ̚͟i̢̯̹̹̘̳̙ͩ̉ͥ͆̽̇̾̎͗̔̓͂͂́̓̌ͬv̧̡̛̟̜̠͉͖̘̲̻̯͚͍͓̯̻̲̹̥͇̻̿̓͛̊̌ͩͩ́ͩ̍͌̚e̵̾́̈́̏͌͌̊͗̏͋ͦ͘͡͏͚̜͚͎͉͍̱͙̖̹̣̘̥̤̹̟͠-̔̌͐́͒ͦͮ̇ͭ̄̏̊̇̍̕͏̩̥̰͚̟m̨̒ͫͦ̔̔͋҉̱̩̗͇̥̰̩̭͍͚͠į̵̷̻̗͉͕͚̣̼̺͉̦̮̠̆̀̐ͩ͒ͯͩͯ͞ͅn̢̫̤̝̝͚̺͍̱̦͚͂̿ͨ̇ͤ͠d̡ͯ͋̋ͧ̈́̒̈͏̛͏̵̤̬͍̗̞̠̟̞̺̠̥̹̱͉̜͍͎̤ ̷̸̢̰͓̘̯͎̤̫̘͓̙̟̳͇̹̥͈͙̮̩̅̋͌͗̓͊̓ͨͣ͗̓͐̈́ͩ̓ͣrͫ͂͌ͪ̏̐̍̾ͥ̓͗̈͆̈ͥ̀̾̚̚҉̴̶̭͇̗͙̘̯̦̭̮̪͚̥̙̯̠͙̪͡e̵̸̲͉̳̙͖͖̫̘̪͕̳͓̻̙͙ͥ̍͂̽ͨ̓̒̒̏ͬ͗ͧ̑̀͠p̵̸̛̦̣͙̳̳̩̣̼̘͈͂ͪͭͤ̎r̶̩̟̞̙͔̼ͫ̆ͦ̐̀̏̾̉̍ͬ̅ͧ͊ͪ̒̈́ͬ̃͞ẻ̴̼͙͍͎̠̀̅̔̃̒͐ͦ̏̆̅̓͋͢ͅš̆̈̆̋ͨ̅̍̇͂̒ͩͨ̂̐̓ͩ͏̸͔͔̯͇͚̤̪̬̗͈̰̦̯͚̕ę̢̱̠͙̲͉̗͚̮̪͖̙̞̦͉͕̗̳͙ͦ̆̋͌ͣ̅̊́ͅņ̴̷̫̪̦͇̺̹͉̗̬̞̲̭̜̪͒̏͂̂̎͊́̋͒̏̅̋̚͘t̷̶̨̟̦̗̦̱͌͌ͩ̀i̴̴̢̖͓͙̘͇̠̦̙̭̼͖̹̾̒̎̐ͥͭ͋ͥ̅͟ͅņ̫͙̹̦̳͈͙̬̫̮͕̰̩̣̘̘͐̀̓ͭͩͬͯ̎͛̿ͫ̊̔̅́̕͠gͥͩ̂͌̒̊̕͏̻͙͖̣͙͍̹͕̝͖̼̙̘͝ ͤ͐̓̒̓͋̐̃̇͊̓ͦ͐̚͢҉̢̨̟̠͉̳͖̲̩͙̕ć̷̡̫̩̞̯̼̝̼͖̤̳̻̘̪̤͈̦̭ͣ́͂͐̽͆̔̀̚͜h̶̢̹̹̙͔̱̓ͦ͌̋̎ͭ͒͋̒ͭ̌̃͌̿ͣ̆̅͑ą̙̳̬̞̬͚̜̤̱̙͇̠̟̈ͤ͋̃̀̓̓ͯ̍̀̽ͣ̐̈̿̌̕ǫ͋͂͐ͬ̿ͯ̂̈́͌̓̌ͧ̕͏̜͔̗͚͔̘̣͕̘̲͖̼͇͖̗̳ͅͅs̷̸̝̙̭̦̣̦̯̭̦͙̹̻͍͇̣̼͗̌͆ͨͭ̃ͮ͐̿̕.̮̝̠̱̺͖͓̼̦̱̉͂͛̓̑̔̓ͮ̈̊̔͗́͝\r ̛̣̺̻̼̙̼͓̱̬͕̩͕̲̳̭̗̍ͤ͋̒̆̄ͨ̿ͧ̓͠ͅI̷̻̤̳̲͔͈̖̬̰͔̪͇͇̟̋ͨ̋̍̉̔͝͞͝ͅn̶͕̭͖̠̣͚̹̪͆ͪ̇̂̅̾ͫ́̅̉ͭ̀͜v̖͉̩͕̣͔̭͕̩̲̖̇̀ͬ́̄͒̆͑͆ͪͤ͆̾̍ͯ̚͜ǫ̡̡̫͎̟̞̰̞̹͇̲̏ͨ̄͊̊̇͒̽͢ķ̶̪̙̰̥͙̞̹̭̺͍͕̙̲̮͊ͭ́͋͛͋̑̒͊̏̒̅͛̄̓͟i̴͎̹̞̥͖̒̄ͮ̒̾ͮͧ̀̚͡n̸̵͓̲̟̞̳͚̼̣͙͖̈ͦ͒̿̅̒̿͛͊̇ͧ̉g̡̧̪̩͚͙͓̪͓͚͉̥̪͍̙̻͖͇͗̑͊͑̾̍͊̀ͅ ̷̵̠͚̘̟͓̫̣̲͎̩̹̣̼̟͊́̏ͫ̆ͩ̓͋͆̿̽̓͘̕t̴̢̝̻̖̲̬̜̺̖̻ͩ̿ͫ͗̈́̔͑̐ͮͦ̽̉̓̚͜h̷̛̲͇̫͈̣̭͂ͭ̂͋ͭ̋̔ͮ̆ͩ͞ë̩͕͉̯͇͔͚̭̼̮̣͓̯́ͭ̀ͣ͗̋̉ͨͬ̒ͥͩ͆̓̓́̀̚͘͝ ̛̫̠̗̥̳͇͉̟̮̪̻̤̪͚̟̜̔̌͌̈͌ͪ̋̎̄ͯ͐ͦ́͞͠fͦ̂̈ͬ̇̅̓̓ͫͣ̉̂̉̚͘͡͡͏̼̖̟͚̙̳͔͎̲̫̦̯͔̣̼̹ě̷̶̫͎̞̺̪̪͇͈̞̳̏̋̋͋̾̓̽̓̑ͮ͊ͣ̋̃̅̀͡e͇̗͎̱͔̦̠̰̩̩͖͙̠̻̝ͯ̿̔̀͋͑ͧ͊̆̇̿ͤ̄ͯ̇̀͢͠ͅl̂̿ͯ͛̊̒̓̈́͏̵̪̦̞̤̫̤͇̙̗͕͎̪͕̙̻̳̗̕͟͢i̞̣̙͎͈̗̮͉̱̜̱̝̞̤͋ͯ͋͐̈́ͫ̉̊̏̀ͯͨ͢͟͝n̳̻̼̥̖͍̭̅͂̓̔̔ͦ̔́ͦ͊̀͛̈́ͬͦ͢͡͡ģ̶̡̳̰̻̙̞̱̳̣̤̫̫͕̤̮̰̬̪̜͋͒̎̈́̉̏̀ͬͯ͌̇͊̚ ́̽ͤͦ̾̔͢҉̛̤͍͉̺̙̮̗̜̟̀͝ơ̢̱͓͓̙͉̖̠̯̦̗͍̓̐̃̉̅̃ͨ͆́ͪ̂̒̀̊̃͆̔͡͡ͅf́ͬ̊ͯͫ̉̈́̽̉̚͢͏̡̺̬̖͇̫͉̱ ̴͇̦̗̙̼̬͓̯͖̮͓͎̗͈̻̈́͆ͭ̐ͦ́͛̀͋̐̌ͬ͑̒̊̿̃͞c̶̸̣͔̬͕̪̱̩̣̑̒̑̓̍̓͂̍̔͌̚͘͜͞h̶͈̱͇͉̳͍͍̰͈͖̬̥͚̯͓̞̹̋̔ͯ̑̃́̒̎̎͊̈́̍̚̕ạ̴̞̱̥͍͙̺͉͚͎̫̦͎̥ͩ̀̀̊ͥ͢o̵̧͕̜͓͈̬̰̫̮͙̹͉̩̝̩͎̓̆͗̿̊̀ͯ̃ͪ̊ͫ̽̉̓ͧ͗́̚͢ͅͅs̡ͫ͋̑ͮ̍̃͊̄ͬ̅̈́ͬ̍̇̔̈̅̍̀҉̜͓̝̘̘̮̼͖͎̻͓͖̖͙̞ͅ.͗ͬͭͩ̌̅͗͏̷̮̗͇͔͇͈̮͢\r ̨͚̲̫̠̼͖̝̻̉ͤ̅̂ͩ̀̇ͬͭ̀͜Ẅ̢́̉͌ͮͬͨ͊̏͌̇̐͊͟͠҉̼̰̦̩͇͕̟̭̪̲͕̥͖̰̪͈̀ͅͅį̷ͣͦ̉̍ͨ͂͂͑̃͂ͪ̊̈̋̄͜҉̨͚̟̲̯̹̺̝̭̺̙͖͍t̼͓̰̩͙̦͓̟͚͖̀ͯ͛̍̈́͑͂̍̋́h̛̼̺̘̥̠̼̼̭͙̮͚̱̍ͯ̓̃̐̂̇͟ ̴̛͖͔̰̠̺̥̲ͮ̍ͫ̽͜õ̒ͯ̒̓ͦ̈́͑̔̒̓̎ͤ͑҉̸̭̱̤̭̬͈ų̙̫̤͖̺̫̱͓͓̗̪͇̩̙̔̉̊͂ͪ̇͢͟͞ͅt̸̬̣̫̞̫̅͐ͮ̌͌̈́̀̀͘ ̷̴̨̖̙̹͚ͬ̈́̈ͯͨͮ̇̈́̋̈́ͭ͛̑̉͊̕ö̡̍ͥ̂ͬͪͧ͒ͧ̏̓̇̂̄͆̌ͫͤ͢͠͝͏̖̱̯̘͙̰̖͎̰͓̟̤ṙ̡̬̟̬̜̪̮̺͖̗̘͈̟ͨ͐͗̑͒̐d̢ͭͫ̊̏ͬͥ͋́̌̈́ͮ̆ͬ̐̌̎͏̵̷̡̞̲̹̙͕̮̮͚ḙ̴̸̠͔͎̥͇͖͕̘̍̓̏̐ͩͩ̈́ͦ̐̋ͤ̎̾̌̏͊̊́̚͞ͅr̸͈̗̣̲̗̣̬̤ͦ̎ͫ̏̀ͥͪ̋ͧ̄͑̋͒͌͋ͦ̉͟͞.ͨͣ̽̈́͒̄ͮ̀͋͋͏̴̧̯̺̙̱̻͙̜\r ̡̣̞̠͓̰͍̠͕̭̺̼͊̽̿͊ͮ̐̓̒̊͒̔̓͐ͨ̈̌́T̸̸̓́̋ͬ́͆ͨͫ͌͂ͣ̋͒҉̺̝͎̟͖͚̠h̸̡̰̜̦͇͕̪̝̳͕͉̲̝̑ͥ͋ͧ̎̆͌͟e̛̹͍͍̫̙̞̪̭̙̟͙̱̺̮̳͕̜ͫ̓ͭ͊ͫ͆̀̚͟͡ ̿͂̄ͧ̔̎ͧ͑̾̀̓͏̦͍̳͈̳͔̘̖̲̯̰̟̝̳̖̦N̶̡̧̦̮̟̦̩̰̣̝̆̀͊̔͢e͛̄ͮͦͨ͂̔̓̍̄̉͆͊̑̑̆̚͏̜̗͎̝̼̯̥̜͖͍̪̝͞ͅͅz̨̛̀̾ͪ͗̉́͠͏͚̫̼̫̜̣pͪͦ͌̄ͥ̆ͣͩ͋̉́̏͞͏̥̜̝̳̱̞̙̳̤͙̟̟̮̦ȅ̷̩̟͉̯͕͔̘̺̥̻̻ͧ̊̅̽ͣ͑̓̑̽ͦ̾͌͜r̴̭̥̲̲̤͚͈̰͇̰͈̰̹ͫ̒ͯ̿͒ͧ̊͆͒ͣ́ḍ̭̟̤̈́̌̓̈́ͫ͐̍͂͞į̛̞̝̮̣͙͙̤̇̂̓̎͋̿̓̎̄̈́ͧ̓ͩ̐̓̄̋ͭ͞͠a͋̔̋ͫ̂͐͂҉̸̛̥̩̯̯̤̝͔̠̝̯̪̥̩̻̼̮n͌ͣ̂͋̿̚҉̛̙̲̺̯͇͓̝̯̪̟͔̩͟ͅ ̢̨͚̻̗̘͖̯̐ͥ͋̽ͯ̎̈́͋̏̄͋̆̑̊̆̚̕͟ͅh̢̛̗̱̭͇͖̰̮̮͈̲͍̯̟ͭ͊̎̽̓ͦͤ͠ï̛̘̝̦͎̦̭̠͖̳͎̮̼̏͐ͧ̒̒͐͑ͪͫ̋̽̚̚͜v̴̮͕̝̮̞͐̄͗̋͒ͤ̎̈̑ͬͮ̄̾ͤ̓̾͊͗͟é̶̷̡̩͖̰̫͓̟ͮͬͣ͊-ͦ͛ͩͤͨͨ̆̄͏̼̜̭͔̳͈͖̳̩͢ͅͅm̷̴̓́̓͛͒̾̍̉҉̛̗̹̠̣̪̺͎̖̝͚̖͙i̛̥͓̬̫͉͕͉͆͒ͧ̂̿̔̔͆̆̓̍͊̀͜n͌ͧͣ̅̌̎ͦͦ͑̑ͭ̆ͬ̀ͤ̀ͣ̚҉͎̰̱͚͈͈̬̹͕̺̙͙̼͘͘͞d̶͖̫̟̲͕̺̠͎̘͕̱̼͙̪̪̩͙̅̅̑̓̇͑̊̉͜͞ ̶̵̷̴̡̠͚̪͕̣̱̖̱̗̤̭̭͔͖͚ͧͤͥ͒̌ͪ͊͂͒̓͂ͧͧ̇̇͐̑̔ͅͅơ̵̲̲͇̯̰͇̜̣͕͕͓̲̤̲͔͚̞͑͗ͤ̓́̚͠ͅf̢̧̛̩̯̼̫͖̾ͣ͌̾̉́̈́̑̈́̚͞͞ͅ ͤͩ́͋͒ͫͬͣ̋̅̆҉̧̱̻͓͕͉̹̫̫̞̯̪̙̩͍̦͔̖̮̀͟ͅc͉̠̜̩̟͕͎̙̣̮̘̼͋ͯ̍ͨ̅̄ͫ̈̋ͫ̊͡͝ȟ̸̨ͯͦ̂̉̇̾̆ͭ̋̐̈̆̀̚͜҉͚͕̻̖a̶̴̛͚̗͙̳̬̲͚ͦ́̐ͥ́̔̅̑̎͐̑ͯ̾ͤͥͧ͡ò̶̧̞̪̦̥̪̻̦̝̳̬̔͛͛ͣ̋̌̔ͫ̂̽ͫ͘͠s̸̖̣̬̤̫͇̫̣̑͆͒̎̏́͟.̴̗̤̭͉̯̻̤͕̌ͯ̍ͤ̓͌ͤ̈̆̉ͦ̇́̚͘͟͝ͅ ̯̹̪͓̬͌̔̌ͬ̀͘͢͡͡Z̡̩̲̩̰̫̩̟͍̰͖͔̭ͣ̆̾ͭ̀́͞ͅa̡̡̙̜̭͇͎͔̙̞̫͓̜͉͔̬ͭ̈ͨ̉͆ͣͫ̃͌̓͌́ͣͥ̒̌͊͘͝l̢̨̡̯̙̫͖̫̺̘̬̟͈͌̊ͧͫͦ̉̃ͩͦ̒ͯ̇̌̓͛͟͝ͅg̵̙̼̼ͪ͂ͭ͗̈̕ȯ̅ͧ̓ͪ́̂͑̐ͩͥͬ̊̑͆̇͒ͫͣ͝҉͎̟̜̥͎̮̣͉̖̟̯̦̖͙͙͞ͅ.̈̑ͩ̇̂ͬ̓ͬ͊͂ͨ̽͠͏̺͎̞̦̜͍͚̯̯͔̝̞̻̩̖\r ̷̰̪͍͎͔͒ͯͥ̾̉͆ͤ̊̓̂͋̀͆H̸̸̹̞̙̺͎̠̯̤ͨ̉̍ͬͤ̓̐͌ͥͮ͞eͣ̈̾͛́͏͕̗͍̜̼͎͚̟̬̣̝̕ͅͅ ̴̛̩̗̼̝̣̩͚͇̯́̉͋̂̍͂̌ͮ͋̾͜͠wͮ̽̓ͭ̿͐̽̐̽͆̓͝҉̡̼̲͖̪̥h̢̢̛͍̰̰̻̱̼̰̹̖̖̪̝̥̘̎̀ͪ͒̾ͫͬ̆̑o̡̗̠̞̱̥͎̰͎͍̫̻͓͇͓͐ͥͯ͂̅͠ͅ ̡̛̏͑ͦ̓͊ͮͫͯͭ̌͒̆̍̈͠҉͖͚̪̫̗̮W̴̐̊͋̾ͥͫ҉͎̞͔̯̫̹͖̰͉̹̼͎̰̱͓̻̀a̶̩̤̙̣͎̳̭̲̗̠͉̳̭̭̦̞͎̮̅͌̾͗̾͛̇̀́͟͞ͅi̷̡ͣ̆̌͋͒͒́͘͏̮̺̩͎͇̜͍̫ṯ̴̢͖̥̖͇͎̦̦̹̖͇̪ͭ̅̍͐̇͒͋̽̏̿̒͆ͧ̄͋ͧͩ͒͜s̙̥̖̘̖͚̭̤̮̖̘̰̫̟̈́ͣ̍ͧ͐ͥ̏͆̃̿͒̔͐́̚͟ͅ ̨ͭ̌ͬͯ͆̒͋ͭ̔̿ͧ̅̓ͣ͡͏͇̟͉̥͔̬̼͚͙͚B̛̜̮̤͓̝̪̪͈͕̘̜͙̰̮̫̘̣͓͔̅ͩ͊̔ͦͯ́̌́͆ͭ̓́e̶̢̡̦͇͕̙͈͖͕̦̬̫͕̣̺̒̿͂͐͒͋͂ͦ́͋ͤ̿ͬ̊ͣ͗̑̽͜ͅͅh̸͑ͫͧ̑ͬͧ̈́̎̃ͣ̊̾͂ͨͤ̓͐̐̑͏̸̭͓̘͉̩i̧̧̭̣͈̝̺̼̺̠͉̞̜̲̳͙̦͐̔ͯ͛̅̾n̸͓̝̤̙͙͔ͪ̋̈́͒̒ͭ̈́̓ͮ̋̀̋̀̈ͩ́͌̄͘d̷̫̳̩̼̥̗̲̰͇͉̼̬̤͇̖ͮ̿ͬ͂ͦ̏̓ͮ̽͂̾̾ͯ͆͜͠ ̨̈́͒̇̏̄̑̓ͮͥ̒ͤͨ̋҉̴̴̟̱͙̟̫̩̗͔̝͔̀Ţ̵̝̟̖̭͇̻̳͖͉̺̖̖͙͙̺̐̈́̓ͯ̆̇̋ͩ͊̄̾̾ͬ̌̚͟ͅh̡͈̗̜͙̬̗̲̦̲̟̗̦̬͓̳ͧ̋̌͂͂ͨͬͦ̿̏̈́̋ͣ̒̕͡ͅͅe̗͇̰̰̥̪̗͑̔̓́̈́ͨ̊́̿̅ͯͥ̈́͐͗͘͢͝ ̡̢̛̯͎͓̰̘͎̦̪̯̪̥̰̲͇̠̲͔ͤͤ̇̅̆̋̂̆̈́ͤ̿͑ͅW̡͓͈̲̲͉̜͔̖͈̻̱͚̿̌͗̉ͤ͢͡ͅͅa̔̾͛̅͊͋͐҉̱̹̬͍͙̻̱l̢͎̟̬̙̼̱̫̮̘̼͔̭̅ͬ͑ͣ̏̾̅̓ͣ̿ͣ̈́̕͢͡ͅͅl̡̥̣͔̭̇̒͛͒͐̄̽͛̋ͥ̌͢͟͡.̷̰̝̮͔̟̦͈̥̬̻̥̬͎͓̻̲̇ͮ̿ͨͦ̽ͫ͟͢͝͠\r ̗̱͖͈͌̈ͦ͛ͮ̌͋̽̃͆̀͂ͨͧ̄̔̔ͭ̏͢Z̃̉̿ͮ̃̀͘͏͕̬̯̖͚̗͔Aͣ̑̈̓̈̑̈̀̿̚҉͙͍̦̗̦͙̠̝̩̯ͅͅL̴͖̞̞͙̱̻̥̬̜̦̐̇̉̈̽ͪ̅ͪ̂̔͌͑ͭ͐ͤ̈́̿̉͞ͅG̴̵̲̰̹̖͎͕ͯ̆̓̽͢͠Ŏ̶̡̺̼͙̣̞̩͕̥̟̝͕͔̯̞ͨ͒͊̂̊͂͗̒͆̾͆̌͆̃̎ͣͫ͜͡ͅ!̓̽̎̑̏́̓̓ͣ̀͏̱̩̭̣̹̺̗͜͞͞\r Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit\r amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus\r eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur\r laoreet. Nunc orci leo, varius eget ligula vulputate, consequat\r eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis\r cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris\r nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper\r porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum\r id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue\r non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa\r mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis\r lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus\r hendrerit a urna a lobortis.\r T̴̷͚͖̜͈̪͎͔̝̫̦̹͔̻̮͂ͬͬ̌ͣ̿ͤ͌ͥ͑̀̂ͬ̚͘͜͞ô̵͚̤̯̹͖͍̦̼̦̖̞̺͕̳̬͇͕̟̜̅̌̈́̑̏̕͘͝ ͍̼̗̫͈̭̦̱̬͚̱̞͓̜̭̼͇̰̞ͮ͗ͣ́ͪ̔ͪ̍̑̏́̀̽̍̔͘͜͜͝ȋ̐̽ͦ̓̔̅͏̧̢̖̭̝̳̹̯̤͈̫͔͔̠͓͉̠͖̠͜ͅn̷̯̗̗̠̱̥͕͉̥͉̳̫̙̅͗̌̒͂̏͑̎̌̌̊͌͘͘ͅͅv̧̜͕͍͙͍̬͕͍̳͉̠͍̹̮̻̜ͨ̏͒̍ͬ̈́͒̈ͥ͗ͣ̄̃ͤ͊̌͆̓o̸̧̎̓͂̊͢҉͍̼̘͇̱̪̠͎̥̹ķ̈́͗͆ͥ͐͑̆̎́͌ͩͯ̊̓͐ͬ̇̕҉̢͏͚̲̰̗̦e̿̀͐̽ͪ̈ͤͬ҉́͟͏̵̫̲̱̻̰̲̦͇̭̟̺͈̞̫̰̜͕͖ͅ ̡̰͎͓͚͓͉͈̮̻̣̮̟̩̬̮̈̋̊͆ͪ̄ͪ͒ͨͧ̇ͪ̇̑̚t̷̬̟͎̞͈̯͙̹̜ͩ̓ͪ͛͐̐ͤ̾̄̈͒̽̈́̑͒̏h̨̢̳͇͓͉̝ͫ̐̓̆̓ͮ̔̓̈́̇ͫe̟̬̣̗͚̬̾̉͋̽ͯ̌ͯͬ̂ͯͭ̓͛́̚͡ ̨̭̱͉̭͈̈̽̆̂͒͗̀ͥͩ͡h̻̼̱̹̗͖̙̗̲̤͓͇͚͚̻̞̥ͥ͛͌ͧ̚͟i̢̯̹̹̘̳̙ͩ̉ͥ͆̽̇̾̎͗̔̓͂͂́̓̌ͬv̧̡̛̟̜̠͉͖̘̲̻̯͚͍͓̯̻̲̹̥͇̻̿̓͛̊̌ͩͩ́ͩ̍͌̚e̵̾́̈́̏͌͌̊͗̏͋ͦ͘͡͏͚̜͚͎͉͍̱͙̖̹̣̘̥̤̹̟͠-̔̌͐́͒ͦͮ̇ͭ̄̏̊̇̍̕͏̩̥̰͚̟m̨̒ͫͦ̔̔͋҉̱̩̗͇̥̰̩̭͍͚͠į̵̷̻̗͉͕͚̣̼̺͉̦̮̠̆̀̐ͩ͒ͯͩͯ͞ͅn̢̫̤̝̝͚̺͍̱̦͚͂̿ͨ̇ͤ͠d̡ͯ͋̋ͧ̈́̒̈͏̛͏̵̤̬͍̗̞̠̟̞̺̠̥̹̱͉̜͍͎̤ ̷̸̢̰͓̘̯͎̤̫̘͓̙̟̳͇̹̥͈͙̮̩̅̋͌͗̓͊̓ͨͣ͗̓͐̈́ͩ̓ͣrͫ͂͌ͪ̏̐̍̾ͥ̓͗̈͆̈ͥ̀̾̚̚҉̴̶̭͇̗͙̘̯̦̭̮̪͚̥̙̯̠͙̪͡e̵̸̲͉̳̙͖͖̫̘̪͕̳͓̻̙͙ͥ̍͂̽ͨ̓̒̒̏ͬ͗ͧ̑̀͠p̵̸̛̦̣͙̳̳̩̣̼̘͈͂ͪͭͤ̎r̶̩̟̞̙͔̼ͫ̆ͦ̐̀̏̾̉̍ͬ̅ͧ͊ͪ̒̈́ͬ̃͞ẻ̴̼͙͍͎̠̀̅̔̃̒͐ͦ̏̆̅̓͋͢ͅš̆̈̆̋ͨ̅̍̇͂̒ͩͨ̂̐̓ͩ͏̸͔͔̯͇͚̤̪̬̗͈̰̦̯͚̕ę̢̱̠͙̲͉̗͚̮̪͖̙̞̦͉͕̗̳͙ͦ̆̋͌ͣ̅̊́ͅņ̴̷̫̪̦͇̺̹͉̗̬̞̲̭̜̪͒̏͂̂̎͊́̋͒̏̅̋̚͘t̷̶̨̟̦̗̦̱͌͌ͩ̀i̴̴̢̖͓͙̘͇̠̦̙̭̼͖̹̾̒̎̐ͥͭ͋ͥ̅͟ͅņ̫͙̹̦̳͈͙̬̫̮͕̰̩̣̘̘͐̀̓ͭͩͬͯ̎͛̿ͫ̊̔̅́̕͠gͥͩ̂͌̒̊̕͏̻͙͖̣͙͍̹͕̝͖̼̙̘͝ ͤ͐̓̒̓͋̐̃̇͊̓ͦ͐̚͢҉̢̨̟̠͉̳͖̲̩͙̕ć̷̡̫̩̞̯̼̝̼͖̤̳̻̘̪̤͈̦̭ͣ́͂͐̽͆̔̀̚͜h̶̢̹̹̙͔̱̓ͦ͌̋̎ͭ͒͋̒ͭ̌̃͌̿ͣ̆̅͑ą̙̳̬̞̬͚̜̤̱̙͇̠̟̈ͤ͋̃̀̓̓ͯ̍̀̽ͣ̐̈̿̌̕ǫ͋͂͐ͬ̿ͯ̂̈́͌̓̌ͧ̕͏̜͔̗͚͔̘̣͕̘̲͖̼͇͖̗̳ͅͅs̷̸̝̙̭̦̣̦̯̭̦͙̹̻͍͇̣̼͗̌͆ͨͭ̃ͮ͐̿̕.̮̝̠̱̺͖͓̼̦̱̉͂͛̓̑̔̓ͮ̈̊̔͗́͝\r ̛̣̺̻̼̙̼͓̱̬͕̩͕̲̳̭̗̍ͤ͋̒̆̄ͨ̿ͧ̓͠ͅI̷̻̤̳̲͔͈̖̬̰͔̪͇͇̟̋ͨ̋̍̉̔͝͞͝ͅn̶͕̭͖̠̣͚̹̪͆ͪ̇̂̅̾ͫ́̅̉ͭ̀͜v̖͉̩͕̣͔̭͕̩̲̖̇̀ͬ́̄͒̆͑͆ͪͤ͆̾̍ͯ̚͜ǫ̡̡̫͎̟̞̰̞̹͇̲̏ͨ̄͊̊̇͒̽͢ķ̶̪̙̰̥͙̞̹̭̺͍͕̙̲̮͊ͭ́͋͛͋̑̒͊̏̒̅͛̄̓͟i̴͎̹̞̥͖̒̄ͮ̒̾ͮͧ̀̚͡n̸̵͓̲̟̞̳͚̼̣͙͖̈ͦ͒̿̅̒̿͛͊̇ͧ̉g̡̧̪̩͚͙͓̪͓͚͉̥̪͍̙̻͖͇͗̑͊͑̾̍͊̀ͅ ̷̵̠͚̘̟͓̫̣̲͎̩̹̣̼̟͊́̏ͫ̆ͩ̓͋͆̿̽̓͘̕t̴̢̝̻̖̲̬̜̺̖̻ͩ̿ͫ͗̈́̔͑̐ͮͦ̽̉̓̚͜h̷̛̲͇̫͈̣̭͂ͭ̂͋ͭ̋̔ͮ̆ͩ͞ë̩͕͉̯͇͔͚̭̼̮̣͓̯́ͭ̀ͣ͗̋̉ͨͬ̒ͥͩ͆̓̓́̀̚͘͝ ̛̫̠̗̥̳͇͉̟̮̪̻̤̪͚̟̜̔̌͌̈͌ͪ̋̎̄ͯ͐ͦ́͞͠fͦ̂̈ͬ̇̅̓̓ͫͣ̉̂̉̚͘͡͡͏̼̖̟͚̙̳͔͎̲̫̦̯͔̣̼̹ě̷̶̫͎̞̺̪̪͇͈̞̳̏̋̋͋̾̓̽̓̑ͮ͊ͣ̋̃̅̀͡e͇̗͎̱͔̦̠̰̩̩͖͙̠̻̝ͯ̿̔̀͋͑ͧ͊̆̇̿ͤ̄ͯ̇̀͢͠ͅl̂̿ͯ͛̊̒̓̈́͏̵̪̦̞̤̫̤͇̙̗͕͎̪͕̙̻̳̗̕͟͢i̞̣̙͎͈̗̮͉̱̜̱̝̞̤͋ͯ͋͐̈́ͫ̉̊̏̀ͯͨ͢͟͝n̳̻̼̥̖͍̭̅͂̓̔̔ͦ̔́ͦ͊̀͛̈́ͬͦ͢͡͡ģ̶̡̳̰̻̙̞̱̳̣̤̫̫͕̤̮̰̬̪̜͋͒̎̈́̉̏̀ͬͯ͌̇͊̚ ́̽ͤͦ̾̔͢҉̛̤͍͉̺̙̮̗̜̟̀͝ơ̢̱͓͓̙͉̖̠̯̦̗͍̓̐̃̉̅̃ͨ͆́ͪ̂̒̀̊̃͆̔͡͡ͅf́ͬ̊ͯͫ̉̈́̽̉̚͢͏̡̺̬̖͇̫͉̱ ̴͇̦̗̙̼̬͓̯͖̮͓͎̗͈̻̈́͆ͭ̐ͦ́͛̀͋̐̌ͬ͑̒̊̿̃͞c̶̸̣͔̬͕̪̱̩̣̑̒̑̓̍̓͂̍̔͌̚͘͜͞h̶͈̱͇͉̳͍͍̰͈͖̬̥͚̯͓̞̹̋̔ͯ̑̃́̒̎̎͊̈́̍̚̕ạ̴̞̱̥͍͙̺͉͚͎̫̦͎̥ͩ̀̀̊ͥ͢o̵̧͕̜͓͈̬̰̫̮͙̹͉̩̝̩͎̓̆͗̿̊̀ͯ̃ͪ̊ͫ̽̉̓ͧ͗́̚͢ͅͅs̡ͫ͋̑ͮ̍̃͊̄ͬ̅̈́ͬ̍̇̔̈̅̍̀҉̜͓̝̘̘̮̼͖͎̻͓͖̖͙̞ͅ.͗ͬͭͩ̌̅͗͏̷̮̗͇͔͇͈̮͢\r ̨͚̲̫̠̼͖̝̻̉ͤ̅̂ͩ̀̇ͬͭ̀͜Ẅ̢́̉͌ͮͬͨ͊̏͌̇̐͊͟͠҉̼̰̦̩͇͕̟̭̪̲͕̥͖̰̪͈̀ͅͅį̷ͣͦ̉̍ͨ͂͂͑̃͂ͪ̊̈̋̄͜҉̨͚̟̲̯̹̺̝̭̺̙͖͍t̼͓̰̩͙̦͓̟͚͖̀ͯ͛̍̈́͑͂̍̋́h̛̼̺̘̥̠̼̼̭͙̮͚̱̍ͯ̓̃̐̂̇͟ ̴̛͖͔̰̠̺̥̲ͮ̍ͫ̽͜õ̒ͯ̒̓ͦ̈́͑̔̒̓̎ͤ͑҉̸̭̱̤̭̬͈ų̙̫̤͖̺̫̱͓͓̗̪͇̩̙̔̉̊͂ͪ̇͢͟͞ͅt̸̬̣̫̞̫̅͐ͮ̌͌̈́̀̀͘ ̷̴̨̖̙̹͚ͬ̈́̈ͯͨͮ̇̈́̋̈́ͭ͛̑̉͊̕ö̡̍ͥ̂ͬͪͧ͒ͧ̏̓̇̂̄͆̌ͫͤ͢͠͝͏̖̱̯̘͙̰̖͎̰͓̟̤ṙ̡̬̟̬̜̪̮̺͖̗̘͈̟ͨ͐͗̑͒̐d̢ͭͫ̊̏ͬͥ͋́̌̈́ͮ̆ͬ̐̌̎͏̵̷̡̞̲̹̙͕̮̮͚ḙ̴̸̠͔͎̥͇͖͕̘̍̓̏̐ͩͩ̈́ͦ̐̋ͤ̎̾̌̏͊̊́̚͞ͅr̸͈̗̣̲̗̣̬̤ͦ̎ͫ̏̀ͥͪ̋ͧ̄͑̋͒͌͋ͦ̉͟͞.ͨͣ̽̈́͒̄ͮ̀͋͋͏̴̧̯̺̙̱̻͙̜\r ̡̣̞̠͓̰͍̠͕̭̺̼͊̽̿͊ͮ̐̓̒̊͒̔̓͐ͨ̈̌́T̸̸̓́̋ͬ́͆ͨͫ͌͂ͣ̋͒҉̺̝͎̟͖͚̠h̸̡̰̜̦͇͕̪̝̳͕͉̲̝̑ͥ͋ͧ̎̆͌͟e̛̹͍͍̫̙̞̪̭̙̟͙̱̺̮̳͕̜ͫ̓ͭ͊ͫ͆̀̚͟͡ ̿͂̄ͧ̔̎ͧ͑̾̀̓͏̦͍̳͈̳͔̘̖̲̯̰̟̝̳̖̦N̶̡̧̦̮̟̦̩̰̣̝̆̀͊̔͢e͛̄ͮͦͨ͂̔̓̍̄̉͆͊̑̑̆̚͏̜̗͎̝̼̯̥̜͖͍̪̝͞ͅͅz̨̛̀̾ͪ͗̉́͠͏͚̫̼̫̜̣pͪͦ͌̄ͥ̆ͣͩ͋̉́̏͞͏̥̜̝̳̱̞̙̳̤͙̟̟̮̦ȅ̷̩̟͉̯͕͔̘̺̥̻̻ͧ̊̅̽ͣ͑̓̑̽ͦ̾͌͜r̴̭̥̲̲̤͚͈̰͇̰͈̰̹ͫ̒ͯ̿͒ͧ̊͆͒ͣ́ḍ̭̟̤̈́̌̓̈́ͫ͐̍͂͞į̛̞̝̮̣͙͙̤̇̂̓̎͋̿̓̎̄̈́ͧ̓ͩ̐̓̄̋ͭ͞͠a͋̔̋ͫ̂͐͂҉̸̛̥̩̯̯̤̝͔̠̝̯̪̥̩̻̼̮n͌ͣ̂͋̿̚҉̛̙̲̺̯͇͓̝̯̪̟͔̩͟ͅ ̢̨͚̻̗̘͖̯̐ͥ͋̽ͯ̎̈́͋̏̄͋̆̑̊̆̚̕͟ͅh̢̛̗̱̭͇͖̰̮̮͈̲͍̯̟ͭ͊̎̽̓ͦͤ͠ï̛̘̝̦͎̦̭̠͖̳͎̮̼̏͐ͧ̒̒͐͑ͪͫ̋̽̚̚͜v̴̮͕̝̮̞͐̄͗̋͒ͤ̎̈̑ͬͮ̄̾ͤ̓̾͊͗͟é̶̷̡̩͖̰̫͓̟ͮͬͣ͊-ͦ͛ͩͤͨͨ̆̄͏̼̜̭͔̳͈͖̳̩͢ͅͅm̷̴̓́̓͛͒̾̍̉҉̛̗̹̠̣̪̺͎̖̝͚̖͙i̛̥͓̬̫͉͕͉͆͒ͧ̂̿̔̔͆̆̓̍͊̀͜n͌ͧͣ̅̌̎ͦͦ͑̑ͭ̆ͬ̀ͤ̀ͣ̚҉͎̰̱͚͈͈̬̹͕̺̙͙̼͘͘͞d̶͖̫̟̲͕̺̠͎̘͕̱̼͙̪̪̩͙̅̅̑̓̇͑̊̉͜͞ ̶̵̷̴̡̠͚̪͕̣̱̖̱̗̤̭̭͔͖͚ͧͤͥ͒̌ͪ͊͂͒̓͂ͧͧ̇̇͐̑̔ͅͅơ̵̲̲͇̯̰͇̜̣͕͕͓̲̤̲͔͚̞͑͗ͤ̓́̚͠ͅf̢̧̛̩̯̼̫͖̾ͣ͌̾̉́̈́̑̈́̚͞͞ͅ ͤͩ́͋͒ͫͬͣ̋̅̆҉̧̱̻͓͕͉̹̫̫̞̯̪̙̩͍̦͔̖̮̀͟ͅc͉̠̜̩̟͕͎̙̣̮̘̼͋ͯ̍ͨ̅̄ͫ̈̋ͫ̊͡͝ȟ̸̨ͯͦ̂̉̇̾̆ͭ̋̐̈̆̀̚͜҉͚͕̻̖a̶̴̛͚̗͙̳̬̲͚ͦ́̐ͥ́̔̅̑̎͐̑ͯ̾ͤͥͧ͡ò̶̧̞̪̦̥̪̻̦̝̳̬̔͛͛ͣ̋̌̔ͫ̂̽ͫ͘͠s̸̖̣̬̤̫͇̫̣̑͆͒̎̏́͟.̴̗̤̭͉̯̻̤͕̌ͯ̍ͤ̓͌ͤ̈̆̉ͦ̇́̚͘͟͝ͅ ̯̹̪͓̬͌̔̌ͬ̀͘͢͡͡Z̡̩̲̩̰̫̩̟͍̰͖͔̭ͣ̆̾ͭ̀́͞ͅa̡̡̙̜̭͇͎͔̙̞̫͓̜͉͔̬ͭ̈ͨ̉͆ͣͫ̃͌̓͌́ͣͥ̒̌͊͘͝l̢̨̡̯̙̫͖̫̺̘̬̟͈͌̊ͧͫͦ̉̃ͩͦ̒ͯ̇̌̓͛͟͝ͅg̵̙̼̼ͪ͂ͭ͗̈̕ȯ̅ͧ̓ͪ́̂͑̐ͩͥͬ̊̑͆̇͒ͫͣ͝҉͎̟̜̥͎̮̣͉̖̟̯̦̖͙͙͞ͅ.̈̑ͩ̇̂ͬ̓ͬ͊͂ͨ̽͠͏̺͎̞̦̜͍͚̯̯͔̝̞̻̩̖\r ̷̰̪͍͎͔͒ͯͥ̾̉͆ͤ̊̓̂͋̀͆H̸̸̹̞̙̺͎̠̯̤ͨ̉̍ͬͤ̓̐͌ͥͮ͞eͣ̈̾͛́͏͕̗͍̜̼͎͚̟̬̣̝̕ͅͅ ̴̛̩̗̼̝̣̩͚͇̯́̉͋̂̍͂̌ͮ͋̾͜͠wͮ̽̓ͭ̿͐̽̐̽͆̓͝҉̡̼̲͖̪̥h̢̢̛͍̰̰̻̱̼̰̹̖̖̪̝̥̘̎̀ͪ͒̾ͫͬ̆̑o̡̗̠̞̱̥͎̰͎͍̫̻͓͇͓͐ͥͯ͂̅͠ͅ ̡̛̏͑ͦ̓͊ͮͫͯͭ̌͒̆̍̈͠҉͖͚̪̫̗̮W̴̐̊͋̾ͥͫ҉͎̞͔̯̫̹͖̰͉̹̼͎̰̱͓̻̀a̶̩̤̙̣͎̳̭̲̗̠͉̳̭̭̦̞͎̮̅͌̾͗̾͛̇̀́͟͞ͅi̷̡ͣ̆̌͋͒͒́͘͏̮̺̩͎͇̜͍̫ṯ̴̢͖̥̖͇͎̦̦̹̖͇̪ͭ̅̍͐̇͒͋̽̏̿̒͆ͧ̄͋ͧͩ͒͜s̙̥̖̘̖͚̭̤̮̖̘̰̫̟̈́ͣ̍ͧ͐ͥ̏͆̃̿͒̔͐́̚͟ͅ ̨ͭ̌ͬͯ͆̒͋ͭ̔̿ͧ̅̓ͣ͡͏͇̟͉̥͔̬̼͚͙͚B̛̜̮̤͓̝̪̪͈͕̘̜͙̰̮̫̘̣͓͔̅ͩ͊̔ͦͯ́̌́͆ͭ̓́e̶̢̡̦͇͕̙͈͖͕̦̬̫͕̣̺̒̿͂͐͒͋͂ͦ́͋ͤ̿ͬ̊ͣ͗̑̽͜ͅͅh̸͑ͫͧ̑ͬͧ̈́̎̃ͣ̊̾͂ͨͤ̓͐̐̑͏̸̭͓̘͉̩i̧̧̭̣͈̝̺̼̺̠͉̞̜̲̳͙̦͐̔ͯ͛̅̾n̸͓̝̤̙͙͔ͪ̋̈́͒̒ͭ̈́̓ͮ̋̀̋̀̈ͩ́͌̄͘d̷̫̳̩̼̥̗̲̰͇͉̼̬̤͇̖ͮ̿ͬ͂ͦ̏̓ͮ̽͂̾̾ͯ͆͜͠ ̨̈́͒̇̏̄̑̓ͮͥ̒ͤͨ̋҉̴̴̟̱͙̟̫̩̗͔̝͔̀Ţ̵̝̟̖̭͇̻̳͖͉̺̖̖͙͙̺̐̈́̓ͯ̆̇̋ͩ͊̄̾̾ͬ̌̚͟ͅh̡͈̗̜͙̬̗̲̦̲̟̗̦̬͓̳ͧ̋̌͂͂ͨͬͦ̿̏̈́̋ͣ̒̕͡ͅͅe̗͇̰̰̥̪̗͑̔̓́̈́ͨ̊́̿̅ͯͥ̈́͐͗͘͢͝ ̡̢̛̯͎͓̰̘͎̦̪̯̪̥̰̲͇̠̲͔ͤͤ̇̅̆̋̂̆̈́ͤ̿͑ͅW̡͓͈̲̲͉̜͔̖͈̻̱͚̿̌͗̉ͤ͢͡ͅͅa̔̾͛̅͊͋͐҉̱̹̬͍͙̻̱l̢͎̟̬̙̼̱̫̮̘̼͔̭̅ͬ͑ͣ̏̾̅̓ͣ̿ͣ̈́̕͢͡ͅͅl̡̥̣͔̭̇̒͛͒͐̄̽͛̋ͥ̌͢͟͡.̷̰̝̮͔̟̦͈̥̬̻̥̬͎͓̻̲̇ͮ̿ͨͦ̽ͫ͟͢͝͠\r ̗̱͖͈͌̈ͦ͛ͮ̌͋̽̃͆̀͂ͨͧ̄̔̔ͭ̏͢Z̃̉̿ͮ̃̀͘͏͕̬̯̖͚̗͔Aͣ̑̈̓̈̑̈̀̿̚҉͙͍̦̗̦͙̠̝̩̯ͅͅL̴͖̞̞͙̱̻̥̬̜̦̐̇̉̈̽ͪ̅ͪ̂̔͌͑ͭ͐ͤ̈́̿̉͞ͅG̴̵̲̰̹̖͎͕ͯ̆̓̽͢͠Ŏ̶̡̺̼͙̣̞̩͕̥̟̝͕͔̯̞ͨ͒͊̂̊͂͗̒͆̾͆̌͆̃̎ͣͫ͜͡ͅ!̓̽̎̑̏́̓̓ͣ̀͏̱̩̭̣̹̺̗͜͞͞\r Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque.\r Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante\r tortor. Suspendisse vitae consectetur sem, at sollicitudin neque.\r Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam\r dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et\r euismod non, hendrerit sed urna. Sed convallis porttitor est, vel\r aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie.\r Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex\r ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu\r eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat\r ante.\r Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit\r amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus\r eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur\r laoreet. Nunc orci leo, varius eget ligula vulputate, consequat\r eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis\r cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris\r nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper\r porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum\r id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue\r non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa\r mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis\r lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus\r hendrerit a urna a lobortis.\r T̴̷͚͖̜͈̪͎͔̝̫̦̹͔̻̮͂ͬͬ̌ͣ̿ͤ͌ͥ͑̀̂ͬ̚͘͜͞ô̵͚̤̯̹͖͍̦̼̦̖̞̺͕̳̬͇͕̟̜̅̌̈́̑̏̕͘͝ ͍̼̗̫͈̭̦̱̬͚̱̞͓̜̭̼͇̰̞ͮ͗ͣ́ͪ̔ͪ̍̑̏́̀̽̍̔͘͜͜͝ȋ̐̽ͦ̓̔̅͏̧̢̖̭̝̳̹̯̤͈̫͔͔̠͓͉̠͖̠͜ͅn̷̯̗̗̠̱̥͕͉̥͉̳̫̙̅͗̌̒͂̏͑̎̌̌̊͌͘͘ͅͅv̧̜͕͍͙͍̬͕͍̳͉̠͍̹̮̻̜ͨ̏͒̍ͬ̈́͒̈ͥ͗ͣ̄̃ͤ͊̌͆̓o̸̧̎̓͂̊͢҉͍̼̘͇̱̪̠͎̥̹ķ̈́͗͆ͥ͐͑̆̎́͌ͩͯ̊̓͐ͬ̇̕҉̢͏͚̲̰̗̦e̿̀͐̽ͪ̈ͤͬ҉́͟͏̵̫̲̱̻̰̲̦͇̭̟̺͈̞̫̰̜͕͖ͅ ̡̰͎͓͚͓͉͈̮̻̣̮̟̩̬̮̈̋̊͆ͪ̄ͪ͒ͨͧ̇ͪ̇̑̚t̷̬̟͎̞͈̯͙̹̜ͩ̓ͪ͛͐̐ͤ̾̄̈͒̽̈́̑͒̏h̨̢̳͇͓͉̝ͫ̐̓̆̓ͮ̔̓̈́̇ͫe̟̬̣̗͚̬̾̉͋̽ͯ̌ͯͬ̂ͯͭ̓͛́̚͡ ̨̭̱͉̭͈̈̽̆̂͒͗̀ͥͩ͡h̻̼̱̹̗͖̙̗̲̤͓͇͚͚̻̞̥ͥ͛͌ͧ̚͟i̢̯̹̹̘̳̙ͩ̉ͥ͆̽̇̾̎͗̔̓͂͂́̓̌ͬv̧̡̛̟̜̠͉͖̘̲̻̯͚͍͓̯̻̲̹̥͇̻̿̓͛̊̌ͩͩ́ͩ̍͌̚e̵̾́̈́̏͌͌̊͗̏͋ͦ͘͡͏͚̜͚͎͉͍̱͙̖̹̣̘̥̤̹̟͠-̔̌͐́͒ͦͮ̇ͭ̄̏̊̇̍̕͏̩̥̰͚̟m̨̒ͫͦ̔̔͋҉̱̩̗͇̥̰̩̭͍͚͠į̵̷̻̗͉͕͚̣̼̺͉̦̮̠̆̀̐ͩ͒ͯͩͯ͞ͅn̢̫̤̝̝͚̺͍̱̦͚͂̿ͨ̇ͤ͠d̡ͯ͋̋ͧ̈́̒̈͏̛͏̵̤̬͍̗̞̠̟̞̺̠̥̹̱͉̜͍͎̤ ̷̸̢̰͓̘̯͎̤̫̘͓̙̟̳͇̹̥͈͙̮̩̅̋͌͗̓͊̓ͨͣ͗̓͐̈́ͩ̓ͣrͫ͂͌ͪ̏̐̍̾ͥ̓͗̈͆̈ͥ̀̾̚̚҉̴̶̭͇̗͙̘̯̦̭̮̪͚̥̙̯̠͙̪͡e̵̸̲͉̳̙͖͖̫̘̪͕̳͓̻̙͙ͥ̍͂̽ͨ̓̒̒̏ͬ͗ͧ̑̀͠p̵̸̛̦̣͙̳̳̩̣̼̘͈͂ͪͭͤ̎r̶̩̟̞̙͔̼ͫ̆ͦ̐̀̏̾̉̍ͬ̅ͧ͊ͪ̒̈́ͬ̃͞ẻ̴̼͙͍͎̠̀̅̔̃̒͐ͦ̏̆̅̓͋͢ͅš̆̈̆̋ͨ̅̍̇͂̒ͩͨ̂̐̓ͩ͏̸͔͔̯͇͚̤̪̬̗͈̰̦̯͚̕ę̢̱̠͙̲͉̗͚̮̪͖̙̞̦͉͕̗̳͙ͦ̆̋͌ͣ̅̊́ͅņ̴̷̫̪̦͇̺̹͉̗̬̞̲̭̜̪͒̏͂̂̎͊́̋͒̏̅̋̚͘t̷̶̨̟̦̗̦̱͌͌ͩ̀i̴̴̢̖͓͙̘͇̠̦̙̭̼͖̹̾̒̎̐ͥͭ͋ͥ̅͟ͅņ̫͙̹̦̳͈͙̬̫̮͕̰̩̣̘̘͐̀̓ͭͩͬͯ̎͛̿ͫ̊̔̅́̕͠gͥͩ̂͌̒̊̕͏̻͙͖̣͙͍̹͕̝͖̼̙̘͝ ͤ͐̓̒̓͋̐̃̇͊̓ͦ͐̚͢҉̢̨̟̠͉̳͖̲̩͙̕ć̷̡̫̩̞̯̼̝̼͖̤̳̻̘̪̤͈̦̭ͣ́͂͐̽͆̔̀̚͜h̶̢̹̹̙͔̱̓ͦ͌̋̎ͭ͒͋̒ͭ̌̃͌̿ͣ̆̅͑ą̙̳̬̞̬͚̜̤̱̙͇̠̟̈ͤ͋̃̀̓̓ͯ̍̀̽ͣ̐̈̿̌̕ǫ͋͂͐ͬ̿ͯ̂̈́͌̓̌ͧ̕͏̜͔̗͚͔̘̣͕̘̲͖̼͇͖̗̳ͅͅs̷̸̝̙̭̦̣̦̯̭̦͙̹̻͍͇̣̼͗̌͆ͨͭ̃ͮ͐̿̕.̮̝̠̱̺͖͓̼̦̱̉͂͛̓̑̔̓ͮ̈̊̔͗́͝\r ̛̣̺̻̼̙̼͓̱̬͕̩͕̲̳̭̗̍ͤ͋̒̆̄ͨ̿ͧ̓͠ͅI̷̻̤̳̲͔͈̖̬̰͔̪͇͇̟̋ͨ̋̍̉̔͝͞͝ͅn̶͕̭͖̠̣͚̹̪͆ͪ̇̂̅̾ͫ́̅̉ͭ̀͜v̖͉̩͕̣͔̭͕̩̲̖̇̀ͬ́̄͒̆͑͆ͪͤ͆̾̍ͯ̚͜ǫ̡̡̫͎̟̞̰̞̹͇̲̏ͨ̄͊̊̇͒̽͢ķ̶̪̙̰̥͙̞̹̭̺͍͕̙̲̮͊ͭ́͋͛͋̑̒͊̏̒̅͛̄̓͟i̴͎̹̞̥͖̒̄ͮ̒̾ͮͧ̀̚͡n̸̵͓̲̟̞̳͚̼̣͙͖̈ͦ͒̿̅̒̿͛͊̇ͧ̉g̡̧̪̩͚͙͓̪͓͚͉̥̪͍̙̻͖͇͗̑͊͑̾̍͊̀ͅ ̷̵̠͚̘̟͓̫̣̲͎̩̹̣̼̟͊́̏ͫ̆ͩ̓͋͆̿̽̓͘̕t̴̢̝̻̖̲̬̜̺̖̻ͩ̿ͫ͗̈́̔͑̐ͮͦ̽̉̓̚͜h̷̛̲͇̫͈̣̭͂ͭ̂͋ͭ̋̔ͮ̆ͩ͞ë̩͕͉̯͇͔͚̭̼̮̣͓̯́ͭ̀ͣ͗̋̉ͨͬ̒ͥͩ͆̓̓́̀̚͘͝ ̛̫̠̗̥̳͇͉̟̮̪̻̤̪͚̟̜̔̌͌̈͌ͪ̋̎̄ͯ͐ͦ́͞͠fͦ̂̈ͬ̇̅̓̓ͫͣ̉̂̉̚͘͡͡͏̼̖̟͚̙̳͔͎̲̫̦̯͔̣̼̹ě̷̶̫͎̞̺̪̪͇͈̞̳̏̋̋͋̾̓̽̓̑ͮ͊ͣ̋̃̅̀͡e͇̗͎̱͔̦̠̰̩̩͖͙̠̻̝ͯ̿̔̀͋͑ͧ͊̆̇̿ͤ̄ͯ̇̀͢͠ͅl̂̿ͯ͛̊̒̓̈́͏̵̪̦̞̤̫̤͇̙̗͕͎̪͕̙̻̳̗̕͟͢i̞̣̙͎͈̗̮͉̱̜̱̝̞̤͋ͯ͋͐̈́ͫ̉̊̏̀ͯͨ͢͟͝n̳̻̼̥̖͍̭̅͂̓̔̔ͦ̔́ͦ͊̀͛̈́ͬͦ͢͡͡ģ̶̡̳̰̻̙̞̱̳̣̤̫̫͕̤̮̰̬̪̜͋͒̎̈́̉̏̀ͬͯ͌̇͊̚ ́̽ͤͦ̾̔͢҉̛̤͍͉̺̙̮̗̜̟̀͝ơ̢̱͓͓̙͉̖̠̯̦̗͍̓̐̃̉̅̃ͨ͆́ͪ̂̒̀̊̃͆̔͡͡ͅf́ͬ̊ͯͫ̉̈́̽̉̚͢͏̡̺̬̖͇̫͉̱ ̴͇̦̗̙̼̬͓̯͖̮͓͎̗͈̻̈́͆ͭ̐ͦ́͛̀͋̐̌ͬ͑̒̊̿̃͞c̶̸̣͔̬͕̪̱̩̣̑̒̑̓̍̓͂̍̔͌̚͘͜͞h̶͈̱͇͉̳͍͍̰͈͖̬̥͚̯͓̞̹̋̔ͯ̑̃́̒̎̎͊̈́̍̚̕ạ̴̞̱̥͍͙̺͉͚͎̫̦͎̥ͩ̀̀̊ͥ͢o̵̧͕̜͓͈̬̰̫̮͙̹͉̩̝̩͎̓̆͗̿̊̀ͯ̃ͪ̊ͫ̽̉̓ͧ͗́̚͢ͅͅs̡ͫ͋̑ͮ̍̃͊̄ͬ̅̈́ͬ̍̇̔̈̅̍̀҉̜͓̝̘̘̮̼͖͎̻͓͖̖͙̞ͅ.͗ͬͭͩ̌̅͗͏̷̮̗͇͔͇͈̮͢\r ̨͚̲̫̠̼͖̝̻̉ͤ̅̂ͩ̀̇ͬͭ̀͜Ẅ̢́̉͌ͮͬͨ͊̏͌̇̐͊͟͠҉̼̰̦̩͇͕̟̭̪̲͕̥͖̰̪͈̀ͅͅį̷ͣͦ̉̍ͨ͂͂͑̃͂ͪ̊̈̋̄͜҉̨͚̟̲̯̹̺̝̭̺̙͖͍t̼͓̰̩͙̦͓̟͚͖̀ͯ͛̍̈́͑͂̍̋́h̛̼̺̘̥̠̼̼̭͙̮͚̱̍ͯ̓̃̐̂̇͟ ̴̛͖͔̰̠̺̥̲ͮ̍ͫ̽͜õ̒ͯ̒̓ͦ̈́͑̔̒̓̎ͤ͑҉̸̭̱̤̭̬͈ų̙̫̤͖̺̫̱͓͓̗̪͇̩̙̔̉̊͂ͪ̇͢͟͞ͅt̸̬̣̫̞̫̅͐ͮ̌͌̈́̀̀͘ ̷̴̨̖̙̹͚ͬ̈́̈ͯͨͮ̇̈́̋̈́ͭ͛̑̉͊̕ö̡̍ͥ̂ͬͪͧ͒ͧ̏̓̇̂̄͆̌ͫͤ͢͠͝͏̖̱̯̘͙̰̖͎̰͓̟̤ṙ̡̬̟̬̜̪̮̺͖̗̘͈̟ͨ͐͗̑͒̐d̢ͭͫ̊̏ͬͥ͋́̌̈́ͮ̆ͬ̐̌̎͏̵̷̡̞̲̹̙͕̮̮͚ḙ̴̸̠͔͎̥͇͖͕̘̍̓̏̐ͩͩ̈́ͦ̐̋ͤ̎̾̌̏͊̊́̚͞ͅr̸͈̗̣̲̗̣̬̤ͦ̎ͫ̏̀ͥͪ̋ͧ̄͑̋͒͌͋ͦ̉͟͞.ͨͣ̽̈́͒̄ͮ̀͋͋͏̴̧̯̺̙̱̻͙̜\r ̡̣̞̠͓̰͍̠͕̭̺̼͊̽̿͊ͮ̐̓̒̊͒̔̓͐ͨ̈̌́T̸̸̓́̋ͬ́͆ͨͫ͌͂ͣ̋͒҉̺̝͎̟͖͚̠h̸̡̰̜̦͇͕̪̝̳͕͉̲̝̑ͥ͋ͧ̎̆͌͟e̛̹͍͍̫̙̞̪̭̙̟͙̱̺̮̳͕̜ͫ̓ͭ͊ͫ͆̀̚͟͡ ̿͂̄ͧ̔̎ͧ͑̾̀̓͏̦͍̳͈̳͔̘̖̲̯̰̟̝̳̖̦N̶̡̧̦̮̟̦̩̰̣̝̆̀͊̔͢e͛̄ͮͦͨ͂̔̓̍̄̉͆͊̑̑̆̚͏̜̗͎̝̼̯̥̜͖͍̪̝͞ͅͅz̨̛̀̾ͪ͗̉́͠͏͚̫̼̫̜̣pͪͦ͌̄ͥ̆ͣͩ͋̉́̏͞͏̥̜̝̳̱̞̙̳̤͙̟̟̮̦ȅ̷̩̟͉̯͕͔̘̺̥̻̻ͧ̊̅̽ͣ͑̓̑̽ͦ̾͌͜r̴̭̥̲̲̤͚͈̰͇̰͈̰̹ͫ̒ͯ̿͒ͧ̊͆͒ͣ́ḍ̭̟̤̈́̌̓̈́ͫ͐̍͂͞į̛̞̝̮̣͙͙̤̇̂̓̎͋̿̓̎̄̈́ͧ̓ͩ̐̓̄̋ͭ͞͠a͋̔̋ͫ̂͐͂҉̸̛̥̩̯̯̤̝͔̠̝̯̪̥̩̻̼̮n͌ͣ̂͋̿̚҉̛̙̲̺̯͇͓̝̯̪̟͔̩͟ͅ ̢̨͚̻̗̘͖̯̐ͥ͋̽ͯ̎̈́͋̏̄͋̆̑̊̆̚̕͟ͅh̢̛̗̱̭͇͖̰̮̮͈̲͍̯̟ͭ͊̎̽̓ͦͤ͠ï̛̘̝̦͎̦̭̠͖̳͎̮̼̏͐ͧ̒̒͐͑ͪͫ̋̽̚̚͜v̴̮͕̝̮̞͐̄͗̋͒ͤ̎̈̑ͬͮ̄̾ͤ̓̾͊͗͟é̶̷̡̩͖̰̫͓̟ͮͬͣ͊-ͦ͛ͩͤͨͨ̆̄͏̼̜̭͔̳͈͖̳̩͢ͅͅm̷̴̓́̓͛͒̾̍̉҉̛̗̹̠̣̪̺͎̖̝͚̖͙i̛̥͓̬̫͉͕͉͆͒ͧ̂̿̔̔͆̆̓̍͊̀͜n͌ͧͣ̅̌̎ͦͦ͑̑ͭ̆ͬ̀ͤ̀ͣ̚҉͎̰̱͚͈͈̬̹͕̺̙͙̼͘͘͞d̶͖̫̟̲͕̺̠͎̘͕̱̼͙̪̪̩͙̅̅̑̓̇͑̊̉͜͞ ̶̵̷̴̡̠͚̪͕̣̱̖̱̗̤̭̭͔͖͚ͧͤͥ͒̌ͪ͊͂͒̓͂ͧͧ̇̇͐̑̔ͅͅơ̵̲̲͇̯̰͇̜̣͕͕͓̲̤̲͔͚̞͑͗ͤ̓́̚͠ͅf̢̧̛̩̯̼̫͖̾ͣ͌̾̉́̈́̑̈́̚͞͞ͅ ͤͩ́͋͒ͫͬͣ̋̅̆҉̧̱̻͓͕͉̹̫̫̞̯̪̙̩͍̦͔̖̮̀͟ͅc͉̠̜̩̟͕͎̙̣̮̘̼͋ͯ̍ͨ̅̄ͫ̈̋ͫ̊͡͝ȟ̸̨ͯͦ̂̉̇̾̆ͭ̋̐̈̆̀̚͜҉͚͕̻̖a̶̴̛͚̗͙̳̬̲͚ͦ́̐ͥ́̔̅̑̎͐̑ͯ̾ͤͥͧ͡ò̶̧̞̪̦̥̪̻̦̝̳̬̔͛͛ͣ̋̌̔ͫ̂̽ͫ͘͠s̸̖̣̬̤̫͇̫̣̑͆͒̎̏́͟.̴̗̤̭͉̯̻̤͕̌ͯ̍ͤ̓͌ͤ̈̆̉ͦ̇́̚͘͟͝ͅ ̯̹̪͓̬͌̔̌ͬ̀͘͢͡͡Z̡̩̲̩̰̫̩̟͍̰͖͔̭ͣ̆̾ͭ̀́͞ͅa̡̡̙̜̭͇͎͔̙̞̫͓̜͉͔̬ͭ̈ͨ̉͆ͣͫ̃͌̓͌́ͣͥ̒̌͊͘͝l̢̨̡̯̙̫͖̫̺̘̬̟͈͌̊ͧͫͦ̉̃ͩͦ̒ͯ̇̌̓͛͟͝ͅg̵̙̼̼ͪ͂ͭ͗̈̕ȯ̅ͧ̓ͪ́̂͑̐ͩͥͬ̊̑͆̇͒ͫͣ͝҉͎̟̜̥͎̮̣͉̖̟̯̦̖͙͙͞ͅ.̈̑ͩ̇̂ͬ̓ͬ͊͂ͨ̽͠͏̺͎̞̦̜͍͚̯̯͔̝̞̻̩̖\r ̷̰̪͍͎͔͒ͯͥ̾̉͆ͤ̊̓̂͋̀͆H̸̸̹̞̙̺͎̠̯̤ͨ̉̍ͬͤ̓̐͌ͥͮ͞eͣ̈̾͛́͏͕̗͍̜̼͎͚̟̬̣̝̕ͅͅ ̴̛̩̗̼̝̣̩͚͇̯́̉͋̂̍͂̌ͮ͋̾͜͠wͮ̽̓ͭ̿͐̽̐̽͆̓͝҉̡̼̲͖̪̥h̢̢̛͍̰̰̻̱̼̰̹̖̖̪̝̥̘̎̀ͪ͒̾ͫͬ̆̑o̡̗̠̞̱̥͎̰͎͍̫̻͓͇͓͐ͥͯ͂̅͠ͅ ̡̛̏͑ͦ̓͊ͮͫͯͭ̌͒̆̍̈͠҉͖͚̪̫̗̮W̴̐̊͋̾ͥͫ҉͎̞͔̯̫̹͖̰͉̹̼͎̰̱͓̻̀a̶̩̤̙̣͎̳̭̲̗̠͉̳̭̭̦̞͎̮̅͌̾͗̾͛̇̀́͟͞ͅi̷̡ͣ̆̌͋͒͒́͘͏̮̺̩͎͇̜͍̫ṯ̴̢͖̥̖͇͎̦̦̹̖͇̪ͭ̅̍͐̇͒͋̽̏̿̒͆ͧ̄͋ͧͩ͒͜s̙̥̖̘̖͚̭̤̮̖̘̰̫̟̈́ͣ̍ͧ͐ͥ̏͆̃̿͒̔͐́̚͟ͅ ̨ͭ̌ͬͯ͆̒͋ͭ̔̿ͧ̅̓ͣ͡͏͇̟͉̥͔̬̼͚͙͚B̛̜̮̤͓̝̪̪͈͕̘̜͙̰̮̫̘̣͓͔̅ͩ͊̔ͦͯ́̌́͆ͭ̓́e̶̢̡̦͇͕̙͈͖͕̦̬̫͕̣̺̒̿͂͐͒͋͂ͦ́͋ͤ̿ͬ̊ͣ͗̑̽͜ͅͅh̸͑ͫͧ̑ͬͧ̈́̎̃ͣ̊̾͂ͨͤ̓͐̐̑͏̸̭͓̘͉̩i̧̧̭̣͈̝̺̼̺̠͉̞̜̲̳͙̦͐̔ͯ͛̅̾n̸͓̝̤̙͙͔ͪ̋̈́͒̒ͭ̈́̓ͮ̋̀̋̀̈ͩ́͌̄͘d̷̫̳̩̼̥̗̲̰͇͉̼̬̤͇̖ͮ̿ͬ͂ͦ̏̓ͮ̽͂̾̾ͯ͆͜͠ ̨̈́͒̇̏̄̑̓ͮͥ̒ͤͨ̋҉̴̴̟̱͙̟̫̩̗͔̝͔̀Ţ̵̝̟̖̭͇̻̳͖͉̺̖̖͙͙̺̐̈́̓ͯ̆̇̋ͩ͊̄̾̾ͬ̌̚͟ͅh̡͈̗̜͙̬̗̲̦̲̟̗̦̬͓̳ͧ̋̌͂͂ͨͬͦ̿̏̈́̋ͣ̒̕͡ͅͅe̗͇̰̰̥̪̗͑̔̓́̈́ͨ̊́̿̅ͯͥ̈́͐͗͘͢͝ ̡̢̛̯͎͓̰̘͎̦̪̯̪̥̰̲͇̠̲͔ͤͤ̇̅̆̋̂̆̈́ͤ̿͑ͅW̡͓͈̲̲͉̜͔̖͈̻̱͚̿̌͗̉ͤ͢͡ͅͅa̔̾͛̅͊͋͐҉̱̹̬͍͙̻̱l̢͎̟̬̙̼̱̫̮̘̼͔̭̅ͬ͑ͣ̏̾̅̓ͣ̿ͣ̈́̕͢͡ͅͅl̡̥̣͔̭̇̒͛͒͐̄̽͛̋ͥ̌͢͟͡.̷̰̝̮͔̟̦͈̥̬̻̥̬͎͓̻̲̇ͮ̿ͨͦ̽ͫ͟͢͝͠\r ̗̱͖͈͌̈ͦ͛ͮ̌͋̽̃͆̀͂ͨͧ̄̔̔ͭ̏͢Z̃̉̿ͮ̃̀͘͏͕̬̯̖͚̗͔Aͣ̑̈̓̈̑̈̀̿̚҉͙͍̦̗̦͙̠̝̩̯ͅͅL̴͖̞̞͙̱̻̥̬̜̦̐̇̉̈̽ͪ̅ͪ̂̔͌͑ͭ͐ͤ̈́̿̉͞ͅG̴̵̲̰̹̖͎͕ͯ̆̓̽͢͠Ŏ̶̡̺̼͙̣̞̩͕̥̟̝͕͔̯̞ͨ͒͊̂̊͂͗̒͆̾͆̌͆̃̎ͣͫ͜͡ͅ!̓̽̎̑̏́̓̓ͣ̀͏̱̩̭̣̹̺̗͜͞͞\r Aliquam finibus metus commodo sem egestas, non mollis odio pretium.\r Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla\r eros augue, vehicula et molestie accumsan, dictum vel odio. In quis\r risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam\r suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer\r porttitor quam magna. Donec vitae metus tempor, ultricies risus in,\r dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti\r sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.\r Vestibulum ante ipsum primis in faucibus orci luctus et ultrices\r posuere cubilia Curae; Nam semper congue ante, a ultricies velit\r venenatis vitae. Proin non neque sit amet ex commodo congue non nec\r elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean\r feugiat rutrum magna ac luctus.\r Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida\r auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia\r felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis\r nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus\r tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante,\r lacinia eget ornare vel, faucibus at metus.\r Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque.\r Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante\r tortor. Suspendisse vitae consectetur sem, at sollicitudin neque.\r Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam\r dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et\r euismod non, hendrerit sed urna. Sed convallis porttitor est, vel\r aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie.\r Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex\r ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu\r eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat\r ante.\r "; ropey-1.6.1/tests/shrink_to_fit.rs000064400000000000000000000024711046102023000153450ustar 00000000000000extern crate rand; extern crate ropey; use rand::Rng; use ropey::Rope; #[test] #[cfg_attr(miri, ignore)] fn shrink_to_fit() { let mut rng = rand::thread_rng(); let mut rope = Rope::new(); // Do a bunch of random incoherent inserts for _ in 0..(1 << 12) { let len = rope.len_chars().max(1); rope.insert(rng.gen::() % len, "Hello "); rope.insert(rng.gen::() % len, "world! "); rope.insert(rng.gen::() % len, "How are "); rope.insert(rng.gen::() % len, "you "); rope.insert(rng.gen::() % len, "doing?\r\n"); rope.insert(rng.gen::() % len, "Let's "); rope.insert(rng.gen::() % len, "keep "); rope.insert(rng.gen::() % len, "inserting "); rope.insert(rng.gen::() % len, "more "); rope.insert(rng.gen::() % len, "items.\r\n"); rope.insert(rng.gen::() % len, "こんいちは、"); rope.insert(rng.gen::() % len, "みんなさん!"); } let rope2 = rope.clone(); rope.shrink_to_fit(); assert_eq!(rope, rope2); assert!(rope.capacity() < rope2.capacity()); // Make sure the rope is sound rope.assert_integrity(); rope.assert_invariants(); rope2.assert_integrity(); rope2.assert_invariants(); } ropey-1.6.1/tests/small_ascii.txt000064400000000000000000000033221046102023000151520ustar 00000000000000Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. ropey-1.6.1/tests/small_random_inserts.rs000064400000000000000000000021721046102023000167200ustar 00000000000000extern crate rand; extern crate ropey; use rand::Rng; use ropey::Rope; #[test] #[cfg_attr(miri, ignore)] fn small_random_inserts() { let mut rng = rand::thread_rng(); let mut tree = Rope::new(); // Do a bunch of random incoherent inserts for _ in 0..(1 << 10) { let len = tree.len_chars().max(1); tree.insert(rng.gen::() % len, "Hello "); tree.insert(rng.gen::() % len, "world! "); tree.insert(rng.gen::() % len, "How are "); tree.insert(rng.gen::() % len, "you "); tree.insert(rng.gen::() % len, "doing?\r\n"); tree.insert(rng.gen::() % len, "Let's "); tree.insert(rng.gen::() % len, "keep "); tree.insert(rng.gen::() % len, "inserting "); tree.insert(rng.gen::() % len, "more "); tree.insert(rng.gen::() % len, "items.\r\n"); tree.insert(rng.gen::() % len, "こんいちは、"); tree.insert(rng.gen::() % len, "みんなさん!"); } // Make sure the tree is sound tree.assert_integrity(); tree.assert_invariants(); } ropey-1.6.1/tests/test_text.txt000064400000000000000000002540701046102023000147250ustar 00000000000000Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. T̴̷͚͖̜͈̪͎͔̝̫̦̹͔̻̮͂ͬͬ̌ͣ̿ͤ͌ͥ͑̀̂ͬ̚͘͜͞ô̵͚̤̯̹͖͍̦̼̦̖̞̺͕̳̬͇͕̟̜̅̌̈́̑̏̕͘͝ ͍̼̗̫͈̭̦̱̬͚̱̞͓̜̭̼͇̰̞ͮ͗ͣ́ͪ̔ͪ̍̑̏́̀̽̍̔͘͜͜͝ȋ̐̽ͦ̓̔̅͏̧̢̖̭̝̳̹̯̤͈̫͔͔̠͓͉̠͖̠͜ͅn̷̯̗̗̠̱̥͕͉̥͉̳̫̙̅͗̌̒͂̏͑̎̌̌̊͌͘͘ͅͅv̧̜͕͍͙͍̬͕͍̳͉̠͍̹̮̻̜ͨ̏͒̍ͬ̈́͒̈ͥ͗ͣ̄̃ͤ͊̌͆̓o̸̧̎̓͂̊͢҉͍̼̘͇̱̪̠͎̥̹ķ̈́͗͆ͥ͐͑̆̎́͌ͩͯ̊̓͐ͬ̇̕҉̢͏͚̲̰̗̦e̿̀͐̽ͪ̈ͤͬ҉́͟͏̵̫̲̱̻̰̲̦͇̭̟̺͈̞̫̰̜͕͖ͅ ̡̰͎͓͚͓͉͈̮̻̣̮̟̩̬̮̈̋̊͆ͪ̄ͪ͒ͨͧ̇ͪ̇̑̚t̷̬̟͎̞͈̯͙̹̜ͩ̓ͪ͛͐̐ͤ̾̄̈͒̽̈́̑͒̏h̨̢̳͇͓͉̝ͫ̐̓̆̓ͮ̔̓̈́̇ͫe̟̬̣̗͚̬̾̉͋̽ͯ̌ͯͬ̂ͯͭ̓͛́̚͡ ̨̭̱͉̭͈̈̽̆̂͒͗̀ͥͩ͡h̻̼̱̹̗͖̙̗̲̤͓͇͚͚̻̞̥ͥ͛͌ͧ̚͟i̢̯̹̹̘̳̙ͩ̉ͥ͆̽̇̾̎͗̔̓͂͂́̓̌ͬv̧̡̛̟̜̠͉͖̘̲̻̯͚͍͓̯̻̲̹̥͇̻̿̓͛̊̌ͩͩ́ͩ̍͌̚e̵̾́̈́̏͌͌̊͗̏͋ͦ͘͡͏͚̜͚͎͉͍̱͙̖̹̣̘̥̤̹̟͠-̔̌͐́͒ͦͮ̇ͭ̄̏̊̇̍̕͏̩̥̰͚̟m̨̒ͫͦ̔̔͋҉̱̩̗͇̥̰̩̭͍͚͠į̵̷̻̗͉͕͚̣̼̺͉̦̮̠̆̀̐ͩ͒ͯͩͯ͞ͅn̢̫̤̝̝͚̺͍̱̦͚͂̿ͨ̇ͤ͠d̡ͯ͋̋ͧ̈́̒̈͏̛͏̵̤̬͍̗̞̠̟̞̺̠̥̹̱͉̜͍͎̤ ̷̸̢̰͓̘̯͎̤̫̘͓̙̟̳͇̹̥͈͙̮̩̅̋͌͗̓͊̓ͨͣ͗̓͐̈́ͩ̓ͣrͫ͂͌ͪ̏̐̍̾ͥ̓͗̈͆̈ͥ̀̾̚̚҉̴̶̭͇̗͙̘̯̦̭̮̪͚̥̙̯̠͙̪͡e̵̸̲͉̳̙͖͖̫̘̪͕̳͓̻̙͙ͥ̍͂̽ͨ̓̒̒̏ͬ͗ͧ̑̀͠p̵̸̛̦̣͙̳̳̩̣̼̘͈͂ͪͭͤ̎r̶̩̟̞̙͔̼ͫ̆ͦ̐̀̏̾̉̍ͬ̅ͧ͊ͪ̒̈́ͬ̃͞ẻ̴̼͙͍͎̠̀̅̔̃̒͐ͦ̏̆̅̓͋͢ͅš̆̈̆̋ͨ̅̍̇͂̒ͩͨ̂̐̓ͩ͏̸͔͔̯͇͚̤̪̬̗͈̰̦̯͚̕ę̢̱̠͙̲͉̗͚̮̪͖̙̞̦͉͕̗̳͙ͦ̆̋͌ͣ̅̊́ͅņ̴̷̫̪̦͇̺̹͉̗̬̞̲̭̜̪͒̏͂̂̎͊́̋͒̏̅̋̚͘t̷̶̨̟̦̗̦̱͌͌ͩ̀i̴̴̢̖͓͙̘͇̠̦̙̭̼͖̹̾̒̎̐ͥͭ͋ͥ̅͟ͅņ̫͙̹̦̳͈͙̬̫̮͕̰̩̣̘̘͐̀̓ͭͩͬͯ̎͛̿ͫ̊̔̅́̕͠gͥͩ̂͌̒̊̕͏̻͙͖̣͙͍̹͕̝͖̼̙̘͝ ͤ͐̓̒̓͋̐̃̇͊̓ͦ͐̚͢҉̢̨̟̠͉̳͖̲̩͙̕ć̷̡̫̩̞̯̼̝̼͖̤̳̻̘̪̤͈̦̭ͣ́͂͐̽͆̔̀̚͜h̶̢̹̹̙͔̱̓ͦ͌̋̎ͭ͒͋̒ͭ̌̃͌̿ͣ̆̅͑ą̙̳̬̞̬͚̜̤̱̙͇̠̟̈ͤ͋̃̀̓̓ͯ̍̀̽ͣ̐̈̿̌̕ǫ͋͂͐ͬ̿ͯ̂̈́͌̓̌ͧ̕͏̜͔̗͚͔̘̣͕̘̲͖̼͇͖̗̳ͅͅs̷̸̝̙̭̦̣̦̯̭̦͙̹̻͍͇̣̼͗̌͆ͨͭ̃ͮ͐̿̕.̮̝̠̱̺͖͓̼̦̱̉͂͛̓̑̔̓ͮ̈̊̔͗́͝ ̛̣̺̻̼̙̼͓̱̬͕̩͕̲̳̭̗̍ͤ͋̒̆̄ͨ̿ͧ̓͠ͅI̷̻̤̳̲͔͈̖̬̰͔̪͇͇̟̋ͨ̋̍̉̔͝͞͝ͅn̶͕̭͖̠̣͚̹̪͆ͪ̇̂̅̾ͫ́̅̉ͭ̀͜v̖͉̩͕̣͔̭͕̩̲̖̇̀ͬ́̄͒̆͑͆ͪͤ͆̾̍ͯ̚͜ǫ̡̡̫͎̟̞̰̞̹͇̲̏ͨ̄͊̊̇͒̽͢ķ̶̪̙̰̥͙̞̹̭̺͍͕̙̲̮͊ͭ́͋͛͋̑̒͊̏̒̅͛̄̓͟i̴͎̹̞̥͖̒̄ͮ̒̾ͮͧ̀̚͡n̸̵͓̲̟̞̳͚̼̣͙͖̈ͦ͒̿̅̒̿͛͊̇ͧ̉g̡̧̪̩͚͙͓̪͓͚͉̥̪͍̙̻͖͇͗̑͊͑̾̍͊̀ͅ ̷̵̠͚̘̟͓̫̣̲͎̩̹̣̼̟͊́̏ͫ̆ͩ̓͋͆̿̽̓͘̕t̴̢̝̻̖̲̬̜̺̖̻ͩ̿ͫ͗̈́̔͑̐ͮͦ̽̉̓̚͜h̷̛̲͇̫͈̣̭͂ͭ̂͋ͭ̋̔ͮ̆ͩ͞ë̩͕͉̯͇͔͚̭̼̮̣͓̯́ͭ̀ͣ͗̋̉ͨͬ̒ͥͩ͆̓̓́̀̚͘͝ ̛̫̠̗̥̳͇͉̟̮̪̻̤̪͚̟̜̔̌͌̈͌ͪ̋̎̄ͯ͐ͦ́͞͠fͦ̂̈ͬ̇̅̓̓ͫͣ̉̂̉̚͘͡͡͏̼̖̟͚̙̳͔͎̲̫̦̯͔̣̼̹ě̷̶̫͎̞̺̪̪͇͈̞̳̏̋̋͋̾̓̽̓̑ͮ͊ͣ̋̃̅̀͡e͇̗͎̱͔̦̠̰̩̩͖͙̠̻̝ͯ̿̔̀͋͑ͧ͊̆̇̿ͤ̄ͯ̇̀͢͠ͅl̂̿ͯ͛̊̒̓̈́͏̵̪̦̞̤̫̤͇̙̗͕͎̪͕̙̻̳̗̕͟͢i̞̣̙͎͈̗̮͉̱̜̱̝̞̤͋ͯ͋͐̈́ͫ̉̊̏̀ͯͨ͢͟͝n̳̻̼̥̖͍̭̅͂̓̔̔ͦ̔́ͦ͊̀͛̈́ͬͦ͢͡͡ģ̶̡̳̰̻̙̞̱̳̣̤̫̫͕̤̮̰̬̪̜͋͒̎̈́̉̏̀ͬͯ͌̇͊̚ ́̽ͤͦ̾̔͢҉̛̤͍͉̺̙̮̗̜̟̀͝ơ̢̱͓͓̙͉̖̠̯̦̗͍̓̐̃̉̅̃ͨ͆́ͪ̂̒̀̊̃͆̔͡͡ͅf́ͬ̊ͯͫ̉̈́̽̉̚͢͏̡̺̬̖͇̫͉̱ ̴͇̦̗̙̼̬͓̯͖̮͓͎̗͈̻̈́͆ͭ̐ͦ́͛̀͋̐̌ͬ͑̒̊̿̃͞c̶̸̣͔̬͕̪̱̩̣̑̒̑̓̍̓͂̍̔͌̚͘͜͞h̶͈̱͇͉̳͍͍̰͈͖̬̥͚̯͓̞̹̋̔ͯ̑̃́̒̎̎͊̈́̍̚̕ạ̴̞̱̥͍͙̺͉͚͎̫̦͎̥ͩ̀̀̊ͥ͢o̵̧͕̜͓͈̬̰̫̮͙̹͉̩̝̩͎̓̆͗̿̊̀ͯ̃ͪ̊ͫ̽̉̓ͧ͗́̚͢ͅͅs̡ͫ͋̑ͮ̍̃͊̄ͬ̅̈́ͬ̍̇̔̈̅̍̀҉̜͓̝̘̘̮̼͖͎̻͓͖̖͙̞ͅ.͗ͬͭͩ̌̅͗͏̷̮̗͇͔͇͈̮͢ ̨͚̲̫̠̼͖̝̻̉ͤ̅̂ͩ̀̇ͬͭ̀͜Ẅ̢́̉͌ͮͬͨ͊̏͌̇̐͊͟͠҉̼̰̦̩͇͕̟̭̪̲͕̥͖̰̪͈̀ͅͅį̷ͣͦ̉̍ͨ͂͂͑̃͂ͪ̊̈̋̄͜҉̨͚̟̲̯̹̺̝̭̺̙͖͍t̼͓̰̩͙̦͓̟͚͖̀ͯ͛̍̈́͑͂̍̋́h̛̼̺̘̥̠̼̼̭͙̮͚̱̍ͯ̓̃̐̂̇͟ ̴̛͖͔̰̠̺̥̲ͮ̍ͫ̽͜õ̒ͯ̒̓ͦ̈́͑̔̒̓̎ͤ͑҉̸̭̱̤̭̬͈ų̙̫̤͖̺̫̱͓͓̗̪͇̩̙̔̉̊͂ͪ̇͢͟͞ͅt̸̬̣̫̞̫̅͐ͮ̌͌̈́̀̀͘ ̷̴̨̖̙̹͚ͬ̈́̈ͯͨͮ̇̈́̋̈́ͭ͛̑̉͊̕ö̡̍ͥ̂ͬͪͧ͒ͧ̏̓̇̂̄͆̌ͫͤ͢͠͝͏̖̱̯̘͙̰̖͎̰͓̟̤ṙ̡̬̟̬̜̪̮̺͖̗̘͈̟ͨ͐͗̑͒̐d̢ͭͫ̊̏ͬͥ͋́̌̈́ͮ̆ͬ̐̌̎͏̵̷̡̞̲̹̙͕̮̮͚ḙ̴̸̠͔͎̥͇͖͕̘̍̓̏̐ͩͩ̈́ͦ̐̋ͤ̎̾̌̏͊̊́̚͞ͅr̸͈̗̣̲̗̣̬̤ͦ̎ͫ̏̀ͥͪ̋ͧ̄͑̋͒͌͋ͦ̉͟͞.ͨͣ̽̈́͒̄ͮ̀͋͋͏̴̧̯̺̙̱̻͙̜ ̡̣̞̠͓̰͍̠͕̭̺̼͊̽̿͊ͮ̐̓̒̊͒̔̓͐ͨ̈̌́T̸̸̓́̋ͬ́͆ͨͫ͌͂ͣ̋͒҉̺̝͎̟͖͚̠h̸̡̰̜̦͇͕̪̝̳͕͉̲̝̑ͥ͋ͧ̎̆͌͟e̛̹͍͍̫̙̞̪̭̙̟͙̱̺̮̳͕̜ͫ̓ͭ͊ͫ͆̀̚͟͡ ̿͂̄ͧ̔̎ͧ͑̾̀̓͏̦͍̳͈̳͔̘̖̲̯̰̟̝̳̖̦N̶̡̧̦̮̟̦̩̰̣̝̆̀͊̔͢e͛̄ͮͦͨ͂̔̓̍̄̉͆͊̑̑̆̚͏̜̗͎̝̼̯̥̜͖͍̪̝͞ͅͅz̨̛̀̾ͪ͗̉́͠͏͚̫̼̫̜̣pͪͦ͌̄ͥ̆ͣͩ͋̉́̏͞͏̥̜̝̳̱̞̙̳̤͙̟̟̮̦ȅ̷̩̟͉̯͕͔̘̺̥̻̻ͧ̊̅̽ͣ͑̓̑̽ͦ̾͌͜r̴̭̥̲̲̤͚͈̰͇̰͈̰̹ͫ̒ͯ̿͒ͧ̊͆͒ͣ́ḍ̭̟̤̈́̌̓̈́ͫ͐̍͂͞į̛̞̝̮̣͙͙̤̇̂̓̎͋̿̓̎̄̈́ͧ̓ͩ̐̓̄̋ͭ͞͠a͋̔̋ͫ̂͐͂҉̸̛̥̩̯̯̤̝͔̠̝̯̪̥̩̻̼̮n͌ͣ̂͋̿̚҉̛̙̲̺̯͇͓̝̯̪̟͔̩͟ͅ ̢̨͚̻̗̘͖̯̐ͥ͋̽ͯ̎̈́͋̏̄͋̆̑̊̆̚̕͟ͅh̢̛̗̱̭͇͖̰̮̮͈̲͍̯̟ͭ͊̎̽̓ͦͤ͠ï̛̘̝̦͎̦̭̠͖̳͎̮̼̏͐ͧ̒̒͐͑ͪͫ̋̽̚̚͜v̴̮͕̝̮̞͐̄͗̋͒ͤ̎̈̑ͬͮ̄̾ͤ̓̾͊͗͟é̶̷̡̩͖̰̫͓̟ͮͬͣ͊-ͦ͛ͩͤͨͨ̆̄͏̼̜̭͔̳͈͖̳̩͢ͅͅm̷̴̓́̓͛͒̾̍̉҉̛̗̹̠̣̪̺͎̖̝͚̖͙i̛̥͓̬̫͉͕͉͆͒ͧ̂̿̔̔͆̆̓̍͊̀͜n͌ͧͣ̅̌̎ͦͦ͑̑ͭ̆ͬ̀ͤ̀ͣ̚҉͎̰̱͚͈͈̬̹͕̺̙͙̼͘͘͞d̶͖̫̟̲͕̺̠͎̘͕̱̼͙̪̪̩͙̅̅̑̓̇͑̊̉͜͞ ̶̵̷̴̡̠͚̪͕̣̱̖̱̗̤̭̭͔͖͚ͧͤͥ͒̌ͪ͊͂͒̓͂ͧͧ̇̇͐̑̔ͅͅơ̵̲̲͇̯̰͇̜̣͕͕͓̲̤̲͔͚̞͑͗ͤ̓́̚͠ͅf̢̧̛̩̯̼̫͖̾ͣ͌̾̉́̈́̑̈́̚͞͞ͅ ͤͩ́͋͒ͫͬͣ̋̅̆҉̧̱̻͓͕͉̹̫̫̞̯̪̙̩͍̦͔̖̮̀͟ͅc͉̠̜̩̟͕͎̙̣̮̘̼͋ͯ̍ͨ̅̄ͫ̈̋ͫ̊͡͝ȟ̸̨ͯͦ̂̉̇̾̆ͭ̋̐̈̆̀̚͜҉͚͕̻̖a̶̴̛͚̗͙̳̬̲͚ͦ́̐ͥ́̔̅̑̎͐̑ͯ̾ͤͥͧ͡ò̶̧̞̪̦̥̪̻̦̝̳̬̔͛͛ͣ̋̌̔ͫ̂̽ͫ͘͠s̸̖̣̬̤̫͇̫̣̑͆͒̎̏́͟.̴̗̤̭͉̯̻̤͕̌ͯ̍ͤ̓͌ͤ̈̆̉ͦ̇́̚͘͟͝ͅ ̯̹̪͓̬͌̔̌ͬ̀͘͢͡͡Z̡̩̲̩̰̫̩̟͍̰͖͔̭ͣ̆̾ͭ̀́͞ͅa̡̡̙̜̭͇͎͔̙̞̫͓̜͉͔̬ͭ̈ͨ̉͆ͣͫ̃͌̓͌́ͣͥ̒̌͊͘͝l̢̨̡̯̙̫͖̫̺̘̬̟͈͌̊ͧͫͦ̉̃ͩͦ̒ͯ̇̌̓͛͟͝ͅg̵̙̼̼ͪ͂ͭ͗̈̕ȯ̅ͧ̓ͪ́̂͑̐ͩͥͬ̊̑͆̇͒ͫͣ͝҉͎̟̜̥͎̮̣͉̖̟̯̦̖͙͙͞ͅ.̈̑ͩ̇̂ͬ̓ͬ͊͂ͨ̽͠͏̺͎̞̦̜͍͚̯̯͔̝̞̻̩̖ ̷̰̪͍͎͔͒ͯͥ̾̉͆ͤ̊̓̂͋̀͆H̸̸̹̞̙̺͎̠̯̤ͨ̉̍ͬͤ̓̐͌ͥͮ͞eͣ̈̾͛́͏͕̗͍̜̼͎͚̟̬̣̝̕ͅͅ ̴̛̩̗̼̝̣̩͚͇̯́̉͋̂̍͂̌ͮ͋̾͜͠wͮ̽̓ͭ̿͐̽̐̽͆̓͝҉̡̼̲͖̪̥h̢̢̛͍̰̰̻̱̼̰̹̖̖̪̝̥̘̎̀ͪ͒̾ͫͬ̆̑o̡̗̠̞̱̥͎̰͎͍̫̻͓͇͓͐ͥͯ͂̅͠ͅ ̡̛̏͑ͦ̓͊ͮͫͯͭ̌͒̆̍̈͠҉͖͚̪̫̗̮W̴̐̊͋̾ͥͫ҉͎̞͔̯̫̹͖̰͉̹̼͎̰̱͓̻̀a̶̩̤̙̣͎̳̭̲̗̠͉̳̭̭̦̞͎̮̅͌̾͗̾͛̇̀́͟͞ͅi̷̡ͣ̆̌͋͒͒́͘͏̮̺̩͎͇̜͍̫ṯ̴̢͖̥̖͇͎̦̦̹̖͇̪ͭ̅̍͐̇͒͋̽̏̿̒͆ͧ̄͋ͧͩ͒͜s̙̥̖̘̖͚̭̤̮̖̘̰̫̟̈́ͣ̍ͧ͐ͥ̏͆̃̿͒̔͐́̚͟ͅ ̨ͭ̌ͬͯ͆̒͋ͭ̔̿ͧ̅̓ͣ͡͏͇̟͉̥͔̬̼͚͙͚B̛̜̮̤͓̝̪̪͈͕̘̜͙̰̮̫̘̣͓͔̅ͩ͊̔ͦͯ́̌́͆ͭ̓́e̶̢̡̦͇͕̙͈͖͕̦̬̫͕̣̺̒̿͂͐͒͋͂ͦ́͋ͤ̿ͬ̊ͣ͗̑̽͜ͅͅh̸͑ͫͧ̑ͬͧ̈́̎̃ͣ̊̾͂ͨͤ̓͐̐̑͏̸̭͓̘͉̩i̧̧̭̣͈̝̺̼̺̠͉̞̜̲̳͙̦͐̔ͯ͛̅̾n̸͓̝̤̙͙͔ͪ̋̈́͒̒ͭ̈́̓ͮ̋̀̋̀̈ͩ́͌̄͘d̷̫̳̩̼̥̗̲̰͇͉̼̬̤͇̖ͮ̿ͬ͂ͦ̏̓ͮ̽͂̾̾ͯ͆͜͠ ̨̈́͒̇̏̄̑̓ͮͥ̒ͤͨ̋҉̴̴̟̱͙̟̫̩̗͔̝͔̀Ţ̵̝̟̖̭͇̻̳͖͉̺̖̖͙͙̺̐̈́̓ͯ̆̇̋ͩ͊̄̾̾ͬ̌̚͟ͅh̡͈̗̜͙̬̗̲̦̲̟̗̦̬͓̳ͧ̋̌͂͂ͨͬͦ̿̏̈́̋ͣ̒̕͡ͅͅe̗͇̰̰̥̪̗͑̔̓́̈́ͨ̊́̿̅ͯͥ̈́͐͗͘͢͝ ̡̢̛̯͎͓̰̘͎̦̪̯̪̥̰̲͇̠̲͔ͤͤ̇̅̆̋̂̆̈́ͤ̿͑ͅW̡͓͈̲̲͉̜͔̖͈̻̱͚̿̌͗̉ͤ͢͡ͅͅa̔̾͛̅͊͋͐҉̱̹̬͍͙̻̱l̢͎̟̬̙̼̱̫̮̘̼͔̭̅ͬ͑ͣ̏̾̅̓ͣ̿ͣ̈́̕͢͡ͅͅl̡̥̣͔̭̇̒͛͒͐̄̽͛̋ͥ̌͢͟͡.̷̰̝̮͔̟̦͈̥̬̻̥̬͎͓̻̲̇ͮ̿ͨͦ̽ͫ͟͢͝͠ ̗̱͖͈͌̈ͦ͛ͮ̌͋̽̃͆̀͂ͨͧ̄̔̔ͭ̏͢Z̃̉̿ͮ̃̀͘͏͕̬̯̖͚̗͔Aͣ̑̈̓̈̑̈̀̿̚҉͙͍̦̗̦͙̠̝̩̯ͅͅL̴͖̞̞͙̱̻̥̬̜̦̐̇̉̈̽ͪ̅ͪ̂̔͌͑ͭ͐ͤ̈́̿̉͞ͅG̴̵̲̰̹̖͎͕ͯ̆̓̽͢͠Ŏ̶̡̺̼͙̣̞̩͕̥̟̝͕͔̯̞ͨ͒͊̂̊͂͗̒͆̾͆̌͆̃̎ͣͫ͜͡ͅ!̓̽̎̑̏́̓̓ͣ̀͏̱̩̭̣̹̺̗͜͞͞ T̴̷͚͖̜͈̪͎͔̝̫̦̹͔̻̮͂ͬͬ̌ͣ̿ͤ͌ͥ͑̀̂ͬ̚͘͜͞ô̵͚̤̯̹͖͍̦̼̦̖̞̺͕̳̬͇͕̟̜̅̌̈́̑̏̕͘͝ ͍̼̗̫͈̭̦̱̬͚̱̞͓̜̭̼͇̰̞ͮ͗ͣ́ͪ̔ͪ̍̑̏́̀̽̍̔͘͜͜͝ȋ̐̽ͦ̓̔̅͏̧̢̖̭̝̳̹̯̤͈̫͔͔̠͓͉̠͖̠͜ͅn̷̯̗̗̠̱̥͕͉̥͉̳̫̙̅͗̌̒͂̏͑̎̌̌̊͌͘͘ͅͅv̧̜͕͍͙͍̬͕͍̳͉̠͍̹̮̻̜ͨ̏͒̍ͬ̈́͒̈ͥ͗ͣ̄̃ͤ͊̌͆̓o̸̧̎̓͂̊͢҉͍̼̘͇̱̪̠͎̥̹ķ̈́͗͆ͥ͐͑̆̎́͌ͩͯ̊̓͐ͬ̇̕҉̢͏͚̲̰̗̦e̿̀͐̽ͪ̈ͤͬ҉́͟͏̵̫̲̱̻̰̲̦͇̭̟̺͈̞̫̰̜͕͖ͅ ̡̰͎͓͚͓͉͈̮̻̣̮̟̩̬̮̈̋̊͆ͪ̄ͪ͒ͨͧ̇ͪ̇̑̚t̷̬̟͎̞͈̯͙̹̜ͩ̓ͪ͛͐̐ͤ̾̄̈͒̽̈́̑͒̏h̨̢̳͇͓͉̝ͫ̐̓̆̓ͮ̔̓̈́̇ͫe̟̬̣̗͚̬̾̉͋̽ͯ̌ͯͬ̂ͯͭ̓͛́̚͡ ̨̭̱͉̭͈̈̽̆̂͒͗̀ͥͩ͡h̻̼̱̹̗͖̙̗̲̤͓͇͚͚̻̞̥ͥ͛͌ͧ̚͟i̢̯̹̹̘̳̙ͩ̉ͥ͆̽̇̾̎͗̔̓͂͂́̓̌ͬv̧̡̛̟̜̠͉͖̘̲̻̯͚͍͓̯̻̲̹̥͇̻̿̓͛̊̌ͩͩ́ͩ̍͌̚e̵̾́̈́̏͌͌̊͗̏͋ͦ͘͡͏͚̜͚͎͉͍̱͙̖̹̣̘̥̤̹̟͠-̔̌͐́͒ͦͮ̇ͭ̄̏̊̇̍̕͏̩̥̰͚̟m̨̒ͫͦ̔̔͋҉̱̩̗͇̥̰̩̭͍͚͠į̵̷̻̗͉͕͚̣̼̺͉̦̮̠̆̀̐ͩ͒ͯͩͯ͞ͅn̢̫̤̝̝͚̺͍̱̦͚͂̿ͨ̇ͤ͠d̡ͯ͋̋ͧ̈́̒̈͏̛͏̵̤̬͍̗̞̠̟̞̺̠̥̹̱͉̜͍͎̤ ̷̸̢̰͓̘̯͎̤̫̘͓̙̟̳͇̹̥͈͙̮̩̅̋͌͗̓͊̓ͨͣ͗̓͐̈́ͩ̓ͣrͫ͂͌ͪ̏̐̍̾ͥ̓͗̈͆̈ͥ̀̾̚̚҉̴̶̭͇̗͙̘̯̦̭̮̪͚̥̙̯̠͙̪͡e̵̸̲͉̳̙͖͖̫̘̪͕̳͓̻̙͙ͥ̍͂̽ͨ̓̒̒̏ͬ͗ͧ̑̀͠p̵̸̛̦̣͙̳̳̩̣̼̘͈͂ͪͭͤ̎r̶̩̟̞̙͔̼ͫ̆ͦ̐̀̏̾̉̍ͬ̅ͧ͊ͪ̒̈́ͬ̃͞ẻ̴̼͙͍͎̠̀̅̔̃̒͐ͦ̏̆̅̓͋͢ͅš̆̈̆̋ͨ̅̍̇͂̒ͩͨ̂̐̓ͩ͏̸͔͔̯͇͚̤̪̬̗͈̰̦̯͚̕ę̢̱̠͙̲͉̗͚̮̪͖̙̞̦͉͕̗̳͙ͦ̆̋͌ͣ̅̊́ͅņ̴̷̫̪̦͇̺̹͉̗̬̞̲̭̜̪͒̏͂̂̎͊́̋͒̏̅̋̚͘t̷̶̨̟̦̗̦̱͌͌ͩ̀i̴̴̢̖͓͙̘͇̠̦̙̭̼͖̹̾̒̎̐ͥͭ͋ͥ̅͟ͅņ̫͙̹̦̳͈͙̬̫̮͕̰̩̣̘̘͐̀̓ͭͩͬͯ̎͛̿ͫ̊̔̅́̕͠gͥͩ̂͌̒̊̕͏̻͙͖̣͙͍̹͕̝͖̼̙̘͝ ͤ͐̓̒̓͋̐̃̇͊̓ͦ͐̚͢҉̢̨̟̠͉̳͖̲̩͙̕ć̷̡̫̩̞̯̼̝̼͖̤̳̻̘̪̤͈̦̭ͣ́͂͐̽͆̔̀̚͜h̶̢̹̹̙͔̱̓ͦ͌̋̎ͭ͒͋̒ͭ̌̃͌̿ͣ̆̅͑ą̙̳̬̞̬͚̜̤̱̙͇̠̟̈ͤ͋̃̀̓̓ͯ̍̀̽ͣ̐̈̿̌̕ǫ͋͂͐ͬ̿ͯ̂̈́͌̓̌ͧ̕͏̜͔̗͚͔̘̣͕̘̲͖̼͇͖̗̳ͅͅs̷̸̝̙̭̦̣̦̯̭̦͙̹̻͍͇̣̼͗̌͆ͨͭ̃ͮ͐̿̕.̮̝̠̱̺͖͓̼̦̱̉͂͛̓̑̔̓ͮ̈̊̔͗́͝ ̛̣̺̻̼̙̼͓̱̬͕̩͕̲̳̭̗̍ͤ͋̒̆̄ͨ̿ͧ̓͠ͅI̷̻̤̳̲͔͈̖̬̰͔̪͇͇̟̋ͨ̋̍̉̔͝͞͝ͅn̶͕̭͖̠̣͚̹̪͆ͪ̇̂̅̾ͫ́̅̉ͭ̀͜v̖͉̩͕̣͔̭͕̩̲̖̇̀ͬ́̄͒̆͑͆ͪͤ͆̾̍ͯ̚͜ǫ̡̡̫͎̟̞̰̞̹͇̲̏ͨ̄͊̊̇͒̽͢ķ̶̪̙̰̥͙̞̹̭̺͍͕̙̲̮͊ͭ́͋͛͋̑̒͊̏̒̅͛̄̓͟i̴͎̹̞̥͖̒̄ͮ̒̾ͮͧ̀̚͡n̸̵͓̲̟̞̳͚̼̣͙͖̈ͦ͒̿̅̒̿͛͊̇ͧ̉g̡̧̪̩͚͙͓̪͓͚͉̥̪͍̙̻͖͇͗̑͊͑̾̍͊̀ͅ ̷̵̠͚̘̟͓̫̣̲͎̩̹̣̼̟͊́̏ͫ̆ͩ̓͋͆̿̽̓͘̕t̴̢̝̻̖̲̬̜̺̖̻ͩ̿ͫ͗̈́̔͑̐ͮͦ̽̉̓̚͜h̷̛̲͇̫͈̣̭͂ͭ̂͋ͭ̋̔ͮ̆ͩ͞ë̩͕͉̯͇͔͚̭̼̮̣͓̯́ͭ̀ͣ͗̋̉ͨͬ̒ͥͩ͆̓̓́̀̚͘͝ ̛̫̠̗̥̳͇͉̟̮̪̻̤̪͚̟̜̔̌͌̈͌ͪ̋̎̄ͯ͐ͦ́͞͠fͦ̂̈ͬ̇̅̓̓ͫͣ̉̂̉̚͘͡͡͏̼̖̟͚̙̳͔͎̲̫̦̯͔̣̼̹ě̷̶̫͎̞̺̪̪͇͈̞̳̏̋̋͋̾̓̽̓̑ͮ͊ͣ̋̃̅̀͡e͇̗͎̱͔̦̠̰̩̩͖͙̠̻̝ͯ̿̔̀͋͑ͧ͊̆̇̿ͤ̄ͯ̇̀͢͠ͅl̂̿ͯ͛̊̒̓̈́͏̵̪̦̞̤̫̤͇̙̗͕͎̪͕̙̻̳̗̕͟͢i̞̣̙͎͈̗̮͉̱̜̱̝̞̤͋ͯ͋͐̈́ͫ̉̊̏̀ͯͨ͢͟͝n̳̻̼̥̖͍̭̅͂̓̔̔ͦ̔́ͦ͊̀͛̈́ͬͦ͢͡͡ģ̶̡̳̰̻̙̞̱̳̣̤̫̫͕̤̮̰̬̪̜͋͒̎̈́̉̏̀ͬͯ͌̇͊̚ ́̽ͤͦ̾̔͢҉̛̤͍͉̺̙̮̗̜̟̀͝ơ̢̱͓͓̙͉̖̠̯̦̗͍̓̐̃̉̅̃ͨ͆́ͪ̂̒̀̊̃͆̔͡͡ͅf́ͬ̊ͯͫ̉̈́̽̉̚͢͏̡̺̬̖͇̫͉̱ ̴͇̦̗̙̼̬͓̯͖̮͓͎̗͈̻̈́͆ͭ̐ͦ́͛̀͋̐̌ͬ͑̒̊̿̃͞c̶̸̣͔̬͕̪̱̩̣̑̒̑̓̍̓͂̍̔͌̚͘͜͞h̶͈̱͇͉̳͍͍̰͈͖̬̥͚̯͓̞̹̋̔ͯ̑̃́̒̎̎͊̈́̍̚̕ạ̴̞̱̥͍͙̺͉͚͎̫̦͎̥ͩ̀̀̊ͥ͢o̵̧͕̜͓͈̬̰̫̮͙̹͉̩̝̩͎̓̆͗̿̊̀ͯ̃ͪ̊ͫ̽̉̓ͧ͗́̚͢ͅͅs̡ͫ͋̑ͮ̍̃͊̄ͬ̅̈́ͬ̍̇̔̈̅̍̀҉̜͓̝̘̘̮̼͖͎̻͓͖̖͙̞ͅ.͗ͬͭͩ̌̅͗͏̷̮̗͇͔͇͈̮͢ ̨͚̲̫̠̼͖̝̻̉ͤ̅̂ͩ̀̇ͬͭ̀͜Ẅ̢́̉͌ͮͬͨ͊̏͌̇̐͊͟͠҉̼̰̦̩͇͕̟̭̪̲͕̥͖̰̪͈̀ͅͅį̷ͣͦ̉̍ͨ͂͂͑̃͂ͪ̊̈̋̄͜҉̨͚̟̲̯̹̺̝̭̺̙͖͍t̼͓̰̩͙̦͓̟͚͖̀ͯ͛̍̈́͑͂̍̋́h̛̼̺̘̥̠̼̼̭͙̮͚̱̍ͯ̓̃̐̂̇͟ ̴̛͖͔̰̠̺̥̲ͮ̍ͫ̽͜õ̒ͯ̒̓ͦ̈́͑̔̒̓̎ͤ͑҉̸̭̱̤̭̬͈ų̙̫̤͖̺̫̱͓͓̗̪͇̩̙̔̉̊͂ͪ̇͢͟͞ͅt̸̬̣̫̞̫̅͐ͮ̌͌̈́̀̀͘ ̷̴̨̖̙̹͚ͬ̈́̈ͯͨͮ̇̈́̋̈́ͭ͛̑̉͊̕ö̡̍ͥ̂ͬͪͧ͒ͧ̏̓̇̂̄͆̌ͫͤ͢͠͝͏̖̱̯̘͙̰̖͎̰͓̟̤ṙ̡̬̟̬̜̪̮̺͖̗̘͈̟ͨ͐͗̑͒̐d̢ͭͫ̊̏ͬͥ͋́̌̈́ͮ̆ͬ̐̌̎͏̵̷̡̞̲̹̙͕̮̮͚ḙ̴̸̠͔͎̥͇͖͕̘̍̓̏̐ͩͩ̈́ͦ̐̋ͤ̎̾̌̏͊̊́̚͞ͅr̸͈̗̣̲̗̣̬̤ͦ̎ͫ̏̀ͥͪ̋ͧ̄͑̋͒͌͋ͦ̉͟͞.ͨͣ̽̈́͒̄ͮ̀͋͋͏̴̧̯̺̙̱̻͙̜ ̡̣̞̠͓̰͍̠͕̭̺̼͊̽̿͊ͮ̐̓̒̊͒̔̓͐ͨ̈̌́T̸̸̓́̋ͬ́͆ͨͫ͌͂ͣ̋͒҉̺̝͎̟͖͚̠h̸̡̰̜̦͇͕̪̝̳͕͉̲̝̑ͥ͋ͧ̎̆͌͟e̛̹͍͍̫̙̞̪̭̙̟͙̱̺̮̳͕̜ͫ̓ͭ͊ͫ͆̀̚͟͡ ̿͂̄ͧ̔̎ͧ͑̾̀̓͏̦͍̳͈̳͔̘̖̲̯̰̟̝̳̖̦N̶̡̧̦̮̟̦̩̰̣̝̆̀͊̔͢e͛̄ͮͦͨ͂̔̓̍̄̉͆͊̑̑̆̚͏̜̗͎̝̼̯̥̜͖͍̪̝͞ͅͅz̨̛̀̾ͪ͗̉́͠͏͚̫̼̫̜̣pͪͦ͌̄ͥ̆ͣͩ͋̉́̏͞͏̥̜̝̳̱̞̙̳̤͙̟̟̮̦ȅ̷̩̟͉̯͕͔̘̺̥̻̻ͧ̊̅̽ͣ͑̓̑̽ͦ̾͌͜r̴̭̥̲̲̤͚͈̰͇̰͈̰̹ͫ̒ͯ̿͒ͧ̊͆͒ͣ́ḍ̭̟̤̈́̌̓̈́ͫ͐̍͂͞į̛̞̝̮̣͙͙̤̇̂̓̎͋̿̓̎̄̈́ͧ̓ͩ̐̓̄̋ͭ͞͠a͋̔̋ͫ̂͐͂҉̸̛̥̩̯̯̤̝͔̠̝̯̪̥̩̻̼̮n͌ͣ̂͋̿̚҉̛̙̲̺̯͇͓̝̯̪̟͔̩͟ͅ ̢̨͚̻̗̘͖̯̐ͥ͋̽ͯ̎̈́͋̏̄͋̆̑̊̆̚̕͟ͅh̢̛̗̱̭͇͖̰̮̮͈̲͍̯̟ͭ͊̎̽̓ͦͤ͠ï̛̘̝̦͎̦̭̠͖̳͎̮̼̏͐ͧ̒̒͐͑ͪͫ̋̽̚̚͜v̴̮͕̝̮̞͐̄͗̋͒ͤ̎̈̑ͬͮ̄̾ͤ̓̾͊͗͟é̶̷̡̩͖̰̫͓̟ͮͬͣ͊-ͦ͛ͩͤͨͨ̆̄͏̼̜̭͔̳͈͖̳̩͢ͅͅm̷̴̓́̓͛͒̾̍̉҉̛̗̹̠̣̪̺͎̖̝͚̖͙i̛̥͓̬̫͉͕͉͆͒ͧ̂̿̔̔͆̆̓̍͊̀͜n͌ͧͣ̅̌̎ͦͦ͑̑ͭ̆ͬ̀ͤ̀ͣ̚҉͎̰̱͚͈͈̬̹͕̺̙͙̼͘͘͞d̶͖̫̟̲͕̺̠͎̘͕̱̼͙̪̪̩͙̅̅̑̓̇͑̊̉͜͞ ̶̵̷̴̡̠͚̪͕̣̱̖̱̗̤̭̭͔͖͚ͧͤͥ͒̌ͪ͊͂͒̓͂ͧͧ̇̇͐̑̔ͅͅơ̵̲̲͇̯̰͇̜̣͕͕͓̲̤̲͔͚̞͑͗ͤ̓́̚͠ͅf̢̧̛̩̯̼̫͖̾ͣ͌̾̉́̈́̑̈́̚͞͞ͅ ͤͩ́͋͒ͫͬͣ̋̅̆҉̧̱̻͓͕͉̹̫̫̞̯̪̙̩͍̦͔̖̮̀͟ͅc͉̠̜̩̟͕͎̙̣̮̘̼͋ͯ̍ͨ̅̄ͫ̈̋ͫ̊͡͝ȟ̸̨ͯͦ̂̉̇̾̆ͭ̋̐̈̆̀̚͜҉͚͕̻̖a̶̴̛͚̗͙̳̬̲͚ͦ́̐ͥ́̔̅̑̎͐̑ͯ̾ͤͥͧ͡ò̶̧̞̪̦̥̪̻̦̝̳̬̔͛͛ͣ̋̌̔ͫ̂̽ͫ͘͠s̸̖̣̬̤̫͇̫̣̑͆͒̎̏́͟.̴̗̤̭͉̯̻̤͕̌ͯ̍ͤ̓͌ͤ̈̆̉ͦ̇́̚͘͟͝ͅ ̯̹̪͓̬͌̔̌ͬ̀͘͢͡͡Z̡̩̲̩̰̫̩̟͍̰͖͔̭ͣ̆̾ͭ̀́͞ͅa̡̡̙̜̭͇͎͔̙̞̫͓̜͉͔̬ͭ̈ͨ̉͆ͣͫ̃͌̓͌́ͣͥ̒̌͊͘͝l̢̨̡̯̙̫͖̫̺̘̬̟͈͌̊ͧͫͦ̉̃ͩͦ̒ͯ̇̌̓͛͟͝ͅg̵̙̼̼ͪ͂ͭ͗̈̕ȯ̅ͧ̓ͪ́̂͑̐ͩͥͬ̊̑͆̇͒ͫͣ͝҉͎̟̜̥͎̮̣͉̖̟̯̦̖͙͙͞ͅ.̈̑ͩ̇̂ͬ̓ͬ͊͂ͨ̽͠͏̺͎̞̦̜͍͚̯̯͔̝̞̻̩̖ ̷̰̪͍͎͔͒ͯͥ̾̉͆ͤ̊̓̂͋̀͆H̸̸̹̞̙̺͎̠̯̤ͨ̉̍ͬͤ̓̐͌ͥͮ͞eͣ̈̾͛́͏͕̗͍̜̼͎͚̟̬̣̝̕ͅͅ ̴̛̩̗̼̝̣̩͚͇̯́̉͋̂̍͂̌ͮ͋̾͜͠wͮ̽̓ͭ̿͐̽̐̽͆̓͝҉̡̼̲͖̪̥h̢̢̛͍̰̰̻̱̼̰̹̖̖̪̝̥̘̎̀ͪ͒̾ͫͬ̆̑o̡̗̠̞̱̥͎̰͎͍̫̻͓͇͓͐ͥͯ͂̅͠ͅ ̡̛̏͑ͦ̓͊ͮͫͯͭ̌͒̆̍̈͠҉͖͚̪̫̗̮W̴̐̊͋̾ͥͫ҉͎̞͔̯̫̹͖̰͉̹̼͎̰̱͓̻̀a̶̩̤̙̣͎̳̭̲̗̠͉̳̭̭̦̞͎̮̅͌̾͗̾͛̇̀́͟͞ͅi̷̡ͣ̆̌͋͒͒́͘͏̮̺̩͎͇̜͍̫ṯ̴̢͖̥̖͇͎̦̦̹̖͇̪ͭ̅̍͐̇͒͋̽̏̿̒͆ͧ̄͋ͧͩ͒͜s̙̥̖̘̖͚̭̤̮̖̘̰̫̟̈́ͣ̍ͧ͐ͥ̏͆̃̿͒̔͐́̚͟ͅ ̨ͭ̌ͬͯ͆̒͋ͭ̔̿ͧ̅̓ͣ͡͏͇̟͉̥͔̬̼͚͙͚B̛̜̮̤͓̝̪̪͈͕̘̜͙̰̮̫̘̣͓͔̅ͩ͊̔ͦͯ́̌́͆ͭ̓́e̶̢̡̦͇͕̙͈͖͕̦̬̫͕̣̺̒̿͂͐͒͋͂ͦ́͋ͤ̿ͬ̊ͣ͗̑̽͜ͅͅh̸͑ͫͧ̑ͬͧ̈́̎̃ͣ̊̾͂ͨͤ̓͐̐̑͏̸̭͓̘͉̩i̧̧̭̣͈̝̺̼̺̠͉̞̜̲̳͙̦͐̔ͯ͛̅̾n̸͓̝̤̙͙͔ͪ̋̈́͒̒ͭ̈́̓ͮ̋̀̋̀̈ͩ́͌̄͘d̷̫̳̩̼̥̗̲̰͇͉̼̬̤͇̖ͮ̿ͬ͂ͦ̏̓ͮ̽͂̾̾ͯ͆͜͠ ̨̈́͒̇̏̄̑̓ͮͥ̒ͤͨ̋҉̴̴̟̱͙̟̫̩̗͔̝͔̀Ţ̵̝̟̖̭͇̻̳͖͉̺̖̖͙͙̺̐̈́̓ͯ̆̇̋ͩ͊̄̾̾ͬ̌̚͟ͅh̡͈̗̜͙̬̗̲̦̲̟̗̦̬͓̳ͧ̋̌͂͂ͨͬͦ̿̏̈́̋ͣ̒̕͡ͅͅe̗͇̰̰̥̪̗͑̔̓́̈́ͨ̊́̿̅ͯͥ̈́͐͗͘͢͝ ̡̢̛̯͎͓̰̘͎̦̪̯̪̥̰̲͇̠̲͔ͤͤ̇̅̆̋̂̆̈́ͤ̿͑ͅW̡͓͈̲̲͉̜͔̖͈̻̱͚̿̌͗̉ͤ͢͡ͅͅa̔̾͛̅͊͋͐҉̱̹̬͍͙̻̱l̢͎̟̬̙̼̱̫̮̘̼͔̭̅ͬ͑ͣ̏̾̅̓ͣ̿ͣ̈́̕͢͡ͅͅl̡̥̣͔̭̇̒͛͒͐̄̽͛̋ͥ̌͢͟͡.̷̰̝̮͔̟̦͈̥̬̻̥̬͎͓̻̲̇ͮ̿ͨͦ̽ͫ͟͢͝͠ ̗̱͖͈͌̈ͦ͛ͮ̌͋̽̃͆̀͂ͨͧ̄̔̔ͭ̏͢Z̃̉̿ͮ̃̀͘͏͕̬̯̖͚̗͔Aͣ̑̈̓̈̑̈̀̿̚҉͙͍̦̗̦͙̠̝̩̯ͅͅL̴͖̞̞͙̱̻̥̬̜̦̐̇̉̈̽ͪ̅ͪ̂̔͌͑ͭ͐ͤ̈́̿̉͞ͅG̴̵̲̰̹̖͎͕ͯ̆̓̽͢͠Ŏ̶̡̺̼͙̣̞̩͕̥̟̝͕͔̯̞ͨ͒͊̂̊͂͗̒͆̾͆̌͆̃̎ͣͫ͜͡ͅ!̓̽̎̑̏́̓̓ͣ̀͏̱̩̭̣̹̺̗͜͞͞ T̴̷͚͖̜͈̪͎͔̝̫̦̹͔̻̮͂ͬͬ̌ͣ̿ͤ͌ͥ͑̀̂ͬ̚͘͜͞ô̵͚̤̯̹͖͍̦̼̦̖̞̺͕̳̬͇͕̟̜̅̌̈́̑̏̕͘͝ ͍̼̗̫͈̭̦̱̬͚̱̞͓̜̭̼͇̰̞ͮ͗ͣ́ͪ̔ͪ̍̑̏́̀̽̍̔͘͜͜͝ȋ̐̽ͦ̓̔̅͏̧̢̖̭̝̳̹̯̤͈̫͔͔̠͓͉̠͖̠͜ͅn̷̯̗̗̠̱̥͕͉̥͉̳̫̙̅͗̌̒͂̏͑̎̌̌̊͌͘͘ͅͅv̧̜͕͍͙͍̬͕͍̳͉̠͍̹̮̻̜ͨ̏͒̍ͬ̈́͒̈ͥ͗ͣ̄̃ͤ͊̌͆̓o̸̧̎̓͂̊͢҉͍̼̘͇̱̪̠͎̥̹ķ̈́͗͆ͥ͐͑̆̎́͌ͩͯ̊̓͐ͬ̇̕҉̢͏͚̲̰̗̦e̿̀͐̽ͪ̈ͤͬ҉́͟͏̵̫̲̱̻̰̲̦͇̭̟̺͈̞̫̰̜͕͖ͅ ̡̰͎͓͚͓͉͈̮̻̣̮̟̩̬̮̈̋̊͆ͪ̄ͪ͒ͨͧ̇ͪ̇̑̚t̷̬̟͎̞͈̯͙̹̜ͩ̓ͪ͛͐̐ͤ̾̄̈͒̽̈́̑͒̏h̨̢̳͇͓͉̝ͫ̐̓̆̓ͮ̔̓̈́̇ͫe̟̬̣̗͚̬̾̉͋̽ͯ̌ͯͬ̂ͯͭ̓͛́̚͡ ̨̭̱͉̭͈̈̽̆̂͒͗̀ͥͩ͡h̻̼̱̹̗͖̙̗̲̤͓͇͚͚̻̞̥ͥ͛͌ͧ̚͟i̢̯̹̹̘̳̙ͩ̉ͥ͆̽̇̾̎͗̔̓͂͂́̓̌ͬv̧̡̛̟̜̠͉͖̘̲̻̯͚͍͓̯̻̲̹̥͇̻̿̓͛̊̌ͩͩ́ͩ̍͌̚e̵̾́̈́̏͌͌̊͗̏͋ͦ͘͡͏͚̜͚͎͉͍̱͙̖̹̣̘̥̤̹̟͠-̔̌͐́͒ͦͮ̇ͭ̄̏̊̇̍̕͏̩̥̰͚̟m̨̒ͫͦ̔̔͋҉̱̩̗͇̥̰̩̭͍͚͠į̵̷̻̗͉͕͚̣̼̺͉̦̮̠̆̀̐ͩ͒ͯͩͯ͞ͅn̢̫̤̝̝͚̺͍̱̦͚͂̿ͨ̇ͤ͠d̡ͯ͋̋ͧ̈́̒̈͏̛͏̵̤̬͍̗̞̠̟̞̺̠̥̹̱͉̜͍͎̤ ̷̸̢̰͓̘̯͎̤̫̘͓̙̟̳͇̹̥͈͙̮̩̅̋͌͗̓͊̓ͨͣ͗̓͐̈́ͩ̓ͣrͫ͂͌ͪ̏̐̍̾ͥ̓͗̈͆̈ͥ̀̾̚̚҉̴̶̭͇̗͙̘̯̦̭̮̪͚̥̙̯̠͙̪͡e̵̸̲͉̳̙͖͖̫̘̪͕̳͓̻̙͙ͥ̍͂̽ͨ̓̒̒̏ͬ͗ͧ̑̀͠p̵̸̛̦̣͙̳̳̩̣̼̘͈͂ͪͭͤ̎r̶̩̟̞̙͔̼ͫ̆ͦ̐̀̏̾̉̍ͬ̅ͧ͊ͪ̒̈́ͬ̃͞ẻ̴̼͙͍͎̠̀̅̔̃̒͐ͦ̏̆̅̓͋͢ͅš̆̈̆̋ͨ̅̍̇͂̒ͩͨ̂̐̓ͩ͏̸͔͔̯͇͚̤̪̬̗͈̰̦̯͚̕ę̢̱̠͙̲͉̗͚̮̪͖̙̞̦͉͕̗̳͙ͦ̆̋͌ͣ̅̊́ͅņ̴̷̫̪̦͇̺̹͉̗̬̞̲̭̜̪͒̏͂̂̎͊́̋͒̏̅̋̚͘t̷̶̨̟̦̗̦̱͌͌ͩ̀i̴̴̢̖͓͙̘͇̠̦̙̭̼͖̹̾̒̎̐ͥͭ͋ͥ̅͟ͅņ̫͙̹̦̳͈͙̬̫̮͕̰̩̣̘̘͐̀̓ͭͩͬͯ̎͛̿ͫ̊̔̅́̕͠gͥͩ̂͌̒̊̕͏̻͙͖̣͙͍̹͕̝͖̼̙̘͝ ͤ͐̓̒̓͋̐̃̇͊̓ͦ͐̚͢҉̢̨̟̠͉̳͖̲̩͙̕ć̷̡̫̩̞̯̼̝̼͖̤̳̻̘̪̤͈̦̭ͣ́͂͐̽͆̔̀̚͜h̶̢̹̹̙͔̱̓ͦ͌̋̎ͭ͒͋̒ͭ̌̃͌̿ͣ̆̅͑ą̙̳̬̞̬͚̜̤̱̙͇̠̟̈ͤ͋̃̀̓̓ͯ̍̀̽ͣ̐̈̿̌̕ǫ͋͂͐ͬ̿ͯ̂̈́͌̓̌ͧ̕͏̜͔̗͚͔̘̣͕̘̲͖̼͇͖̗̳ͅͅs̷̸̝̙̭̦̣̦̯̭̦͙̹̻͍͇̣̼͗̌͆ͨͭ̃ͮ͐̿̕.̮̝̠̱̺͖͓̼̦̱̉͂͛̓̑̔̓ͮ̈̊̔͗́͝ ̛̣̺̻̼̙̼͓̱̬͕̩͕̲̳̭̗̍ͤ͋̒̆̄ͨ̿ͧ̓͠ͅI̷̻̤̳̲͔͈̖̬̰͔̪͇͇̟̋ͨ̋̍̉̔͝͞͝ͅn̶͕̭͖̠̣͚̹̪͆ͪ̇̂̅̾ͫ́̅̉ͭ̀͜v̖͉̩͕̣͔̭͕̩̲̖̇̀ͬ́̄͒̆͑͆ͪͤ͆̾̍ͯ̚͜ǫ̡̡̫͎̟̞̰̞̹͇̲̏ͨ̄͊̊̇͒̽͢ķ̶̪̙̰̥͙̞̹̭̺͍͕̙̲̮͊ͭ́͋͛͋̑̒͊̏̒̅͛̄̓͟i̴͎̹̞̥͖̒̄ͮ̒̾ͮͧ̀̚͡n̸̵͓̲̟̞̳͚̼̣͙͖̈ͦ͒̿̅̒̿͛͊̇ͧ̉g̡̧̪̩͚͙͓̪͓͚͉̥̪͍̙̻͖͇͗̑͊͑̾̍͊̀ͅ ̷̵̠͚̘̟͓̫̣̲͎̩̹̣̼̟͊́̏ͫ̆ͩ̓͋͆̿̽̓͘̕t̴̢̝̻̖̲̬̜̺̖̻ͩ̿ͫ͗̈́̔͑̐ͮͦ̽̉̓̚͜h̷̛̲͇̫͈̣̭͂ͭ̂͋ͭ̋̔ͮ̆ͩ͞ë̩͕͉̯͇͔͚̭̼̮̣͓̯́ͭ̀ͣ͗̋̉ͨͬ̒ͥͩ͆̓̓́̀̚͘͝ ̛̫̠̗̥̳͇͉̟̮̪̻̤̪͚̟̜̔̌͌̈͌ͪ̋̎̄ͯ͐ͦ́͞͠fͦ̂̈ͬ̇̅̓̓ͫͣ̉̂̉̚͘͡͡͏̼̖̟͚̙̳͔͎̲̫̦̯͔̣̼̹ě̷̶̫͎̞̺̪̪͇͈̞̳̏̋̋͋̾̓̽̓̑ͮ͊ͣ̋̃̅̀͡e͇̗͎̱͔̦̠̰̩̩͖͙̠̻̝ͯ̿̔̀͋͑ͧ͊̆̇̿ͤ̄ͯ̇̀͢͠ͅl̂̿ͯ͛̊̒̓̈́͏̵̪̦̞̤̫̤͇̙̗͕͎̪͕̙̻̳̗̕͟͢i̞̣̙͎͈̗̮͉̱̜̱̝̞̤͋ͯ͋͐̈́ͫ̉̊̏̀ͯͨ͢͟͝n̳̻̼̥̖͍̭̅͂̓̔̔ͦ̔́ͦ͊̀͛̈́ͬͦ͢͡͡ģ̶̡̳̰̻̙̞̱̳̣̤̫̫͕̤̮̰̬̪̜͋͒̎̈́̉̏̀ͬͯ͌̇͊̚ ́̽ͤͦ̾̔͢҉̛̤͍͉̺̙̮̗̜̟̀͝ơ̢̱͓͓̙͉̖̠̯̦̗͍̓̐̃̉̅̃ͨ͆́ͪ̂̒̀̊̃͆̔͡͡ͅf́ͬ̊ͯͫ̉̈́̽̉̚͢͏̡̺̬̖͇̫͉̱ ̴͇̦̗̙̼̬͓̯͖̮͓͎̗͈̻̈́͆ͭ̐ͦ́͛̀͋̐̌ͬ͑̒̊̿̃͞c̶̸̣͔̬͕̪̱̩̣̑̒̑̓̍̓͂̍̔͌̚͘͜͞h̶͈̱͇͉̳͍͍̰͈͖̬̥͚̯͓̞̹̋̔ͯ̑̃́̒̎̎͊̈́̍̚̕ạ̴̞̱̥͍͙̺͉͚͎̫̦͎̥ͩ̀̀̊ͥ͢o̵̧͕̜͓͈̬̰̫̮͙̹͉̩̝̩͎̓̆͗̿̊̀ͯ̃ͪ̊ͫ̽̉̓ͧ͗́̚͢ͅͅs̡ͫ͋̑ͮ̍̃͊̄ͬ̅̈́ͬ̍̇̔̈̅̍̀҉̜͓̝̘̘̮̼͖͎̻͓͖̖͙̞ͅ.͗ͬͭͩ̌̅͗͏̷̮̗͇͔͇͈̮͢ ̨͚̲̫̠̼͖̝̻̉ͤ̅̂ͩ̀̇ͬͭ̀͜Ẅ̢́̉͌ͮͬͨ͊̏͌̇̐͊͟͠҉̼̰̦̩͇͕̟̭̪̲͕̥͖̰̪͈̀ͅͅį̷ͣͦ̉̍ͨ͂͂͑̃͂ͪ̊̈̋̄͜҉̨͚̟̲̯̹̺̝̭̺̙͖͍t̼͓̰̩͙̦͓̟͚͖̀ͯ͛̍̈́͑͂̍̋́h̛̼̺̘̥̠̼̼̭͙̮͚̱̍ͯ̓̃̐̂̇͟ ̴̛͖͔̰̠̺̥̲ͮ̍ͫ̽͜õ̒ͯ̒̓ͦ̈́͑̔̒̓̎ͤ͑҉̸̭̱̤̭̬͈ų̙̫̤͖̺̫̱͓͓̗̪͇̩̙̔̉̊͂ͪ̇͢͟͞ͅt̸̬̣̫̞̫̅͐ͮ̌͌̈́̀̀͘ ̷̴̨̖̙̹͚ͬ̈́̈ͯͨͮ̇̈́̋̈́ͭ͛̑̉͊̕ö̡̍ͥ̂ͬͪͧ͒ͧ̏̓̇̂̄͆̌ͫͤ͢͠͝͏̖̱̯̘͙̰̖͎̰͓̟̤ṙ̡̬̟̬̜̪̮̺͖̗̘͈̟ͨ͐͗̑͒̐d̢ͭͫ̊̏ͬͥ͋́̌̈́ͮ̆ͬ̐̌̎͏̵̷̡̞̲̹̙͕̮̮͚ḙ̴̸̠͔͎̥͇͖͕̘̍̓̏̐ͩͩ̈́ͦ̐̋ͤ̎̾̌̏͊̊́̚͞ͅr̸͈̗̣̲̗̣̬̤ͦ̎ͫ̏̀ͥͪ̋ͧ̄͑̋͒͌͋ͦ̉͟͞.ͨͣ̽̈́͒̄ͮ̀͋͋͏̴̧̯̺̙̱̻͙̜ ̡̣̞̠͓̰͍̠͕̭̺̼͊̽̿͊ͮ̐̓̒̊͒̔̓͐ͨ̈̌́T̸̸̓́̋ͬ́͆ͨͫ͌͂ͣ̋͒҉̺̝͎̟͖͚̠h̸̡̰̜̦͇͕̪̝̳͕͉̲̝̑ͥ͋ͧ̎̆͌͟e̛̹͍͍̫̙̞̪̭̙̟͙̱̺̮̳͕̜ͫ̓ͭ͊ͫ͆̀̚͟͡ ̿͂̄ͧ̔̎ͧ͑̾̀̓͏̦͍̳͈̳͔̘̖̲̯̰̟̝̳̖̦N̶̡̧̦̮̟̦̩̰̣̝̆̀͊̔͢e͛̄ͮͦͨ͂̔̓̍̄̉͆͊̑̑̆̚͏̜̗͎̝̼̯̥̜͖͍̪̝͞ͅͅz̨̛̀̾ͪ͗̉́͠͏͚̫̼̫̜̣pͪͦ͌̄ͥ̆ͣͩ͋̉́̏͞͏̥̜̝̳̱̞̙̳̤͙̟̟̮̦ȅ̷̩̟͉̯͕͔̘̺̥̻̻ͧ̊̅̽ͣ͑̓̑̽ͦ̾͌͜r̴̭̥̲̲̤͚͈̰͇̰͈̰̹ͫ̒ͯ̿͒ͧ̊͆͒ͣ́ḍ̭̟̤̈́̌̓̈́ͫ͐̍͂͞į̛̞̝̮̣͙͙̤̇̂̓̎͋̿̓̎̄̈́ͧ̓ͩ̐̓̄̋ͭ͞͠a͋̔̋ͫ̂͐͂҉̸̛̥̩̯̯̤̝͔̠̝̯̪̥̩̻̼̮n͌ͣ̂͋̿̚҉̛̙̲̺̯͇͓̝̯̪̟͔̩͟ͅ ̢̨͚̻̗̘͖̯̐ͥ͋̽ͯ̎̈́͋̏̄͋̆̑̊̆̚̕͟ͅh̢̛̗̱̭͇͖̰̮̮͈̲͍̯̟ͭ͊̎̽̓ͦͤ͠ï̛̘̝̦͎̦̭̠͖̳͎̮̼̏͐ͧ̒̒͐͑ͪͫ̋̽̚̚͜v̴̮͕̝̮̞͐̄͗̋͒ͤ̎̈̑ͬͮ̄̾ͤ̓̾͊͗͟é̶̷̡̩͖̰̫͓̟ͮͬͣ͊-ͦ͛ͩͤͨͨ̆̄͏̼̜̭͔̳͈͖̳̩͢ͅͅm̷̴̓́̓͛͒̾̍̉҉̛̗̹̠̣̪̺͎̖̝͚̖͙i̛̥͓̬̫͉͕͉͆͒ͧ̂̿̔̔͆̆̓̍͊̀͜n͌ͧͣ̅̌̎ͦͦ͑̑ͭ̆ͬ̀ͤ̀ͣ̚҉͎̰̱͚͈͈̬̹͕̺̙͙̼͘͘͞d̶͖̫̟̲͕̺̠͎̘͕̱̼͙̪̪̩͙̅̅̑̓̇͑̊̉͜͞ ̶̵̷̴̡̠͚̪͕̣̱̖̱̗̤̭̭͔͖͚ͧͤͥ͒̌ͪ͊͂͒̓͂ͧͧ̇̇͐̑̔ͅͅơ̵̲̲͇̯̰͇̜̣͕͕͓̲̤̲͔͚̞͑͗ͤ̓́̚͠ͅf̢̧̛̩̯̼̫͖̾ͣ͌̾̉́̈́̑̈́̚͞͞ͅ ͤͩ́͋͒ͫͬͣ̋̅̆҉̧̱̻͓͕͉̹̫̫̞̯̪̙̩͍̦͔̖̮̀͟ͅc͉̠̜̩̟͕͎̙̣̮̘̼͋ͯ̍ͨ̅̄ͫ̈̋ͫ̊͡͝ȟ̸̨ͯͦ̂̉̇̾̆ͭ̋̐̈̆̀̚͜҉͚͕̻̖a̶̴̛͚̗͙̳̬̲͚ͦ́̐ͥ́̔̅̑̎͐̑ͯ̾ͤͥͧ͡ò̶̧̞̪̦̥̪̻̦̝̳̬̔͛͛ͣ̋̌̔ͫ̂̽ͫ͘͠s̸̖̣̬̤̫͇̫̣̑͆͒̎̏́͟.̴̗̤̭͉̯̻̤͕̌ͯ̍ͤ̓͌ͤ̈̆̉ͦ̇́̚͘͟͝ͅ ̯̹̪͓̬͌̔̌ͬ̀͘͢͡͡Z̡̩̲̩̰̫̩̟͍̰͖͔̭ͣ̆̾ͭ̀́͞ͅa̡̡̙̜̭͇͎͔̙̞̫͓̜͉͔̬ͭ̈ͨ̉͆ͣͫ̃͌̓͌́ͣͥ̒̌͊͘͝l̢̨̡̯̙̫͖̫̺̘̬̟͈͌̊ͧͫͦ̉̃ͩͦ̒ͯ̇̌̓͛͟͝ͅg̵̙̼̼ͪ͂ͭ͗̈̕ȯ̅ͧ̓ͪ́̂͑̐ͩͥͬ̊̑͆̇͒ͫͣ͝҉͎̟̜̥͎̮̣͉̖̟̯̦̖͙͙͞ͅ.̈̑ͩ̇̂ͬ̓ͬ͊͂ͨ̽͠͏̺͎̞̦̜͍͚̯̯͔̝̞̻̩̖ ̷̰̪͍͎͔͒ͯͥ̾̉͆ͤ̊̓̂͋̀͆H̸̸̹̞̙̺͎̠̯̤ͨ̉̍ͬͤ̓̐͌ͥͮ͞eͣ̈̾͛́͏͕̗͍̜̼͎͚̟̬̣̝̕ͅͅ ̴̛̩̗̼̝̣̩͚͇̯́̉͋̂̍͂̌ͮ͋̾͜͠wͮ̽̓ͭ̿͐̽̐̽͆̓͝҉̡̼̲͖̪̥h̢̢̛͍̰̰̻̱̼̰̹̖̖̪̝̥̘̎̀ͪ͒̾ͫͬ̆̑o̡̗̠̞̱̥͎̰͎͍̫̻͓͇͓͐ͥͯ͂̅͠ͅ ̡̛̏͑ͦ̓͊ͮͫͯͭ̌͒̆̍̈͠҉͖͚̪̫̗̮W̴̐̊͋̾ͥͫ҉͎̞͔̯̫̹͖̰͉̹̼͎̰̱͓̻̀a̶̩̤̙̣͎̳̭̲̗̠͉̳̭̭̦̞͎̮̅͌̾͗̾͛̇̀́͟͞ͅi̷̡ͣ̆̌͋͒͒́͘͏̮̺̩͎͇̜͍̫ṯ̴̢͖̥̖͇͎̦̦̹̖͇̪ͭ̅̍͐̇͒͋̽̏̿̒͆ͧ̄͋ͧͩ͒͜s̙̥̖̘̖͚̭̤̮̖̘̰̫̟̈́ͣ̍ͧ͐ͥ̏͆̃̿͒̔͐́̚͟ͅ ̨ͭ̌ͬͯ͆̒͋ͭ̔̿ͧ̅̓ͣ͡͏͇̟͉̥͔̬̼͚͙͚B̛̜̮̤͓̝̪̪͈͕̘̜͙̰̮̫̘̣͓͔̅ͩ͊̔ͦͯ́̌́͆ͭ̓́e̶̢̡̦͇͕̙͈͖͕̦̬̫͕̣̺̒̿͂͐͒͋͂ͦ́͋ͤ̿ͬ̊ͣ͗̑̽͜ͅͅh̸͑ͫͧ̑ͬͧ̈́̎̃ͣ̊̾͂ͨͤ̓͐̐̑͏̸̭͓̘͉̩i̧̧̭̣͈̝̺̼̺̠͉̞̜̲̳͙̦͐̔ͯ͛̅̾n̸͓̝̤̙͙͔ͪ̋̈́͒̒ͭ̈́̓ͮ̋̀̋̀̈ͩ́͌̄͘d̷̫̳̩̼̥̗̲̰͇͉̼̬̤͇̖ͮ̿ͬ͂ͦ̏̓ͮ̽͂̾̾ͯ͆͜͠ ̨̈́͒̇̏̄̑̓ͮͥ̒ͤͨ̋҉̴̴̟̱͙̟̫̩̗͔̝͔̀Ţ̵̝̟̖̭͇̻̳͖͉̺̖̖͙͙̺̐̈́̓ͯ̆̇̋ͩ͊̄̾̾ͬ̌̚͟ͅh̡͈̗̜͙̬̗̲̦̲̟̗̦̬͓̳ͧ̋̌͂͂ͨͬͦ̿̏̈́̋ͣ̒̕͡ͅͅe̗͇̰̰̥̪̗͑̔̓́̈́ͨ̊́̿̅ͯͥ̈́͐͗͘͢͝ ̡̢̛̯͎͓̰̘͎̦̪̯̪̥̰̲͇̠̲͔ͤͤ̇̅̆̋̂̆̈́ͤ̿͑ͅW̡͓͈̲̲͉̜͔̖͈̻̱͚̿̌͗̉ͤ͢͡ͅͅa̔̾͛̅͊͋͐҉̱̹̬͍͙̻̱l̢͎̟̬̙̼̱̫̮̘̼͔̭̅ͬ͑ͣ̏̾̅̓ͣ̿ͣ̈́̕͢͡ͅͅl̡̥̣͔̭̇̒͛͒͐̄̽͛̋ͥ̌͢͟͡.̷̰̝̮͔̟̦͈̥̬̻̥̬͎͓̻̲̇ͮ̿ͨͦ̽ͫ͟͢͝͠ ̗̱͖͈͌̈ͦ͛ͮ̌͋̽̃͆̀͂ͨͧ̄̔̔ͭ̏͢Z̃̉̿ͮ̃̀͘͏͕̬̯̖͚̗͔Aͣ̑̈̓̈̑̈̀̿̚҉͙͍̦̗̦͙̠̝̩̯ͅͅL̴͖̞̞͙̱̻̥̬̜̦̐̇̉̈̽ͪ̅ͪ̂̔͌͑ͭ͐ͤ̈́̿̉͞ͅG̴̵̲̰̹̖͎͕ͯ̆̓̽͢͠Ŏ̶̡̺̼͙̣̞̩͕̥̟̝͕͔̯̞ͨ͒͊̂̊͂͗̒͆̾͆̌͆̃̎ͣͫ͜͡ͅ!̓̽̎̑̏́̓̓ͣ̀͏̱̩̭̣̹̺̗͜͞͞ Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sit amet tellus nec turpis feugiat semper. Nam at nulla laoreet, finibus eros sit amet, fringilla mauris. Fusce vestibulum nec ligula efficitur laoreet. Nunc orci leo, varius eget ligula vulputate, consequat eleifend nisi. Cras justo purus, imperdiet a augue malesuada, convallis cursus libero. Fusce pretium arcu in elementum laoreet. Duis mauris nulla, suscipit at est nec, malesuada pellentesque eros. Quisque semper porta malesuada. Nunc hendrerit est ac faucibus mollis. Nam fermentum id libero sed egestas. Duis a accumsan sapien. Nam neque diam, congue non erat et, porta sagittis turpis. Vivamus vitae mauris sit amet massa mollis molestie. Morbi scelerisque, augue id congue imperdiet, felis lacus euismod dui, vitae facilisis massa dui quis sapien. Vivamus hendrerit a urna a lobortis. Donec ut suscipit risus. Vivamus dictum auctor vehicula. Sed lacinia ligula sit amet urna tristique commodo. Sed sapien risus, egestas ac tempus vel, pellentesque sed velit. Duis pulvinar blandit suscipit. Curabitur viverra dignissim est quis ornare. Nam et lectus purus. Integer sed augue vehicula, volutpat est vel, convallis justo. Suspendisse a convallis nibh, pulvinar rutrum nisi. Fusce ultrices accumsan mauris vitae ornare. Cras elementum et ante at tincidunt. Sed luctus scelerisque lobortis. Sed vel dictum enim. Fusce quis arcu euismod, iaculis mi id, placerat nulla. Pellentesque porttitor felis elementum justo porttitor auctor. T̴̷͚͖̜͈̪͎͔̝̫̦̹͔̻̮͂ͬͬ̌ͣ̿ͤ͌ͥ͑̀̂ͬ̚͘͜͞ô̵͚̤̯̹͖͍̦̼̦̖̞̺͕̳̬͇͕̟̜̅̌̈́̑̏̕͘͝ ͍̼̗̫͈̭̦̱̬͚̱̞͓̜̭̼͇̰̞ͮ͗ͣ́ͪ̔ͪ̍̑̏́̀̽̍̔͘͜͜͝ȋ̐̽ͦ̓̔̅͏̧̢̖̭̝̳̹̯̤͈̫͔͔̠͓͉̠͖̠͜ͅn̷̯̗̗̠̱̥͕͉̥͉̳̫̙̅͗̌̒͂̏͑̎̌̌̊͌͘͘ͅͅv̧̜͕͍͙͍̬͕͍̳͉̠͍̹̮̻̜ͨ̏͒̍ͬ̈́͒̈ͥ͗ͣ̄̃ͤ͊̌͆̓o̸̧̎̓͂̊͢҉͍̼̘͇̱̪̠͎̥̹ķ̈́͗͆ͥ͐͑̆̎́͌ͩͯ̊̓͐ͬ̇̕҉̢͏͚̲̰̗̦e̿̀͐̽ͪ̈ͤͬ҉́͟͏̵̫̲̱̻̰̲̦͇̭̟̺͈̞̫̰̜͕͖ͅ ̡̰͎͓͚͓͉͈̮̻̣̮̟̩̬̮̈̋̊͆ͪ̄ͪ͒ͨͧ̇ͪ̇̑̚t̷̬̟͎̞͈̯͙̹̜ͩ̓ͪ͛͐̐ͤ̾̄̈͒̽̈́̑͒̏h̨̢̳͇͓͉̝ͫ̐̓̆̓ͮ̔̓̈́̇ͫe̟̬̣̗͚̬̾̉͋̽ͯ̌ͯͬ̂ͯͭ̓͛́̚͡ ̨̭̱͉̭͈̈̽̆̂͒͗̀ͥͩ͡h̻̼̱̹̗͖̙̗̲̤͓͇͚͚̻̞̥ͥ͛͌ͧ̚͟i̢̯̹̹̘̳̙ͩ̉ͥ͆̽̇̾̎͗̔̓͂͂́̓̌ͬv̧̡̛̟̜̠͉͖̘̲̻̯͚͍͓̯̻̲̹̥͇̻̿̓͛̊̌ͩͩ́ͩ̍͌̚e̵̾́̈́̏͌͌̊͗̏͋ͦ͘͡͏͚̜͚͎͉͍̱͙̖̹̣̘̥̤̹̟͠-̔̌͐́͒ͦͮ̇ͭ̄̏̊̇̍̕͏̩̥̰͚̟m̨̒ͫͦ̔̔͋҉̱̩̗͇̥̰̩̭͍͚͠į̵̷̻̗͉͕͚̣̼̺͉̦̮̠̆̀̐ͩ͒ͯͩͯ͞ͅn̢̫̤̝̝͚̺͍̱̦͚͂̿ͨ̇ͤ͠d̡ͯ͋̋ͧ̈́̒̈͏̛͏̵̤̬͍̗̞̠̟̞̺̠̥̹̱͉̜͍͎̤ ̷̸̢̰͓̘̯͎̤̫̘͓̙̟̳͇̹̥͈͙̮̩̅̋͌͗̓͊̓ͨͣ͗̓͐̈́ͩ̓ͣrͫ͂͌ͪ̏̐̍̾ͥ̓͗̈͆̈ͥ̀̾̚̚҉̴̶̭͇̗͙̘̯̦̭̮̪͚̥̙̯̠͙̪͡e̵̸̲͉̳̙͖͖̫̘̪͕̳͓̻̙͙ͥ̍͂̽ͨ̓̒̒̏ͬ͗ͧ̑̀͠p̵̸̛̦̣͙̳̳̩̣̼̘͈͂ͪͭͤ̎r̶̩̟̞̙͔̼ͫ̆ͦ̐̀̏̾̉̍ͬ̅ͧ͊ͪ̒̈́ͬ̃͞ẻ̴̼͙͍͎̠̀̅̔̃̒͐ͦ̏̆̅̓͋͢ͅš̆̈̆̋ͨ̅̍̇͂̒ͩͨ̂̐̓ͩ͏̸͔͔̯͇͚̤̪̬̗͈̰̦̯͚̕ę̢̱̠͙̲͉̗͚̮̪͖̙̞̦͉͕̗̳͙ͦ̆̋͌ͣ̅̊́ͅņ̴̷̫̪̦͇̺̹͉̗̬̞̲̭̜̪͒̏͂̂̎͊́̋͒̏̅̋̚͘t̷̶̨̟̦̗̦̱͌͌ͩ̀i̴̴̢̖͓͙̘͇̠̦̙̭̼͖̹̾̒̎̐ͥͭ͋ͥ̅͟ͅņ̫͙̹̦̳͈͙̬̫̮͕̰̩̣̘̘͐̀̓ͭͩͬͯ̎͛̿ͫ̊̔̅́̕͠gͥͩ̂͌̒̊̕͏̻͙͖̣͙͍̹͕̝͖̼̙̘͝ ͤ͐̓̒̓͋̐̃̇͊̓ͦ͐̚͢҉̢̨̟̠͉̳͖̲̩͙̕ć̷̡̫̩̞̯̼̝̼͖̤̳̻̘̪̤͈̦̭ͣ́͂͐̽͆̔̀̚͜h̶̢̹̹̙͔̱̓ͦ͌̋̎ͭ͒͋̒ͭ̌̃͌̿ͣ̆̅͑ą̙̳̬̞̬͚̜̤̱̙͇̠̟̈ͤ͋̃̀̓̓ͯ̍̀̽ͣ̐̈̿̌̕ǫ͋͂͐ͬ̿ͯ̂̈́͌̓̌ͧ̕͏̜͔̗͚͔̘̣͕̘̲͖̼͇͖̗̳ͅͅs̷̸̝̙̭̦̣̦̯̭̦͙̹̻͍͇̣̼͗̌͆ͨͭ̃ͮ͐̿̕.̮̝̠̱̺͖͓̼̦̱̉͂͛̓̑̔̓ͮ̈̊̔͗́͝ ̛̣̺̻̼̙̼͓̱̬͕̩͕̲̳̭̗̍ͤ͋̒̆̄ͨ̿ͧ̓͠ͅI̷̻̤̳̲͔͈̖̬̰͔̪͇͇̟̋ͨ̋̍̉̔͝͞͝ͅn̶͕̭͖̠̣͚̹̪͆ͪ̇̂̅̾ͫ́̅̉ͭ̀͜v̖͉̩͕̣͔̭͕̩̲̖̇̀ͬ́̄͒̆͑͆ͪͤ͆̾̍ͯ̚͜ǫ̡̡̫͎̟̞̰̞̹͇̲̏ͨ̄͊̊̇͒̽͢ķ̶̪̙̰̥͙̞̹̭̺͍͕̙̲̮͊ͭ́͋͛͋̑̒͊̏̒̅͛̄̓͟i̴͎̹̞̥͖̒̄ͮ̒̾ͮͧ̀̚͡n̸̵͓̲̟̞̳͚̼̣͙͖̈ͦ͒̿̅̒̿͛͊̇ͧ̉g̡̧̪̩͚͙͓̪͓͚͉̥̪͍̙̻͖͇͗̑͊͑̾̍͊̀ͅ ̷̵̠͚̘̟͓̫̣̲͎̩̹̣̼̟͊́̏ͫ̆ͩ̓͋͆̿̽̓͘̕t̴̢̝̻̖̲̬̜̺̖̻ͩ̿ͫ͗̈́̔͑̐ͮͦ̽̉̓̚͜h̷̛̲͇̫͈̣̭͂ͭ̂͋ͭ̋̔ͮ̆ͩ͞ë̩͕͉̯͇͔͚̭̼̮̣͓̯́ͭ̀ͣ͗̋̉ͨͬ̒ͥͩ͆̓̓́̀̚͘͝ ̛̫̠̗̥̳͇͉̟̮̪̻̤̪͚̟̜̔̌͌̈͌ͪ̋̎̄ͯ͐ͦ́͞͠fͦ̂̈ͬ̇̅̓̓ͫͣ̉̂̉̚͘͡͡͏̼̖̟͚̙̳͔͎̲̫̦̯͔̣̼̹ě̷̶̫͎̞̺̪̪͇͈̞̳̏̋̋͋̾̓̽̓̑ͮ͊ͣ̋̃̅̀͡e͇̗͎̱͔̦̠̰̩̩͖͙̠̻̝ͯ̿̔̀͋͑ͧ͊̆̇̿ͤ̄ͯ̇̀͢͠ͅl̂̿ͯ͛̊̒̓̈́͏̵̪̦̞̤̫̤͇̙̗͕͎̪͕̙̻̳̗̕͟͢i̞̣̙͎͈̗̮͉̱̜̱̝̞̤͋ͯ͋͐̈́ͫ̉̊̏̀ͯͨ͢͟͝n̳̻̼̥̖͍̭̅͂̓̔̔ͦ̔́ͦ͊̀͛̈́ͬͦ͢͡͡ģ̶̡̳̰̻̙̞̱̳̣̤̫̫͕̤̮̰̬̪̜͋͒̎̈́̉̏̀ͬͯ͌̇͊̚ ́̽ͤͦ̾̔͢҉̛̤͍͉̺̙̮̗̜̟̀͝ơ̢̱͓͓̙͉̖̠̯̦̗͍̓̐̃̉̅̃ͨ͆́ͪ̂̒̀̊̃͆̔͡͡ͅf́ͬ̊ͯͫ̉̈́̽̉̚͢͏̡̺̬̖͇̫͉̱ ̴͇̦̗̙̼̬͓̯͖̮͓͎̗͈̻̈́͆ͭ̐ͦ́͛̀͋̐̌ͬ͑̒̊̿̃͞c̶̸̣͔̬͕̪̱̩̣̑̒̑̓̍̓͂̍̔͌̚͘͜͞h̶͈̱͇͉̳͍͍̰͈͖̬̥͚̯͓̞̹̋̔ͯ̑̃́̒̎̎͊̈́̍̚̕ạ̴̞̱̥͍͙̺͉͚͎̫̦͎̥ͩ̀̀̊ͥ͢o̵̧͕̜͓͈̬̰̫̮͙̹͉̩̝̩͎̓̆͗̿̊̀ͯ̃ͪ̊ͫ̽̉̓ͧ͗́̚͢ͅͅs̡ͫ͋̑ͮ̍̃͊̄ͬ̅̈́ͬ̍̇̔̈̅̍̀҉̜͓̝̘̘̮̼͖͎̻͓͖̖͙̞ͅ.͗ͬͭͩ̌̅͗͏̷̮̗͇͔͇͈̮͢ ̨͚̲̫̠̼͖̝̻̉ͤ̅̂ͩ̀̇ͬͭ̀͜Ẅ̢́̉͌ͮͬͨ͊̏͌̇̐͊͟͠҉̼̰̦̩͇͕̟̭̪̲͕̥͖̰̪͈̀ͅͅį̷ͣͦ̉̍ͨ͂͂͑̃͂ͪ̊̈̋̄͜҉̨͚̟̲̯̹̺̝̭̺̙͖͍t̼͓̰̩͙̦͓̟͚͖̀ͯ͛̍̈́͑͂̍̋́h̛̼̺̘̥̠̼̼̭͙̮͚̱̍ͯ̓̃̐̂̇͟ ̴̛͖͔̰̠̺̥̲ͮ̍ͫ̽͜õ̒ͯ̒̓ͦ̈́͑̔̒̓̎ͤ͑҉̸̭̱̤̭̬͈ų̙̫̤͖̺̫̱͓͓̗̪͇̩̙̔̉̊͂ͪ̇͢͟͞ͅt̸̬̣̫̞̫̅͐ͮ̌͌̈́̀̀͘ ̷̴̨̖̙̹͚ͬ̈́̈ͯͨͮ̇̈́̋̈́ͭ͛̑̉͊̕ö̡̍ͥ̂ͬͪͧ͒ͧ̏̓̇̂̄͆̌ͫͤ͢͠͝͏̖̱̯̘͙̰̖͎̰͓̟̤ṙ̡̬̟̬̜̪̮̺͖̗̘͈̟ͨ͐͗̑͒̐d̢ͭͫ̊̏ͬͥ͋́̌̈́ͮ̆ͬ̐̌̎͏̵̷̡̞̲̹̙͕̮̮͚ḙ̴̸̠͔͎̥͇͖͕̘̍̓̏̐ͩͩ̈́ͦ̐̋ͤ̎̾̌̏͊̊́̚͞ͅr̸͈̗̣̲̗̣̬̤ͦ̎ͫ̏̀ͥͪ̋ͧ̄͑̋͒͌͋ͦ̉͟͞.ͨͣ̽̈́͒̄ͮ̀͋͋͏̴̧̯̺̙̱̻͙̜ ̡̣̞̠͓̰͍̠͕̭̺̼͊̽̿͊ͮ̐̓̒̊͒̔̓͐ͨ̈̌́T̸̸̓́̋ͬ́͆ͨͫ͌͂ͣ̋͒҉̺̝͎̟͖͚̠h̸̡̰̜̦͇͕̪̝̳͕͉̲̝̑ͥ͋ͧ̎̆͌͟e̛̹͍͍̫̙̞̪̭̙̟͙̱̺̮̳͕̜ͫ̓ͭ͊ͫ͆̀̚͟͡ ̿͂̄ͧ̔̎ͧ͑̾̀̓͏̦͍̳͈̳͔̘̖̲̯̰̟̝̳̖̦N̶̡̧̦̮̟̦̩̰̣̝̆̀͊̔͢e͛̄ͮͦͨ͂̔̓̍̄̉͆͊̑̑̆̚͏̜̗͎̝̼̯̥̜͖͍̪̝͞ͅͅz̨̛̀̾ͪ͗̉́͠͏͚̫̼̫̜̣pͪͦ͌̄ͥ̆ͣͩ͋̉́̏͞͏̥̜̝̳̱̞̙̳̤͙̟̟̮̦ȅ̷̩̟͉̯͕͔̘̺̥̻̻ͧ̊̅̽ͣ͑̓̑̽ͦ̾͌͜r̴̭̥̲̲̤͚͈̰͇̰͈̰̹ͫ̒ͯ̿͒ͧ̊͆͒ͣ́ḍ̭̟̤̈́̌̓̈́ͫ͐̍͂͞į̛̞̝̮̣͙͙̤̇̂̓̎͋̿̓̎̄̈́ͧ̓ͩ̐̓̄̋ͭ͞͠a͋̔̋ͫ̂͐͂҉̸̛̥̩̯̯̤̝͔̠̝̯̪̥̩̻̼̮n͌ͣ̂͋̿̚҉̛̙̲̺̯͇͓̝̯̪̟͔̩͟ͅ ̢̨͚̻̗̘͖̯̐ͥ͋̽ͯ̎̈́͋̏̄͋̆̑̊̆̚̕͟ͅh̢̛̗̱̭͇͖̰̮̮͈̲͍̯̟ͭ͊̎̽̓ͦͤ͠ï̛̘̝̦͎̦̭̠͖̳͎̮̼̏͐ͧ̒̒͐͑ͪͫ̋̽̚̚͜v̴̮͕̝̮̞͐̄͗̋͒ͤ̎̈̑ͬͮ̄̾ͤ̓̾͊͗͟é̶̷̡̩͖̰̫͓̟ͮͬͣ͊-ͦ͛ͩͤͨͨ̆̄͏̼̜̭͔̳͈͖̳̩͢ͅͅm̷̴̓́̓͛͒̾̍̉҉̛̗̹̠̣̪̺͎̖̝͚̖͙i̛̥͓̬̫͉͕͉͆͒ͧ̂̿̔̔͆̆̓̍͊̀͜n͌ͧͣ̅̌̎ͦͦ͑̑ͭ̆ͬ̀ͤ̀ͣ̚҉͎̰̱͚͈͈̬̹͕̺̙͙̼͘͘͞d̶͖̫̟̲͕̺̠͎̘͕̱̼͙̪̪̩͙̅̅̑̓̇͑̊̉͜͞ ̶̵̷̴̡̠͚̪͕̣̱̖̱̗̤̭̭͔͖͚ͧͤͥ͒̌ͪ͊͂͒̓͂ͧͧ̇̇͐̑̔ͅͅơ̵̲̲͇̯̰͇̜̣͕͕͓̲̤̲͔͚̞͑͗ͤ̓́̚͠ͅf̢̧̛̩̯̼̫͖̾ͣ͌̾̉́̈́̑̈́̚͞͞ͅ ͤͩ́͋͒ͫͬͣ̋̅̆҉̧̱̻͓͕͉̹̫̫̞̯̪̙̩͍̦͔̖̮̀͟ͅc͉̠̜̩̟͕͎̙̣̮̘̼͋ͯ̍ͨ̅̄ͫ̈̋ͫ̊͡͝ȟ̸̨ͯͦ̂̉̇̾̆ͭ̋̐̈̆̀̚͜҉͚͕̻̖a̶̴̛͚̗͙̳̬̲͚ͦ́̐ͥ́̔̅̑̎͐̑ͯ̾ͤͥͧ͡ò̶̧̞̪̦̥̪̻̦̝̳̬̔͛͛ͣ̋̌̔ͫ̂̽ͫ͘͠s̸̖̣̬̤̫͇̫̣̑͆͒̎̏́͟.̴̗̤̭͉̯̻̤͕̌ͯ̍ͤ̓͌ͤ̈̆̉ͦ̇́̚͘͟͝ͅ ̯̹̪͓̬͌̔̌ͬ̀͘͢͡͡Z̡̩̲̩̰̫̩̟͍̰͖͔̭ͣ̆̾ͭ̀́͞ͅa̡̡̙̜̭͇͎͔̙̞̫͓̜͉͔̬ͭ̈ͨ̉͆ͣͫ̃͌̓͌́ͣͥ̒̌͊͘͝l̢̨̡̯̙̫͖̫̺̘̬̟͈͌̊ͧͫͦ̉̃ͩͦ̒ͯ̇̌̓͛͟͝ͅg̵̙̼̼ͪ͂ͭ͗̈̕ȯ̅ͧ̓ͪ́̂͑̐ͩͥͬ̊̑͆̇͒ͫͣ͝҉͎̟̜̥͎̮̣͉̖̟̯̦̖͙͙͞ͅ.̈̑ͩ̇̂ͬ̓ͬ͊͂ͨ̽͠͏̺͎̞̦̜͍͚̯̯͔̝̞̻̩̖ ̷̰̪͍͎͔͒ͯͥ̾̉͆ͤ̊̓̂͋̀͆H̸̸̹̞̙̺͎̠̯̤ͨ̉̍ͬͤ̓̐͌ͥͮ͞eͣ̈̾͛́͏͕̗͍̜̼͎͚̟̬̣̝̕ͅͅ ̴̛̩̗̼̝̣̩͚͇̯́̉͋̂̍͂̌ͮ͋̾͜͠wͮ̽̓ͭ̿͐̽̐̽͆̓͝҉̡̼̲͖̪̥h̢̢̛͍̰̰̻̱̼̰̹̖̖̪̝̥̘̎̀ͪ͒̾ͫͬ̆̑o̡̗̠̞̱̥͎̰͎͍̫̻͓͇͓͐ͥͯ͂̅͠ͅ ̡̛̏͑ͦ̓͊ͮͫͯͭ̌͒̆̍̈͠҉͖͚̪̫̗̮W̴̐̊͋̾ͥͫ҉͎̞͔̯̫̹͖̰͉̹̼͎̰̱͓̻̀a̶̩̤̙̣͎̳̭̲̗̠͉̳̭̭̦̞͎̮̅͌̾͗̾͛̇̀́͟͞ͅi̷̡ͣ̆̌͋͒͒́͘͏̮̺̩͎͇̜͍̫ṯ̴̢͖̥̖͇͎̦̦̹̖͇̪ͭ̅̍͐̇͒͋̽̏̿̒͆ͧ̄͋ͧͩ͒͜s̙̥̖̘̖͚̭̤̮̖̘̰̫̟̈́ͣ̍ͧ͐ͥ̏͆̃̿͒̔͐́̚͟ͅ ̨ͭ̌ͬͯ͆̒͋ͭ̔̿ͧ̅̓ͣ͡͏͇̟͉̥͔̬̼͚͙͚B̛̜̮̤͓̝̪̪͈͕̘̜͙̰̮̫̘̣͓͔̅ͩ͊̔ͦͯ́̌́͆ͭ̓́e̶̢̡̦͇͕̙͈͖͕̦̬̫͕̣̺̒̿͂͐͒͋͂ͦ́͋ͤ̿ͬ̊ͣ͗̑̽͜ͅͅh̸͑ͫͧ̑ͬͧ̈́̎̃ͣ̊̾͂ͨͤ̓͐̐̑͏̸̭͓̘͉̩i̧̧̭̣͈̝̺̼̺̠͉̞̜̲̳͙̦͐̔ͯ͛̅̾n̸͓̝̤̙͙͔ͪ̋̈́͒̒ͭ̈́̓ͮ̋̀̋̀̈ͩ́͌̄͘d̷̫̳̩̼̥̗̲̰͇͉̼̬̤͇̖ͮ̿ͬ͂ͦ̏̓ͮ̽͂̾̾ͯ͆͜͠ ̨̈́͒̇̏̄̑̓ͮͥ̒ͤͨ̋҉̴̴̟̱͙̟̫̩̗͔̝͔̀Ţ̵̝̟̖̭͇̻̳͖͉̺̖̖͙͙̺̐̈́̓ͯ̆̇̋ͩ͊̄̾̾ͬ̌̚͟ͅh̡͈̗̜͙̬̗̲̦̲̟̗̦̬͓̳ͧ̋̌͂͂ͨͬͦ̿̏̈́̋ͣ̒̕͡ͅͅe̗͇̰̰̥̪̗͑̔̓́̈́ͨ̊́̿̅ͯͥ̈́͐͗͘͢͝ ̡̢̛̯͎͓̰̘͎̦̪̯̪̥̰̲͇̠̲͔ͤͤ̇̅̆̋̂̆̈́ͤ̿͑ͅW̡͓͈̲̲͉̜͔̖͈̻̱͚̿̌͗̉ͤ͢͡ͅͅa̔̾͛̅͊͋͐҉̱̹̬͍͙̻̱l̢͎̟̬̙̼̱̫̮̘̼͔̭̅ͬ͑ͣ̏̾̅̓ͣ̿ͣ̈́̕͢͡ͅͅl̡̥̣͔̭̇̒͛͒͐̄̽͛̋ͥ̌͢͟͡.̷̰̝̮͔̟̦͈̥̬̻̥̬͎͓̻̲̇ͮ̿ͨͦ̽ͫ͟͢͝͠ ̗̱͖͈͌̈ͦ͛ͮ̌͋̽̃͆̀͂ͨͧ̄̔̔ͭ̏͢Z̃̉̿ͮ̃̀͘͏͕̬̯̖͚̗͔Aͣ̑̈̓̈̑̈̀̿̚҉͙͍̦̗̦͙̠̝̩̯ͅͅL̴͖̞̞͙̱̻̥̬̜̦̐̇̉̈̽ͪ̅ͪ̂̔͌͑ͭ͐ͤ̈́̿̉͞ͅG̴̵̲̰̹̖͎͕ͯ̆̓̽͢͠Ŏ̶̡̺̼͙̣̞̩͕̥̟̝͕͔̯̞ͨ͒͊̂̊͂͗̒͆̾͆̌͆̃̎ͣͫ͜͡ͅ!̓̽̎̑̏́̓̓ͣ̀͏̱̩̭̣̹̺̗͜͞͞ T̴̷͚͖̜͈̪͎͔̝̫̦̹͔̻̮͂ͬͬ̌ͣ̿ͤ͌ͥ͑̀̂ͬ̚͘͜͞ô̵͚̤̯̹͖͍̦̼̦̖̞̺͕̳̬͇͕̟̜̅̌̈́̑̏̕͘͝ ͍̼̗̫͈̭̦̱̬͚̱̞͓̜̭̼͇̰̞ͮ͗ͣ́ͪ̔ͪ̍̑̏́̀̽̍̔͘͜͜͝ȋ̐̽ͦ̓̔̅͏̧̢̖̭̝̳̹̯̤͈̫͔͔̠͓͉̠͖̠͜ͅn̷̯̗̗̠̱̥͕͉̥͉̳̫̙̅͗̌̒͂̏͑̎̌̌̊͌͘͘ͅͅv̧̜͕͍͙͍̬͕͍̳͉̠͍̹̮̻̜ͨ̏͒̍ͬ̈́͒̈ͥ͗ͣ̄̃ͤ͊̌͆̓o̸̧̎̓͂̊͢҉͍̼̘͇̱̪̠͎̥̹ķ̈́͗͆ͥ͐͑̆̎́͌ͩͯ̊̓͐ͬ̇̕҉̢͏͚̲̰̗̦e̿̀͐̽ͪ̈ͤͬ҉́͟͏̵̫̲̱̻̰̲̦͇̭̟̺͈̞̫̰̜͕͖ͅ ̡̰͎͓͚͓͉͈̮̻̣̮̟̩̬̮̈̋̊͆ͪ̄ͪ͒ͨͧ̇ͪ̇̑̚t̷̬̟͎̞͈̯͙̹̜ͩ̓ͪ͛͐̐ͤ̾̄̈͒̽̈́̑͒̏h̨̢̳͇͓͉̝ͫ̐̓̆̓ͮ̔̓̈́̇ͫe̟̬̣̗͚̬̾̉͋̽ͯ̌ͯͬ̂ͯͭ̓͛́̚͡ ̨̭̱͉̭͈̈̽̆̂͒͗̀ͥͩ͡h̻̼̱̹̗͖̙̗̲̤͓͇͚͚̻̞̥ͥ͛͌ͧ̚͟i̢̯̹̹̘̳̙ͩ̉ͥ͆̽̇̾̎͗̔̓͂͂́̓̌ͬv̧̡̛̟̜̠͉͖̘̲̻̯͚͍͓̯̻̲̹̥͇̻̿̓͛̊̌ͩͩ́ͩ̍͌̚e̵̾́̈́̏͌͌̊͗̏͋ͦ͘͡͏͚̜͚͎͉͍̱͙̖̹̣̘̥̤̹̟͠-̔̌͐́͒ͦͮ̇ͭ̄̏̊̇̍̕͏̩̥̰͚̟m̨̒ͫͦ̔̔͋҉̱̩̗͇̥̰̩̭͍͚͠į̵̷̻̗͉͕͚̣̼̺͉̦̮̠̆̀̐ͩ͒ͯͩͯ͞ͅn̢̫̤̝̝͚̺͍̱̦͚͂̿ͨ̇ͤ͠d̡ͯ͋̋ͧ̈́̒̈͏̛͏̵̤̬͍̗̞̠̟̞̺̠̥̹̱͉̜͍͎̤ ̷̸̢̰͓̘̯͎̤̫̘͓̙̟̳͇̹̥͈͙̮̩̅̋͌͗̓͊̓ͨͣ͗̓͐̈́ͩ̓ͣrͫ͂͌ͪ̏̐̍̾ͥ̓͗̈͆̈ͥ̀̾̚̚҉̴̶̭͇̗͙̘̯̦̭̮̪͚̥̙̯̠͙̪͡e̵̸̲͉̳̙͖͖̫̘̪͕̳͓̻̙͙ͥ̍͂̽ͨ̓̒̒̏ͬ͗ͧ̑̀͠p̵̸̛̦̣͙̳̳̩̣̼̘͈͂ͪͭͤ̎r̶̩̟̞̙͔̼ͫ̆ͦ̐̀̏̾̉̍ͬ̅ͧ͊ͪ̒̈́ͬ̃͞ẻ̴̼͙͍͎̠̀̅̔̃̒͐ͦ̏̆̅̓͋͢ͅš̆̈̆̋ͨ̅̍̇͂̒ͩͨ̂̐̓ͩ͏̸͔͔̯͇͚̤̪̬̗͈̰̦̯͚̕ę̢̱̠͙̲͉̗͚̮̪͖̙̞̦͉͕̗̳͙ͦ̆̋͌ͣ̅̊́ͅņ̴̷̫̪̦͇̺̹͉̗̬̞̲̭̜̪͒̏͂̂̎͊́̋͒̏̅̋̚͘t̷̶̨̟̦̗̦̱͌͌ͩ̀i̴̴̢̖͓͙̘͇̠̦̙̭̼͖̹̾̒̎̐ͥͭ͋ͥ̅͟ͅņ̫͙̹̦̳͈͙̬̫̮͕̰̩̣̘̘͐̀̓ͭͩͬͯ̎͛̿ͫ̊̔̅́̕͠gͥͩ̂͌̒̊̕͏̻͙͖̣͙͍̹͕̝͖̼̙̘͝ ͤ͐̓̒̓͋̐̃̇͊̓ͦ͐̚͢҉̢̨̟̠͉̳͖̲̩͙̕ć̷̡̫̩̞̯̼̝̼͖̤̳̻̘̪̤͈̦̭ͣ́͂͐̽͆̔̀̚͜h̶̢̹̹̙͔̱̓ͦ͌̋̎ͭ͒͋̒ͭ̌̃͌̿ͣ̆̅͑ą̙̳̬̞̬͚̜̤̱̙͇̠̟̈ͤ͋̃̀̓̓ͯ̍̀̽ͣ̐̈̿̌̕ǫ͋͂͐ͬ̿ͯ̂̈́͌̓̌ͧ̕͏̜͔̗͚͔̘̣͕̘̲͖̼͇͖̗̳ͅͅs̷̸̝̙̭̦̣̦̯̭̦͙̹̻͍͇̣̼͗̌͆ͨͭ̃ͮ͐̿̕.̮̝̠̱̺͖͓̼̦̱̉͂͛̓̑̔̓ͮ̈̊̔͗́͝ ̛̣̺̻̼̙̼͓̱̬͕̩͕̲̳̭̗̍ͤ͋̒̆̄ͨ̿ͧ̓͠ͅI̷̻̤̳̲͔͈̖̬̰͔̪͇͇̟̋ͨ̋̍̉̔͝͞͝ͅn̶͕̭͖̠̣͚̹̪͆ͪ̇̂̅̾ͫ́̅̉ͭ̀͜v̖͉̩͕̣͔̭͕̩̲̖̇̀ͬ́̄͒̆͑͆ͪͤ͆̾̍ͯ̚͜ǫ̡̡̫͎̟̞̰̞̹͇̲̏ͨ̄͊̊̇͒̽͢ķ̶̪̙̰̥͙̞̹̭̺͍͕̙̲̮͊ͭ́͋͛͋̑̒͊̏̒̅͛̄̓͟i̴͎̹̞̥͖̒̄ͮ̒̾ͮͧ̀̚͡n̸̵͓̲̟̞̳͚̼̣͙͖̈ͦ͒̿̅̒̿͛͊̇ͧ̉g̡̧̪̩͚͙͓̪͓͚͉̥̪͍̙̻͖͇͗̑͊͑̾̍͊̀ͅ ̷̵̠͚̘̟͓̫̣̲͎̩̹̣̼̟͊́̏ͫ̆ͩ̓͋͆̿̽̓͘̕t̴̢̝̻̖̲̬̜̺̖̻ͩ̿ͫ͗̈́̔͑̐ͮͦ̽̉̓̚͜h̷̛̲͇̫͈̣̭͂ͭ̂͋ͭ̋̔ͮ̆ͩ͞ë̩͕͉̯͇͔͚̭̼̮̣͓̯́ͭ̀ͣ͗̋̉ͨͬ̒ͥͩ͆̓̓́̀̚͘͝ ̛̫̠̗̥̳͇͉̟̮̪̻̤̪͚̟̜̔̌͌̈͌ͪ̋̎̄ͯ͐ͦ́͞͠fͦ̂̈ͬ̇̅̓̓ͫͣ̉̂̉̚͘͡͡͏̼̖̟͚̙̳͔͎̲̫̦̯͔̣̼̹ě̷̶̫͎̞̺̪̪͇͈̞̳̏̋̋͋̾̓̽̓̑ͮ͊ͣ̋̃̅̀͡e͇̗͎̱͔̦̠̰̩̩͖͙̠̻̝ͯ̿̔̀͋͑ͧ͊̆̇̿ͤ̄ͯ̇̀͢͠ͅl̂̿ͯ͛̊̒̓̈́͏̵̪̦̞̤̫̤͇̙̗͕͎̪͕̙̻̳̗̕͟͢i̞̣̙͎͈̗̮͉̱̜̱̝̞̤͋ͯ͋͐̈́ͫ̉̊̏̀ͯͨ͢͟͝n̳̻̼̥̖͍̭̅͂̓̔̔ͦ̔́ͦ͊̀͛̈́ͬͦ͢͡͡ģ̶̡̳̰̻̙̞̱̳̣̤̫̫͕̤̮̰̬̪̜͋͒̎̈́̉̏̀ͬͯ͌̇͊̚ ́̽ͤͦ̾̔͢҉̛̤͍͉̺̙̮̗̜̟̀͝ơ̢̱͓͓̙͉̖̠̯̦̗͍̓̐̃̉̅̃ͨ͆́ͪ̂̒̀̊̃͆̔͡͡ͅf́ͬ̊ͯͫ̉̈́̽̉̚͢͏̡̺̬̖͇̫͉̱ ̴͇̦̗̙̼̬͓̯͖̮͓͎̗͈̻̈́͆ͭ̐ͦ́͛̀͋̐̌ͬ͑̒̊̿̃͞c̶̸̣͔̬͕̪̱̩̣̑̒̑̓̍̓͂̍̔͌̚͘͜͞h̶͈̱͇͉̳͍͍̰͈͖̬̥͚̯͓̞̹̋̔ͯ̑̃́̒̎̎͊̈́̍̚̕ạ̴̞̱̥͍͙̺͉͚͎̫̦͎̥ͩ̀̀̊ͥ͢o̵̧͕̜͓͈̬̰̫̮͙̹͉̩̝̩͎̓̆͗̿̊̀ͯ̃ͪ̊ͫ̽̉̓ͧ͗́̚͢ͅͅs̡ͫ͋̑ͮ̍̃͊̄ͬ̅̈́ͬ̍̇̔̈̅̍̀҉̜͓̝̘̘̮̼͖͎̻͓͖̖͙̞ͅ.͗ͬͭͩ̌̅͗͏̷̮̗͇͔͇͈̮͢ ̨͚̲̫̠̼͖̝̻̉ͤ̅̂ͩ̀̇ͬͭ̀͜Ẅ̢́̉͌ͮͬͨ͊̏͌̇̐͊͟͠҉̼̰̦̩͇͕̟̭̪̲͕̥͖̰̪͈̀ͅͅį̷ͣͦ̉̍ͨ͂͂͑̃͂ͪ̊̈̋̄͜҉̨͚̟̲̯̹̺̝̭̺̙͖͍t̼͓̰̩͙̦͓̟͚͖̀ͯ͛̍̈́͑͂̍̋́h̛̼̺̘̥̠̼̼̭͙̮͚̱̍ͯ̓̃̐̂̇͟ ̴̛͖͔̰̠̺̥̲ͮ̍ͫ̽͜õ̒ͯ̒̓ͦ̈́͑̔̒̓̎ͤ͑҉̸̭̱̤̭̬͈ų̙̫̤͖̺̫̱͓͓̗̪͇̩̙̔̉̊͂ͪ̇͢͟͞ͅt̸̬̣̫̞̫̅͐ͮ̌͌̈́̀̀͘ ̷̴̨̖̙̹͚ͬ̈́̈ͯͨͮ̇̈́̋̈́ͭ͛̑̉͊̕ö̡̍ͥ̂ͬͪͧ͒ͧ̏̓̇̂̄͆̌ͫͤ͢͠͝͏̖̱̯̘͙̰̖͎̰͓̟̤ṙ̡̬̟̬̜̪̮̺͖̗̘͈̟ͨ͐͗̑͒̐d̢ͭͫ̊̏ͬͥ͋́̌̈́ͮ̆ͬ̐̌̎͏̵̷̡̞̲̹̙͕̮̮͚ḙ̴̸̠͔͎̥͇͖͕̘̍̓̏̐ͩͩ̈́ͦ̐̋ͤ̎̾̌̏͊̊́̚͞ͅr̸͈̗̣̲̗̣̬̤ͦ̎ͫ̏̀ͥͪ̋ͧ̄͑̋͒͌͋ͦ̉͟͞.ͨͣ̽̈́͒̄ͮ̀͋͋͏̴̧̯̺̙̱̻͙̜ ̡̣̞̠͓̰͍̠͕̭̺̼͊̽̿͊ͮ̐̓̒̊͒̔̓͐ͨ̈̌́T̸̸̓́̋ͬ́͆ͨͫ͌͂ͣ̋͒҉̺̝͎̟͖͚̠h̸̡̰̜̦͇͕̪̝̳͕͉̲̝̑ͥ͋ͧ̎̆͌͟e̛̹͍͍̫̙̞̪̭̙̟͙̱̺̮̳͕̜ͫ̓ͭ͊ͫ͆̀̚͟͡ ̿͂̄ͧ̔̎ͧ͑̾̀̓͏̦͍̳͈̳͔̘̖̲̯̰̟̝̳̖̦N̶̡̧̦̮̟̦̩̰̣̝̆̀͊̔͢e͛̄ͮͦͨ͂̔̓̍̄̉͆͊̑̑̆̚͏̜̗͎̝̼̯̥̜͖͍̪̝͞ͅͅz̨̛̀̾ͪ͗̉́͠͏͚̫̼̫̜̣pͪͦ͌̄ͥ̆ͣͩ͋̉́̏͞͏̥̜̝̳̱̞̙̳̤͙̟̟̮̦ȅ̷̩̟͉̯͕͔̘̺̥̻̻ͧ̊̅̽ͣ͑̓̑̽ͦ̾͌͜r̴̭̥̲̲̤͚͈̰͇̰͈̰̹ͫ̒ͯ̿͒ͧ̊͆͒ͣ́ḍ̭̟̤̈́̌̓̈́ͫ͐̍͂͞į̛̞̝̮̣͙͙̤̇̂̓̎͋̿̓̎̄̈́ͧ̓ͩ̐̓̄̋ͭ͞͠a͋̔̋ͫ̂͐͂҉̸̛̥̩̯̯̤̝͔̠̝̯̪̥̩̻̼̮n͌ͣ̂͋̿̚҉̛̙̲̺̯͇͓̝̯̪̟͔̩͟ͅ ̢̨͚̻̗̘͖̯̐ͥ͋̽ͯ̎̈́͋̏̄͋̆̑̊̆̚̕͟ͅh̢̛̗̱̭͇͖̰̮̮͈̲͍̯̟ͭ͊̎̽̓ͦͤ͠ï̛̘̝̦͎̦̭̠͖̳͎̮̼̏͐ͧ̒̒͐͑ͪͫ̋̽̚̚͜v̴̮͕̝̮̞͐̄͗̋͒ͤ̎̈̑ͬͮ̄̾ͤ̓̾͊͗͟é̶̷̡̩͖̰̫͓̟ͮͬͣ͊-ͦ͛ͩͤͨͨ̆̄͏̼̜̭͔̳͈͖̳̩͢ͅͅm̷̴̓́̓͛͒̾̍̉҉̛̗̹̠̣̪̺͎̖̝͚̖͙i̛̥͓̬̫͉͕͉͆͒ͧ̂̿̔̔͆̆̓̍͊̀͜n͌ͧͣ̅̌̎ͦͦ͑̑ͭ̆ͬ̀ͤ̀ͣ̚҉͎̰̱͚͈͈̬̹͕̺̙͙̼͘͘͞d̶͖̫̟̲͕̺̠͎̘͕̱̼͙̪̪̩͙̅̅̑̓̇͑̊̉͜͞ ̶̵̷̴̡̠͚̪͕̣̱̖̱̗̤̭̭͔͖͚ͧͤͥ͒̌ͪ͊͂͒̓͂ͧͧ̇̇͐̑̔ͅͅơ̵̲̲͇̯̰͇̜̣͕͕͓̲̤̲͔͚̞͑͗ͤ̓́̚͠ͅf̢̧̛̩̯̼̫͖̾ͣ͌̾̉́̈́̑̈́̚͞͞ͅ ͤͩ́͋͒ͫͬͣ̋̅̆҉̧̱̻͓͕͉̹̫̫̞̯̪̙̩͍̦͔̖̮̀͟ͅc͉̠̜̩̟͕͎̙̣̮̘̼͋ͯ̍ͨ̅̄ͫ̈̋ͫ̊͡͝ȟ̸̨ͯͦ̂̉̇̾̆ͭ̋̐̈̆̀̚͜҉͚͕̻̖a̶̴̛͚̗͙̳̬̲͚ͦ́̐ͥ́̔̅̑̎͐̑ͯ̾ͤͥͧ͡ò̶̧̞̪̦̥̪̻̦̝̳̬̔͛͛ͣ̋̌̔ͫ̂̽ͫ͘͠s̸̖̣̬̤̫͇̫̣̑͆͒̎̏́͟.̴̗̤̭͉̯̻̤͕̌ͯ̍ͤ̓͌ͤ̈̆̉ͦ̇́̚͘͟͝ͅ ̯̹̪͓̬͌̔̌ͬ̀͘͢͡͡Z̡̩̲̩̰̫̩̟͍̰͖͔̭ͣ̆̾ͭ̀́͞ͅa̡̡̙̜̭͇͎͔̙̞̫͓̜͉͔̬ͭ̈ͨ̉͆ͣͫ̃͌̓͌́ͣͥ̒̌͊͘͝l̢̨̡̯̙̫͖̫̺̘̬̟͈͌̊ͧͫͦ̉̃ͩͦ̒ͯ̇̌̓͛͟͝ͅg̵̙̼̼ͪ͂ͭ͗̈̕ȯ̅ͧ̓ͪ́̂͑̐ͩͥͬ̊̑͆̇͒ͫͣ͝҉͎̟̜̥͎̮̣͉̖̟̯̦̖͙͙͞ͅ.̈̑ͩ̇̂ͬ̓ͬ͊͂ͨ̽͠͏̺͎̞̦̜͍͚̯̯͔̝̞̻̩̖ ̷̰̪͍͎͔͒ͯͥ̾̉͆ͤ̊̓̂͋̀͆H̸̸̹̞̙̺͎̠̯̤ͨ̉̍ͬͤ̓̐͌ͥͮ͞eͣ̈̾͛́͏͕̗͍̜̼͎͚̟̬̣̝̕ͅͅ ̴̛̩̗̼̝̣̩͚͇̯́̉͋̂̍͂̌ͮ͋̾͜͠wͮ̽̓ͭ̿͐̽̐̽͆̓͝҉̡̼̲͖̪̥h̢̢̛͍̰̰̻̱̼̰̹̖̖̪̝̥̘̎̀ͪ͒̾ͫͬ̆̑o̡̗̠̞̱̥͎̰͎͍̫̻͓͇͓͐ͥͯ͂̅͠ͅ ̡̛̏͑ͦ̓͊ͮͫͯͭ̌͒̆̍̈͠҉͖͚̪̫̗̮W̴̐̊͋̾ͥͫ҉͎̞͔̯̫̹͖̰͉̹̼͎̰̱͓̻̀a̶̩̤̙̣͎̳̭̲̗̠͉̳̭̭̦̞͎̮̅͌̾͗̾͛̇̀́͟͞ͅi̷̡ͣ̆̌͋͒͒́͘͏̮̺̩͎͇̜͍̫ṯ̴̢͖̥̖͇͎̦̦̹̖͇̪ͭ̅̍͐̇͒͋̽̏̿̒͆ͧ̄͋ͧͩ͒͜s̙̥̖̘̖͚̭̤̮̖̘̰̫̟̈́ͣ̍ͧ͐ͥ̏͆̃̿͒̔͐́̚͟ͅ ̨ͭ̌ͬͯ͆̒͋ͭ̔̿ͧ̅̓ͣ͡͏͇̟͉̥͔̬̼͚͙͚B̛̜̮̤͓̝̪̪͈͕̘̜͙̰̮̫̘̣͓͔̅ͩ͊̔ͦͯ́̌́͆ͭ̓́e̶̢̡̦͇͕̙͈͖͕̦̬̫͕̣̺̒̿͂͐͒͋͂ͦ́͋ͤ̿ͬ̊ͣ͗̑̽͜ͅͅh̸͑ͫͧ̑ͬͧ̈́̎̃ͣ̊̾͂ͨͤ̓͐̐̑͏̸̭͓̘͉̩i̧̧̭̣͈̝̺̼̺̠͉̞̜̲̳͙̦͐̔ͯ͛̅̾n̸͓̝̤̙͙͔ͪ̋̈́͒̒ͭ̈́̓ͮ̋̀̋̀̈ͩ́͌̄͘d̷̫̳̩̼̥̗̲̰͇͉̼̬̤͇̖ͮ̿ͬ͂ͦ̏̓ͮ̽͂̾̾ͯ͆͜͠ ̨̈́͒̇̏̄̑̓ͮͥ̒ͤͨ̋҉̴̴̟̱͙̟̫̩̗͔̝͔̀Ţ̵̝̟̖̭͇̻̳͖͉̺̖̖͙͙̺̐̈́̓ͯ̆̇̋ͩ͊̄̾̾ͬ̌̚͟ͅh̡͈̗̜͙̬̗̲̦̲̟̗̦̬͓̳ͧ̋̌͂͂ͨͬͦ̿̏̈́̋ͣ̒̕͡ͅͅe̗͇̰̰̥̪̗͑̔̓́̈́ͨ̊́̿̅ͯͥ̈́͐͗͘͢͝ ̡̢̛̯͎͓̰̘͎̦̪̯̪̥̰̲͇̠̲͔ͤͤ̇̅̆̋̂̆̈́ͤ̿͑ͅW̡͓͈̲̲͉̜͔̖͈̻̱͚̿̌͗̉ͤ͢͡ͅͅa̔̾͛̅͊͋͐҉̱̹̬͍͙̻̱l̢͎̟̬̙̼̱̫̮̘̼͔̭̅ͬ͑ͣ̏̾̅̓ͣ̿ͣ̈́̕͢͡ͅͅl̡̥̣͔̭̇̒͛͒͐̄̽͛̋ͥ̌͢͟͡.̷̰̝̮͔̟̦͈̥̬̻̥̬͎͓̻̲̇ͮ̿ͨͦ̽ͫ͟͢͝͠ ̗̱͖͈͌̈ͦ͛ͮ̌͋̽̃͆̀͂ͨͧ̄̔̔ͭ̏͢Z̃̉̿ͮ̃̀͘͏͕̬̯̖͚̗͔Aͣ̑̈̓̈̑̈̀̿̚҉͙͍̦̗̦͙̠̝̩̯ͅͅL̴͖̞̞͙̱̻̥̬̜̦̐̇̉̈̽ͪ̅ͪ̂̔͌͑ͭ͐ͤ̈́̿̉͞ͅG̴̵̲̰̹̖͎͕ͯ̆̓̽͢͠Ŏ̶̡̺̼͙̣̞̩͕̥̟̝͕͔̯̞ͨ͒͊̂̊͂͗̒͆̾͆̌͆̃̎ͣͫ͜͡ͅ!̓̽̎̑̏́̓̓ͣ̀͏̱̩̭̣̹̺̗͜͞͞ T̴̷͚͖̜͈̪͎͔̝̫̦̹͔̻̮͂ͬͬ̌ͣ̿ͤ͌ͥ͑̀̂ͬ̚͘͜͞ô̵͚̤̯̹͖͍̦̼̦̖̞̺͕̳̬͇͕̟̜̅̌̈́̑̏̕͘͝ ͍̼̗̫͈̭̦̱̬͚̱̞͓̜̭̼͇̰̞ͮ͗ͣ́ͪ̔ͪ̍̑̏́̀̽̍̔͘͜͜͝ȋ̐̽ͦ̓̔̅͏̧̢̖̭̝̳̹̯̤͈̫͔͔̠͓͉̠͖̠͜ͅn̷̯̗̗̠̱̥͕͉̥͉̳̫̙̅͗̌̒͂̏͑̎̌̌̊͌͘͘ͅͅv̧̜͕͍͙͍̬͕͍̳͉̠͍̹̮̻̜ͨ̏͒̍ͬ̈́͒̈ͥ͗ͣ̄̃ͤ͊̌͆̓o̸̧̎̓͂̊͢҉͍̼̘͇̱̪̠͎̥̹ķ̈́͗͆ͥ͐͑̆̎́͌ͩͯ̊̓͐ͬ̇̕҉̢͏͚̲̰̗̦e̿̀͐̽ͪ̈ͤͬ҉́͟͏̵̫̲̱̻̰̲̦͇̭̟̺͈̞̫̰̜͕͖ͅ ̡̰͎͓͚͓͉͈̮̻̣̮̟̩̬̮̈̋̊͆ͪ̄ͪ͒ͨͧ̇ͪ̇̑̚t̷̬̟͎̞͈̯͙̹̜ͩ̓ͪ͛͐̐ͤ̾̄̈͒̽̈́̑͒̏h̨̢̳͇͓͉̝ͫ̐̓̆̓ͮ̔̓̈́̇ͫe̟̬̣̗͚̬̾̉͋̽ͯ̌ͯͬ̂ͯͭ̓͛́̚͡ ̨̭̱͉̭͈̈̽̆̂͒͗̀ͥͩ͡h̻̼̱̹̗͖̙̗̲̤͓͇͚͚̻̞̥ͥ͛͌ͧ̚͟i̢̯̹̹̘̳̙ͩ̉ͥ͆̽̇̾̎͗̔̓͂͂́̓̌ͬv̧̡̛̟̜̠͉͖̘̲̻̯͚͍͓̯̻̲̹̥͇̻̿̓͛̊̌ͩͩ́ͩ̍͌̚e̵̾́̈́̏͌͌̊͗̏͋ͦ͘͡͏͚̜͚͎͉͍̱͙̖̹̣̘̥̤̹̟͠-̔̌͐́͒ͦͮ̇ͭ̄̏̊̇̍̕͏̩̥̰͚̟m̨̒ͫͦ̔̔͋҉̱̩̗͇̥̰̩̭͍͚͠į̵̷̻̗͉͕͚̣̼̺͉̦̮̠̆̀̐ͩ͒ͯͩͯ͞ͅn̢̫̤̝̝͚̺͍̱̦͚͂̿ͨ̇ͤ͠d̡ͯ͋̋ͧ̈́̒̈͏̛͏̵̤̬͍̗̞̠̟̞̺̠̥̹̱͉̜͍͎̤ ̷̸̢̰͓̘̯͎̤̫̘͓̙̟̳͇̹̥͈͙̮̩̅̋͌͗̓͊̓ͨͣ͗̓͐̈́ͩ̓ͣrͫ͂͌ͪ̏̐̍̾ͥ̓͗̈͆̈ͥ̀̾̚̚҉̴̶̭͇̗͙̘̯̦̭̮̪͚̥̙̯̠͙̪͡e̵̸̲͉̳̙͖͖̫̘̪͕̳͓̻̙͙ͥ̍͂̽ͨ̓̒̒̏ͬ͗ͧ̑̀͠p̵̸̛̦̣͙̳̳̩̣̼̘͈͂ͪͭͤ̎r̶̩̟̞̙͔̼ͫ̆ͦ̐̀̏̾̉̍ͬ̅ͧ͊ͪ̒̈́ͬ̃͞ẻ̴̼͙͍͎̠̀̅̔̃̒͐ͦ̏̆̅̓͋͢ͅš̆̈̆̋ͨ̅̍̇͂̒ͩͨ̂̐̓ͩ͏̸͔͔̯͇͚̤̪̬̗͈̰̦̯͚̕ę̢̱̠͙̲͉̗͚̮̪͖̙̞̦͉͕̗̳͙ͦ̆̋͌ͣ̅̊́ͅņ̴̷̫̪̦͇̺̹͉̗̬̞̲̭̜̪͒̏͂̂̎͊́̋͒̏̅̋̚͘t̷̶̨̟̦̗̦̱͌͌ͩ̀i̴̴̢̖͓͙̘͇̠̦̙̭̼͖̹̾̒̎̐ͥͭ͋ͥ̅͟ͅņ̫͙̹̦̳͈͙̬̫̮͕̰̩̣̘̘͐̀̓ͭͩͬͯ̎͛̿ͫ̊̔̅́̕͠gͥͩ̂͌̒̊̕͏̻͙͖̣͙͍̹͕̝͖̼̙̘͝ ͤ͐̓̒̓͋̐̃̇͊̓ͦ͐̚͢҉̢̨̟̠͉̳͖̲̩͙̕ć̷̡̫̩̞̯̼̝̼͖̤̳̻̘̪̤͈̦̭ͣ́͂͐̽͆̔̀̚͜h̶̢̹̹̙͔̱̓ͦ͌̋̎ͭ͒͋̒ͭ̌̃͌̿ͣ̆̅͑ą̙̳̬̞̬͚̜̤̱̙͇̠̟̈ͤ͋̃̀̓̓ͯ̍̀̽ͣ̐̈̿̌̕ǫ͋͂͐ͬ̿ͯ̂̈́͌̓̌ͧ̕͏̜͔̗͚͔̘̣͕̘̲͖̼͇͖̗̳ͅͅs̷̸̝̙̭̦̣̦̯̭̦͙̹̻͍͇̣̼͗̌͆ͨͭ̃ͮ͐̿̕.̮̝̠̱̺͖͓̼̦̱̉͂͛̓̑̔̓ͮ̈̊̔͗́͝ ̛̣̺̻̼̙̼͓̱̬͕̩͕̲̳̭̗̍ͤ͋̒̆̄ͨ̿ͧ̓͠ͅI̷̻̤̳̲͔͈̖̬̰͔̪͇͇̟̋ͨ̋̍̉̔͝͞͝ͅn̶͕̭͖̠̣͚̹̪͆ͪ̇̂̅̾ͫ́̅̉ͭ̀͜v̖͉̩͕̣͔̭͕̩̲̖̇̀ͬ́̄͒̆͑͆ͪͤ͆̾̍ͯ̚͜ǫ̡̡̫͎̟̞̰̞̹͇̲̏ͨ̄͊̊̇͒̽͢ķ̶̪̙̰̥͙̞̹̭̺͍͕̙̲̮͊ͭ́͋͛͋̑̒͊̏̒̅͛̄̓͟i̴͎̹̞̥͖̒̄ͮ̒̾ͮͧ̀̚͡n̸̵͓̲̟̞̳͚̼̣͙͖̈ͦ͒̿̅̒̿͛͊̇ͧ̉g̡̧̪̩͚͙͓̪͓͚͉̥̪͍̙̻͖͇͗̑͊͑̾̍͊̀ͅ ̷̵̠͚̘̟͓̫̣̲͎̩̹̣̼̟͊́̏ͫ̆ͩ̓͋͆̿̽̓͘̕t̴̢̝̻̖̲̬̜̺̖̻ͩ̿ͫ͗̈́̔͑̐ͮͦ̽̉̓̚͜h̷̛̲͇̫͈̣̭͂ͭ̂͋ͭ̋̔ͮ̆ͩ͞ë̩͕͉̯͇͔͚̭̼̮̣͓̯́ͭ̀ͣ͗̋̉ͨͬ̒ͥͩ͆̓̓́̀̚͘͝ ̛̫̠̗̥̳͇͉̟̮̪̻̤̪͚̟̜̔̌͌̈͌ͪ̋̎̄ͯ͐ͦ́͞͠fͦ̂̈ͬ̇̅̓̓ͫͣ̉̂̉̚͘͡͡͏̼̖̟͚̙̳͔͎̲̫̦̯͔̣̼̹ě̷̶̫͎̞̺̪̪͇͈̞̳̏̋̋͋̾̓̽̓̑ͮ͊ͣ̋̃̅̀͡e͇̗͎̱͔̦̠̰̩̩͖͙̠̻̝ͯ̿̔̀͋͑ͧ͊̆̇̿ͤ̄ͯ̇̀͢͠ͅl̂̿ͯ͛̊̒̓̈́͏̵̪̦̞̤̫̤͇̙̗͕͎̪͕̙̻̳̗̕͟͢i̞̣̙͎͈̗̮͉̱̜̱̝̞̤͋ͯ͋͐̈́ͫ̉̊̏̀ͯͨ͢͟͝n̳̻̼̥̖͍̭̅͂̓̔̔ͦ̔́ͦ͊̀͛̈́ͬͦ͢͡͡ģ̶̡̳̰̻̙̞̱̳̣̤̫̫͕̤̮̰̬̪̜͋͒̎̈́̉̏̀ͬͯ͌̇͊̚ ́̽ͤͦ̾̔͢҉̛̤͍͉̺̙̮̗̜̟̀͝ơ̢̱͓͓̙͉̖̠̯̦̗͍̓̐̃̉̅̃ͨ͆́ͪ̂̒̀̊̃͆̔͡͡ͅf́ͬ̊ͯͫ̉̈́̽̉̚͢͏̡̺̬̖͇̫͉̱ ̴͇̦̗̙̼̬͓̯͖̮͓͎̗͈̻̈́͆ͭ̐ͦ́͛̀͋̐̌ͬ͑̒̊̿̃͞c̶̸̣͔̬͕̪̱̩̣̑̒̑̓̍̓͂̍̔͌̚͘͜͞h̶͈̱͇͉̳͍͍̰͈͖̬̥͚̯͓̞̹̋̔ͯ̑̃́̒̎̎͊̈́̍̚̕ạ̴̞̱̥͍͙̺͉͚͎̫̦͎̥ͩ̀̀̊ͥ͢o̵̧͕̜͓͈̬̰̫̮͙̹͉̩̝̩͎̓̆͗̿̊̀ͯ̃ͪ̊ͫ̽̉̓ͧ͗́̚͢ͅͅs̡ͫ͋̑ͮ̍̃͊̄ͬ̅̈́ͬ̍̇̔̈̅̍̀҉̜͓̝̘̘̮̼͖͎̻͓͖̖͙̞ͅ.͗ͬͭͩ̌̅͗͏̷̮̗͇͔͇͈̮͢ ̨͚̲̫̠̼͖̝̻̉ͤ̅̂ͩ̀̇ͬͭ̀͜Ẅ̢́̉͌ͮͬͨ͊̏͌̇̐͊͟͠҉̼̰̦̩͇͕̟̭̪̲͕̥͖̰̪͈̀ͅͅį̷ͣͦ̉̍ͨ͂͂͑̃͂ͪ̊̈̋̄͜҉̨͚̟̲̯̹̺̝̭̺̙͖͍t̼͓̰̩͙̦͓̟͚͖̀ͯ͛̍̈́͑͂̍̋́h̛̼̺̘̥̠̼̼̭͙̮͚̱̍ͯ̓̃̐̂̇͟ ̴̛͖͔̰̠̺̥̲ͮ̍ͫ̽͜õ̒ͯ̒̓ͦ̈́͑̔̒̓̎ͤ͑҉̸̭̱̤̭̬͈ų̙̫̤͖̺̫̱͓͓̗̪͇̩̙̔̉̊͂ͪ̇͢͟͞ͅt̸̬̣̫̞̫̅͐ͮ̌͌̈́̀̀͘ ̷̴̨̖̙̹͚ͬ̈́̈ͯͨͮ̇̈́̋̈́ͭ͛̑̉͊̕ö̡̍ͥ̂ͬͪͧ͒ͧ̏̓̇̂̄͆̌ͫͤ͢͠͝͏̖̱̯̘͙̰̖͎̰͓̟̤ṙ̡̬̟̬̜̪̮̺͖̗̘͈̟ͨ͐͗̑͒̐d̢ͭͫ̊̏ͬͥ͋́̌̈́ͮ̆ͬ̐̌̎͏̵̷̡̞̲̹̙͕̮̮͚ḙ̴̸̠͔͎̥͇͖͕̘̍̓̏̐ͩͩ̈́ͦ̐̋ͤ̎̾̌̏͊̊́̚͞ͅr̸͈̗̣̲̗̣̬̤ͦ̎ͫ̏̀ͥͪ̋ͧ̄͑̋͒͌͋ͦ̉͟͞.ͨͣ̽̈́͒̄ͮ̀͋͋͏̴̧̯̺̙̱̻͙̜ ̡̣̞̠͓̰͍̠͕̭̺̼͊̽̿͊ͮ̐̓̒̊͒̔̓͐ͨ̈̌́T̸̸̓́̋ͬ́͆ͨͫ͌͂ͣ̋͒҉̺̝͎̟͖͚̠h̸̡̰̜̦͇͕̪̝̳͕͉̲̝̑ͥ͋ͧ̎̆͌͟e̛̹͍͍̫̙̞̪̭̙̟͙̱̺̮̳͕̜ͫ̓ͭ͊ͫ͆̀̚͟͡ ̿͂̄ͧ̔̎ͧ͑̾̀̓͏̦͍̳͈̳͔̘̖̲̯̰̟̝̳̖̦N̶̡̧̦̮̟̦̩̰̣̝̆̀͊̔͢e͛̄ͮͦͨ͂̔̓̍̄̉͆͊̑̑̆̚͏̜̗͎̝̼̯̥̜͖͍̪̝͞ͅͅz̨̛̀̾ͪ͗̉́͠͏͚̫̼̫̜̣pͪͦ͌̄ͥ̆ͣͩ͋̉́̏͞͏̥̜̝̳̱̞̙̳̤͙̟̟̮̦ȅ̷̩̟͉̯͕͔̘̺̥̻̻ͧ̊̅̽ͣ͑̓̑̽ͦ̾͌͜r̴̭̥̲̲̤͚͈̰͇̰͈̰̹ͫ̒ͯ̿͒ͧ̊͆͒ͣ́ḍ̭̟̤̈́̌̓̈́ͫ͐̍͂͞į̛̞̝̮̣͙͙̤̇̂̓̎͋̿̓̎̄̈́ͧ̓ͩ̐̓̄̋ͭ͞͠a͋̔̋ͫ̂͐͂҉̸̛̥̩̯̯̤̝͔̠̝̯̪̥̩̻̼̮n͌ͣ̂͋̿̚҉̛̙̲̺̯͇͓̝̯̪̟͔̩͟ͅ ̢̨͚̻̗̘͖̯̐ͥ͋̽ͯ̎̈́͋̏̄͋̆̑̊̆̚̕͟ͅh̢̛̗̱̭͇͖̰̮̮͈̲͍̯̟ͭ͊̎̽̓ͦͤ͠ï̛̘̝̦͎̦̭̠͖̳͎̮̼̏͐ͧ̒̒͐͑ͪͫ̋̽̚̚͜v̴̮͕̝̮̞͐̄͗̋͒ͤ̎̈̑ͬͮ̄̾ͤ̓̾͊͗͟é̶̷̡̩͖̰̫͓̟ͮͬͣ͊-ͦ͛ͩͤͨͨ̆̄͏̼̜̭͔̳͈͖̳̩͢ͅͅm̷̴̓́̓͛͒̾̍̉҉̛̗̹̠̣̪̺͎̖̝͚̖͙i̛̥͓̬̫͉͕͉͆͒ͧ̂̿̔̔͆̆̓̍͊̀͜n͌ͧͣ̅̌̎ͦͦ͑̑ͭ̆ͬ̀ͤ̀ͣ̚҉͎̰̱͚͈͈̬̹͕̺̙͙̼͘͘͞d̶͖̫̟̲͕̺̠͎̘͕̱̼͙̪̪̩͙̅̅̑̓̇͑̊̉͜͞ ̶̵̷̴̡̠͚̪͕̣̱̖̱̗̤̭̭͔͖͚ͧͤͥ͒̌ͪ͊͂͒̓͂ͧͧ̇̇͐̑̔ͅͅơ̵̲̲͇̯̰͇̜̣͕͕͓̲̤̲͔͚̞͑͗ͤ̓́̚͠ͅf̢̧̛̩̯̼̫͖̾ͣ͌̾̉́̈́̑̈́̚͞͞ͅ ͤͩ́͋͒ͫͬͣ̋̅̆҉̧̱̻͓͕͉̹̫̫̞̯̪̙̩͍̦͔̖̮̀͟ͅc͉̠̜̩̟͕͎̙̣̮̘̼͋ͯ̍ͨ̅̄ͫ̈̋ͫ̊͡͝ȟ̸̨ͯͦ̂̉̇̾̆ͭ̋̐̈̆̀̚͜҉͚͕̻̖a̶̴̛͚̗͙̳̬̲͚ͦ́̐ͥ́̔̅̑̎͐̑ͯ̾ͤͥͧ͡ò̶̧̞̪̦̥̪̻̦̝̳̬̔͛͛ͣ̋̌̔ͫ̂̽ͫ͘͠s̸̖̣̬̤̫͇̫̣̑͆͒̎̏́͟.̴̗̤̭͉̯̻̤͕̌ͯ̍ͤ̓͌ͤ̈̆̉ͦ̇́̚͘͟͝ͅ ̯̹̪͓̬͌̔̌ͬ̀͘͢͡͡Z̡̩̲̩̰̫̩̟͍̰͖͔̭ͣ̆̾ͭ̀́͞ͅa̡̡̙̜̭͇͎͔̙̞̫͓̜͉͔̬ͭ̈ͨ̉͆ͣͫ̃͌̓͌́ͣͥ̒̌͊͘͝l̢̨̡̯̙̫͖̫̺̘̬̟͈͌̊ͧͫͦ̉̃ͩͦ̒ͯ̇̌̓͛͟͝ͅg̵̙̼̼ͪ͂ͭ͗̈̕ȯ̅ͧ̓ͪ́̂͑̐ͩͥͬ̊̑͆̇͒ͫͣ͝҉͎̟̜̥͎̮̣͉̖̟̯̦̖͙͙͞ͅ.̈̑ͩ̇̂ͬ̓ͬ͊͂ͨ̽͠͏̺͎̞̦̜͍͚̯̯͔̝̞̻̩̖ ̷̰̪͍͎͔͒ͯͥ̾̉͆ͤ̊̓̂͋̀͆H̸̸̹̞̙̺͎̠̯̤ͨ̉̍ͬͤ̓̐͌ͥͮ͞eͣ̈̾͛́͏͕̗͍̜̼͎͚̟̬̣̝̕ͅͅ ̴̛̩̗̼̝̣̩͚͇̯́̉͋̂̍͂̌ͮ͋̾͜͠wͮ̽̓ͭ̿͐̽̐̽͆̓͝҉̡̼̲͖̪̥h̢̢̛͍̰̰̻̱̼̰̹̖̖̪̝̥̘̎̀ͪ͒̾ͫͬ̆̑o̡̗̠̞̱̥͎̰͎͍̫̻͓͇͓͐ͥͯ͂̅͠ͅ ̡̛̏͑ͦ̓͊ͮͫͯͭ̌͒̆̍̈͠҉͖͚̪̫̗̮W̴̐̊͋̾ͥͫ҉͎̞͔̯̫̹͖̰͉̹̼͎̰̱͓̻̀a̶̩̤̙̣͎̳̭̲̗̠͉̳̭̭̦̞͎̮̅͌̾͗̾͛̇̀́͟͞ͅi̷̡ͣ̆̌͋͒͒́͘͏̮̺̩͎͇̜͍̫ṯ̴̢͖̥̖͇͎̦̦̹̖͇̪ͭ̅̍͐̇͒͋̽̏̿̒͆ͧ̄͋ͧͩ͒͜s̙̥̖̘̖͚̭̤̮̖̘̰̫̟̈́ͣ̍ͧ͐ͥ̏͆̃̿͒̔͐́̚͟ͅ ̨ͭ̌ͬͯ͆̒͋ͭ̔̿ͧ̅̓ͣ͡͏͇̟͉̥͔̬̼͚͙͚B̛̜̮̤͓̝̪̪͈͕̘̜͙̰̮̫̘̣͓͔̅ͩ͊̔ͦͯ́̌́͆ͭ̓́e̶̢̡̦͇͕̙͈͖͕̦̬̫͕̣̺̒̿͂͐͒͋͂ͦ́͋ͤ̿ͬ̊ͣ͗̑̽͜ͅͅh̸͑ͫͧ̑ͬͧ̈́̎̃ͣ̊̾͂ͨͤ̓͐̐̑͏̸̭͓̘͉̩i̧̧̭̣͈̝̺̼̺̠͉̞̜̲̳͙̦͐̔ͯ͛̅̾n̸͓̝̤̙͙͔ͪ̋̈́͒̒ͭ̈́̓ͮ̋̀̋̀̈ͩ́͌̄͘d̷̫̳̩̼̥̗̲̰͇͉̼̬̤͇̖ͮ̿ͬ͂ͦ̏̓ͮ̽͂̾̾ͯ͆͜͠ ̨̈́͒̇̏̄̑̓ͮͥ̒ͤͨ̋҉̴̴̟̱͙̟̫̩̗͔̝͔̀Ţ̵̝̟̖̭͇̻̳͖͉̺̖̖͙͙̺̐̈́̓ͯ̆̇̋ͩ͊̄̾̾ͬ̌̚͟ͅh̡͈̗̜͙̬̗̲̦̲̟̗̦̬͓̳ͧ̋̌͂͂ͨͬͦ̿̏̈́̋ͣ̒̕͡ͅͅe̗͇̰̰̥̪̗͑̔̓́̈́ͨ̊́̿̅ͯͥ̈́͐͗͘͢͝ ̡̢̛̯͎͓̰̘͎̦̪̯̪̥̰̲͇̠̲͔ͤͤ̇̅̆̋̂̆̈́ͤ̿͑ͅW̡͓͈̲̲͉̜͔̖͈̻̱͚̿̌͗̉ͤ͢͡ͅͅa̔̾͛̅͊͋͐҉̱̹̬͍͙̻̱l̢͎̟̬̙̼̱̫̮̘̼͔̭̅ͬ͑ͣ̏̾̅̓ͣ̿ͣ̈́̕͢͡ͅͅl̡̥̣͔̭̇̒͛͒͐̄̽͛̋ͥ̌͢͟͡.̷̰̝̮͔̟̦͈̥̬̻̥̬͎͓̻̲̇ͮ̿ͨͦ̽ͫ͟͢͝͠ ̗̱͖͈͌̈ͦ͛ͮ̌͋̽̃͆̀͂ͨͧ̄̔̔ͭ̏͢Z̃̉̿ͮ̃̀͘͏͕̬̯̖͚̗͔Aͣ̑̈̓̈̑̈̀̿̚҉͙͍̦̗̦͙̠̝̩̯ͅͅL̴͖̞̞͙̱̻̥̬̜̦̐̇̉̈̽ͪ̅ͪ̂̔͌͑ͭ͐ͤ̈́̿̉͞ͅG̴̵̲̰̹̖͎͕ͯ̆̓̽͢͠Ŏ̶̡̺̼͙̣̞̩͕̥̟̝͕͔̯̞ͨ͒͊̂̊͂͗̒͆̾͆̌͆̃̎ͣͫ͜͡ͅ!̓̽̎̑̏́̓̓ͣ̀͏̱̩̭̣̹̺̗͜͞͞ T̴̷͚͖̜͈̪͎͔̝̫̦̹͔̻̮͂ͬͬ̌ͣ̿ͤ͌ͥ͑̀̂ͬ̚͘͜͞ô̵͚̤̯̹͖͍̦̼̦̖̞̺͕̳̬͇͕̟̜̅̌̈́̑̏̕͘͝ ͍̼̗̫͈̭̦̱̬͚̱̞͓̜̭̼͇̰̞ͮ͗ͣ́ͪ̔ͪ̍̑̏́̀̽̍̔͘͜͜͝ȋ̐̽ͦ̓̔̅͏̧̢̖̭̝̳̹̯̤͈̫͔͔̠͓͉̠͖̠͜ͅn̷̯̗̗̠̱̥͕͉̥͉̳̫̙̅͗̌̒͂̏͑̎̌̌̊͌͘͘ͅͅv̧̜͕͍͙͍̬͕͍̳͉̠͍̹̮̻̜ͨ̏͒̍ͬ̈́͒̈ͥ͗ͣ̄̃ͤ͊̌͆̓o̸̧̎̓͂̊͢҉͍̼̘͇̱̪̠͎̥̹ķ̈́͗͆ͥ͐͑̆̎́͌ͩͯ̊̓͐ͬ̇̕҉̢͏͚̲̰̗̦e̿̀͐̽ͪ̈ͤͬ҉́͟͏̵̫̲̱̻̰̲̦͇̭̟̺͈̞̫̰̜͕͖ͅ ̡̰͎͓͚͓͉͈̮̻̣̮̟̩̬̮̈̋̊͆ͪ̄ͪ͒ͨͧ̇ͪ̇̑̚t̷̬̟͎̞͈̯͙̹̜ͩ̓ͪ͛͐̐ͤ̾̄̈͒̽̈́̑͒̏h̨̢̳͇͓͉̝ͫ̐̓̆̓ͮ̔̓̈́̇ͫe̟̬̣̗͚̬̾̉͋̽ͯ̌ͯͬ̂ͯͭ̓͛́̚͡ ̨̭̱͉̭͈̈̽̆̂͒͗̀ͥͩ͡h̻̼̱̹̗͖̙̗̲̤͓͇͚͚̻̞̥ͥ͛͌ͧ̚͟i̢̯̹̹̘̳̙ͩ̉ͥ͆̽̇̾̎͗̔̓͂͂́̓̌ͬv̧̡̛̟̜̠͉͖̘̲̻̯͚͍͓̯̻̲̹̥͇̻̿̓͛̊̌ͩͩ́ͩ̍͌̚e̵̾́̈́̏͌͌̊͗̏͋ͦ͘͡͏͚̜͚͎͉͍̱͙̖̹̣̘̥̤̹̟͠-̔̌͐́͒ͦͮ̇ͭ̄̏̊̇̍̕͏̩̥̰͚̟m̨̒ͫͦ̔̔͋҉̱̩̗͇̥̰̩̭͍͚͠į̵̷̻̗͉͕͚̣̼̺͉̦̮̠̆̀̐ͩ͒ͯͩͯ͞ͅn̢̫̤̝̝͚̺͍̱̦͚͂̿ͨ̇ͤ͠d̡ͯ͋̋ͧ̈́̒̈͏̛͏̵̤̬͍̗̞̠̟̞̺̠̥̹̱͉̜͍͎̤ ̷̸̢̰͓̘̯͎̤̫̘͓̙̟̳͇̹̥͈͙̮̩̅̋͌͗̓͊̓ͨͣ͗̓͐̈́ͩ̓ͣrͫ͂͌ͪ̏̐̍̾ͥ̓͗̈͆̈ͥ̀̾̚̚҉̴̶̭͇̗͙̘̯̦̭̮̪͚̥̙̯̠͙̪͡e̵̸̲͉̳̙͖͖̫̘̪͕̳͓̻̙͙ͥ̍͂̽ͨ̓̒̒̏ͬ͗ͧ̑̀͠p̵̸̛̦̣͙̳̳̩̣̼̘͈͂ͪͭͤ̎r̶̩̟̞̙͔̼ͫ̆ͦ̐̀̏̾̉̍ͬ̅ͧ͊ͪ̒̈́ͬ̃͞ẻ̴̼͙͍͎̠̀̅̔̃̒͐ͦ̏̆̅̓͋͢ͅš̆̈̆̋ͨ̅̍̇͂̒ͩͨ̂̐̓ͩ͏̸͔͔̯͇͚̤̪̬̗͈̰̦̯͚̕ę̢̱̠͙̲͉̗͚̮̪͖̙̞̦͉͕̗̳͙ͦ̆̋͌ͣ̅̊́ͅņ̴̷̫̪̦͇̺̹͉̗̬̞̲̭̜̪͒̏͂̂̎͊́̋͒̏̅̋̚͘t̷̶̨̟̦̗̦̱͌͌ͩ̀i̴̴̢̖͓͙̘͇̠̦̙̭̼͖̹̾̒̎̐ͥͭ͋ͥ̅͟ͅņ̫͙̹̦̳͈͙̬̫̮͕̰̩̣̘̘͐̀̓ͭͩͬͯ̎͛̿ͫ̊̔̅́̕͠gͥͩ̂͌̒̊̕͏̻͙͖̣͙͍̹͕̝͖̼̙̘͝ ͤ͐̓̒̓͋̐̃̇͊̓ͦ͐̚͢҉̢̨̟̠͉̳͖̲̩͙̕ć̷̡̫̩̞̯̼̝̼͖̤̳̻̘̪̤͈̦̭ͣ́͂͐̽͆̔̀̚͜h̶̢̹̹̙͔̱̓ͦ͌̋̎ͭ͒͋̒ͭ̌̃͌̿ͣ̆̅͑ą̙̳̬̞̬͚̜̤̱̙͇̠̟̈ͤ͋̃̀̓̓ͯ̍̀̽ͣ̐̈̿̌̕ǫ͋͂͐ͬ̿ͯ̂̈́͌̓̌ͧ̕͏̜͔̗͚͔̘̣͕̘̲͖̼͇͖̗̳ͅͅs̷̸̝̙̭̦̣̦̯̭̦͙̹̻͍͇̣̼͗̌͆ͨͭ̃ͮ͐̿̕.̮̝̠̱̺͖͓̼̦̱̉͂͛̓̑̔̓ͮ̈̊̔͗́͝ ̛̣̺̻̼̙̼͓̱̬͕̩͕̲̳̭̗̍ͤ͋̒̆̄ͨ̿ͧ̓͠ͅI̷̻̤̳̲͔͈̖̬̰͔̪͇͇̟̋ͨ̋̍̉̔͝͞͝ͅn̶͕̭͖̠̣͚̹̪͆ͪ̇̂̅̾ͫ́̅̉ͭ̀͜v̖͉̩͕̣͔̭͕̩̲̖̇̀ͬ́̄͒̆͑͆ͪͤ͆̾̍ͯ̚͜ǫ̡̡̫͎̟̞̰̞̹͇̲̏ͨ̄͊̊̇͒̽͢ķ̶̪̙̰̥͙̞̹̭̺͍͕̙̲̮͊ͭ́͋͛͋̑̒͊̏̒̅͛̄̓͟i̴͎̹̞̥͖̒̄ͮ̒̾ͮͧ̀̚͡n̸̵͓̲̟̞̳͚̼̣͙͖̈ͦ͒̿̅̒̿͛͊̇ͧ̉g̡̧̪̩͚͙͓̪͓͚͉̥̪͍̙̻͖͇͗̑͊͑̾̍͊̀ͅ ̷̵̠͚̘̟͓̫̣̲͎̩̹̣̼̟͊́̏ͫ̆ͩ̓͋͆̿̽̓͘̕t̴̢̝̻̖̲̬̜̺̖̻ͩ̿ͫ͗̈́̔͑̐ͮͦ̽̉̓̚͜h̷̛̲͇̫͈̣̭͂ͭ̂͋ͭ̋̔ͮ̆ͩ͞ë̩͕͉̯͇͔͚̭̼̮̣͓̯́ͭ̀ͣ͗̋̉ͨͬ̒ͥͩ͆̓̓́̀̚͘͝ ̛̫̠̗̥̳͇͉̟̮̪̻̤̪͚̟̜̔̌͌̈͌ͪ̋̎̄ͯ͐ͦ́͞͠fͦ̂̈ͬ̇̅̓̓ͫͣ̉̂̉̚͘͡͡͏̼̖̟͚̙̳͔͎̲̫̦̯͔̣̼̹ě̷̶̫͎̞̺̪̪͇͈̞̳̏̋̋͋̾̓̽̓̑ͮ͊ͣ̋̃̅̀͡e͇̗͎̱͔̦̠̰̩̩͖͙̠̻̝ͯ̿̔̀͋͑ͧ͊̆̇̿ͤ̄ͯ̇̀͢͠ͅl̂̿ͯ͛̊̒̓̈́͏̵̪̦̞̤̫̤͇̙̗͕͎̪͕̙̻̳̗̕͟͢i̞̣̙͎͈̗̮͉̱̜̱̝̞̤͋ͯ͋͐̈́ͫ̉̊̏̀ͯͨ͢͟͝n̳̻̼̥̖͍̭̅͂̓̔̔ͦ̔́ͦ͊̀͛̈́ͬͦ͢͡͡ģ̶̡̳̰̻̙̞̱̳̣̤̫̫͕̤̮̰̬̪̜͋͒̎̈́̉̏̀ͬͯ͌̇͊̚ ́̽ͤͦ̾̔͢҉̛̤͍͉̺̙̮̗̜̟̀͝ơ̢̱͓͓̙͉̖̠̯̦̗͍̓̐̃̉̅̃ͨ͆́ͪ̂̒̀̊̃͆̔͡͡ͅf́ͬ̊ͯͫ̉̈́̽̉̚͢͏̡̺̬̖͇̫͉̱ ̴͇̦̗̙̼̬͓̯͖̮͓͎̗͈̻̈́͆ͭ̐ͦ́͛̀͋̐̌ͬ͑̒̊̿̃͞c̶̸̣͔̬͕̪̱̩̣̑̒̑̓̍̓͂̍̔͌̚͘͜͞h̶͈̱͇͉̳͍͍̰͈͖̬̥͚̯͓̞̹̋̔ͯ̑̃́̒̎̎͊̈́̍̚̕ạ̴̞̱̥͍͙̺͉͚͎̫̦͎̥ͩ̀̀̊ͥ͢o̵̧͕̜͓͈̬̰̫̮͙̹͉̩̝̩͎̓̆͗̿̊̀ͯ̃ͪ̊ͫ̽̉̓ͧ͗́̚͢ͅͅs̡ͫ͋̑ͮ̍̃͊̄ͬ̅̈́ͬ̍̇̔̈̅̍̀҉̜͓̝̘̘̮̼͖͎̻͓͖̖͙̞ͅ.͗ͬͭͩ̌̅͗͏̷̮̗͇͔͇͈̮͢ ̨͚̲̫̠̼͖̝̻̉ͤ̅̂ͩ̀̇ͬͭ̀͜Ẅ̢́̉͌ͮͬͨ͊̏͌̇̐͊͟͠҉̼̰̦̩͇͕̟̭̪̲͕̥͖̰̪͈̀ͅͅį̷ͣͦ̉̍ͨ͂͂͑̃͂ͪ̊̈̋̄͜҉̨͚̟̲̯̹̺̝̭̺̙͖͍t̼͓̰̩͙̦͓̟͚͖̀ͯ͛̍̈́͑͂̍̋́h̛̼̺̘̥̠̼̼̭͙̮͚̱̍ͯ̓̃̐̂̇͟ ̴̛͖͔̰̠̺̥̲ͮ̍ͫ̽͜õ̒ͯ̒̓ͦ̈́͑̔̒̓̎ͤ͑҉̸̭̱̤̭̬͈ų̙̫̤͖̺̫̱͓͓̗̪͇̩̙̔̉̊͂ͪ̇͢͟͞ͅt̸̬̣̫̞̫̅͐ͮ̌͌̈́̀̀͘ ̷̴̨̖̙̹͚ͬ̈́̈ͯͨͮ̇̈́̋̈́ͭ͛̑̉͊̕ö̡̍ͥ̂ͬͪͧ͒ͧ̏̓̇̂̄͆̌ͫͤ͢͠͝͏̖̱̯̘͙̰̖͎̰͓̟̤ṙ̡̬̟̬̜̪̮̺͖̗̘͈̟ͨ͐͗̑͒̐d̢ͭͫ̊̏ͬͥ͋́̌̈́ͮ̆ͬ̐̌̎͏̵̷̡̞̲̹̙͕̮̮͚ḙ̴̸̠͔͎̥͇͖͕̘̍̓̏̐ͩͩ̈́ͦ̐̋ͤ̎̾̌̏͊̊́̚͞ͅr̸͈̗̣̲̗̣̬̤ͦ̎ͫ̏̀ͥͪ̋ͧ̄͑̋͒͌͋ͦ̉͟͞.ͨͣ̽̈́͒̄ͮ̀͋͋͏̴̧̯̺̙̱̻͙̜ ̡̣̞̠͓̰͍̠͕̭̺̼͊̽̿͊ͮ̐̓̒̊͒̔̓͐ͨ̈̌́T̸̸̓́̋ͬ́͆ͨͫ͌͂ͣ̋͒҉̺̝͎̟͖͚̠h̸̡̰̜̦͇͕̪̝̳͕͉̲̝̑ͥ͋ͧ̎̆͌͟e̛̹͍͍̫̙̞̪̭̙̟͙̱̺̮̳͕̜ͫ̓ͭ͊ͫ͆̀̚͟͡ ̿͂̄ͧ̔̎ͧ͑̾̀̓͏̦͍̳͈̳͔̘̖̲̯̰̟̝̳̖̦N̶̡̧̦̮̟̦̩̰̣̝̆̀͊̔͢e͛̄ͮͦͨ͂̔̓̍̄̉͆͊̑̑̆̚͏̜̗͎̝̼̯̥̜͖͍̪̝͞ͅͅz̨̛̀̾ͪ͗̉́͠͏͚̫̼̫̜̣pͪͦ͌̄ͥ̆ͣͩ͋̉́̏͞͏̥̜̝̳̱̞̙̳̤͙̟̟̮̦ȅ̷̩̟͉̯͕͔̘̺̥̻̻ͧ̊̅̽ͣ͑̓̑̽ͦ̾͌͜r̴̭̥̲̲̤͚͈̰͇̰͈̰̹ͫ̒ͯ̿͒ͧ̊͆͒ͣ́ḍ̭̟̤̈́̌̓̈́ͫ͐̍͂͞į̛̞̝̮̣͙͙̤̇̂̓̎͋̿̓̎̄̈́ͧ̓ͩ̐̓̄̋ͭ͞͠a͋̔̋ͫ̂͐͂҉̸̛̥̩̯̯̤̝͔̠̝̯̪̥̩̻̼̮n͌ͣ̂͋̿̚҉̛̙̲̺̯͇͓̝̯̪̟͔̩͟ͅ ̢̨͚̻̗̘͖̯̐ͥ͋̽ͯ̎̈́͋̏̄͋̆̑̊̆̚̕͟ͅh̢̛̗̱̭͇͖̰̮̮͈̲͍̯̟ͭ͊̎̽̓ͦͤ͠ï̛̘̝̦͎̦̭̠͖̳͎̮̼̏͐ͧ̒̒͐͑ͪͫ̋̽̚̚͜v̴̮͕̝̮̞͐̄͗̋͒ͤ̎̈̑ͬͮ̄̾ͤ̓̾͊͗͟é̶̷̡̩͖̰̫͓̟ͮͬͣ͊-ͦ͛ͩͤͨͨ̆̄͏̼̜̭͔̳͈͖̳̩͢ͅͅm̷̴̓́̓͛͒̾̍̉҉̛̗̹̠̣̪̺͎̖̝͚̖͙i̛̥͓̬̫͉͕͉͆͒ͧ̂̿̔̔͆̆̓̍͊̀͜n͌ͧͣ̅̌̎ͦͦ͑̑ͭ̆ͬ̀ͤ̀ͣ̚҉͎̰̱͚͈͈̬̹͕̺̙͙̼͘͘͞d̶͖̫̟̲͕̺̠͎̘͕̱̼͙̪̪̩͙̅̅̑̓̇͑̊̉͜͞ ̶̵̷̴̡̠͚̪͕̣̱̖̱̗̤̭̭͔͖͚ͧͤͥ͒̌ͪ͊͂͒̓͂ͧͧ̇̇͐̑̔ͅͅơ̵̲̲͇̯̰͇̜̣͕͕͓̲̤̲͔͚̞͑͗ͤ̓́̚͠ͅf̢̧̛̩̯̼̫͖̾ͣ͌̾̉́̈́̑̈́̚͞͞ͅ ͤͩ́͋͒ͫͬͣ̋̅̆҉̧̱̻͓͕͉̹̫̫̞̯̪̙̩͍̦͔̖̮̀͟ͅc͉̠̜̩̟͕͎̙̣̮̘̼͋ͯ̍ͨ̅̄ͫ̈̋ͫ̊͡͝ȟ̸̨ͯͦ̂̉̇̾̆ͭ̋̐̈̆̀̚͜҉͚͕̻̖a̶̴̛͚̗͙̳̬̲͚ͦ́̐ͥ́̔̅̑̎͐̑ͯ̾ͤͥͧ͡ò̶̧̞̪̦̥̪̻̦̝̳̬̔͛͛ͣ̋̌̔ͫ̂̽ͫ͘͠s̸̖̣̬̤̫͇̫̣̑͆͒̎̏́͟.̴̗̤̭͉̯̻̤͕̌ͯ̍ͤ̓͌ͤ̈̆̉ͦ̇́̚͘͟͝ͅ ̯̹̪͓̬͌̔̌ͬ̀͘͢͡͡Z̡̩̲̩̰̫̩̟͍̰͖͔̭ͣ̆̾ͭ̀́͞ͅa̡̡̙̜̭͇͎͔̙̞̫͓̜͉͔̬ͭ̈ͨ̉͆ͣͫ̃͌̓͌́ͣͥ̒̌͊͘͝l̢̨̡̯̙̫͖̫̺̘̬̟͈͌̊ͧͫͦ̉̃ͩͦ̒ͯ̇̌̓͛͟͝ͅg̵̙̼̼ͪ͂ͭ͗̈̕ȯ̅ͧ̓ͪ́̂͑̐ͩͥͬ̊̑͆̇͒ͫͣ͝҉͎̟̜̥͎̮̣͉̖̟̯̦̖͙͙͞ͅ.̈̑ͩ̇̂ͬ̓ͬ͊͂ͨ̽͠͏̺͎̞̦̜͍͚̯̯͔̝̞̻̩̖ ̷̰̪͍͎͔͒ͯͥ̾̉͆ͤ̊̓̂͋̀͆H̸̸̹̞̙̺͎̠̯̤ͨ̉̍ͬͤ̓̐͌ͥͮ͞eͣ̈̾͛́͏͕̗͍̜̼͎͚̟̬̣̝̕ͅͅ ̴̛̩̗̼̝̣̩͚͇̯́̉͋̂̍͂̌ͮ͋̾͜͠wͮ̽̓ͭ̿͐̽̐̽͆̓͝҉̡̼̲͖̪̥h̢̢̛͍̰̰̻̱̼̰̹̖̖̪̝̥̘̎̀ͪ͒̾ͫͬ̆̑o̡̗̠̞̱̥͎̰͎͍̫̻͓͇͓͐ͥͯ͂̅͠ͅ ̡̛̏͑ͦ̓͊ͮͫͯͭ̌͒̆̍̈͠҉͖͚̪̫̗̮W̴̐̊͋̾ͥͫ҉͎̞͔̯̫̹͖̰͉̹̼͎̰̱͓̻̀a̶̩̤̙̣͎̳̭̲̗̠͉̳̭̭̦̞͎̮̅͌̾͗̾͛̇̀́͟͞ͅi̷̡ͣ̆̌͋͒͒́͘͏̮̺̩͎͇̜͍̫ṯ̴̢͖̥̖͇͎̦̦̹̖͇̪ͭ̅̍͐̇͒͋̽̏̿̒͆ͧ̄͋ͧͩ͒͜s̙̥̖̘̖͚̭̤̮̖̘̰̫̟̈́ͣ̍ͧ͐ͥ̏͆̃̿͒̔͐́̚͟ͅ ̨ͭ̌ͬͯ͆̒͋ͭ̔̿ͧ̅̓ͣ͡͏͇̟͉̥͔̬̼͚͙͚B̛̜̮̤͓̝̪̪͈͕̘̜͙̰̮̫̘̣͓͔̅ͩ͊̔ͦͯ́̌́͆ͭ̓́e̶̢̡̦͇͕̙͈͖͕̦̬̫͕̣̺̒̿͂͐͒͋͂ͦ́͋ͤ̿ͬ̊ͣ͗̑̽͜ͅͅh̸͑ͫͧ̑ͬͧ̈́̎̃ͣ̊̾͂ͨͤ̓͐̐̑͏̸̭͓̘͉̩i̧̧̭̣͈̝̺̼̺̠͉̞̜̲̳͙̦͐̔ͯ͛̅̾n̸͓̝̤̙͙͔ͪ̋̈́͒̒ͭ̈́̓ͮ̋̀̋̀̈ͩ́͌̄͘d̷̫̳̩̼̥̗̲̰͇͉̼̬̤͇̖ͮ̿ͬ͂ͦ̏̓ͮ̽͂̾̾ͯ͆͜͠ ̨̈́͒̇̏̄̑̓ͮͥ̒ͤͨ̋҉̴̴̟̱͙̟̫̩̗͔̝͔̀Ţ̵̝̟̖̭͇̻̳͖͉̺̖̖͙͙̺̐̈́̓ͯ̆̇̋ͩ͊̄̾̾ͬ̌̚͟ͅh̡͈̗̜͙̬̗̲̦̲̟̗̦̬͓̳ͧ̋̌͂͂ͨͬͦ̿̏̈́̋ͣ̒̕͡ͅͅe̗͇̰̰̥̪̗͑̔̓́̈́ͨ̊́̿̅ͯͥ̈́͐͗͘͢͝ ̡̢̛̯͎͓̰̘͎̦̪̯̪̥̰̲͇̠̲͔ͤͤ̇̅̆̋̂̆̈́ͤ̿͑ͅW̡͓͈̲̲͉̜͔̖͈̻̱͚̿̌͗̉ͤ͢͡ͅͅa̔̾͛̅͊͋͐҉̱̹̬͍͙̻̱l̢͎̟̬̙̼̱̫̮̘̼͔̭̅ͬ͑ͣ̏̾̅̓ͣ̿ͣ̈́̕͢͡ͅͅl̡̥̣͔̭̇̒͛͒͐̄̽͛̋ͥ̌͢͟͡.̷̰̝̮͔̟̦͈̥̬̻̥̬͎͓̻̲̇ͮ̿ͨͦ̽ͫ͟͢͝͠ ̗̱͖͈͌̈ͦ͛ͮ̌͋̽̃͆̀͂ͨͧ̄̔̔ͭ̏͢Z̃̉̿ͮ̃̀͘͏͕̬̯̖͚̗͔Aͣ̑̈̓̈̑̈̀̿̚҉͙͍̦̗̦͙̠̝̩̯ͅͅL̴͖̞̞͙̱̻̥̬̜̦̐̇̉̈̽ͪ̅ͪ̂̔͌͑ͭ͐ͤ̈́̿̉͞ͅG̴̵̲̰̹̖͎͕ͯ̆̓̽͢͠Ŏ̶̡̺̼͙̣̞̩͕̥̟̝͕͔̯̞ͨ͒͊̂̊͂͗̒͆̾͆̌͆̃̎ͣͫ͜͡ͅ!̓̽̎̑̏́̓̓ͣ̀͏̱̩̭̣̹̺̗͜͞͞ T̴̷͚͖̜͈̪͎͔̝̫̦̹͔̻̮͂ͬͬ̌ͣ̿ͤ͌ͥ͑̀̂ͬ̚͘͜͞ô̵͚̤̯̹͖͍̦̼̦̖̞̺͕̳̬͇͕̟̜̅̌̈́̑̏̕͘͝ ͍̼̗̫͈̭̦̱̬͚̱̞͓̜̭̼͇̰̞ͮ͗ͣ́ͪ̔ͪ̍̑̏́̀̽̍̔͘͜͜͝ȋ̐̽ͦ̓̔̅͏̧̢̖̭̝̳̹̯̤͈̫͔͔̠͓͉̠͖̠͜ͅn̷̯̗̗̠̱̥͕͉̥͉̳̫̙̅͗̌̒͂̏͑̎̌̌̊͌͘͘ͅͅv̧̜͕͍͙͍̬͕͍̳͉̠͍̹̮̻̜ͨ̏͒̍ͬ̈́͒̈ͥ͗ͣ̄̃ͤ͊̌͆̓o̸̧̎̓͂̊͢҉͍̼̘͇̱̪̠͎̥̹ķ̈́͗͆ͥ͐͑̆̎́͌ͩͯ̊̓͐ͬ̇̕҉̢͏͚̲̰̗̦e̿̀͐̽ͪ̈ͤͬ҉́͟͏̵̫̲̱̻̰̲̦͇̭̟̺͈̞̫̰̜͕͖ͅ ̡̰͎͓͚͓͉͈̮̻̣̮̟̩̬̮̈̋̊͆ͪ̄ͪ͒ͨͧ̇ͪ̇̑̚t̷̬̟͎̞͈̯͙̹̜ͩ̓ͪ͛͐̐ͤ̾̄̈͒̽̈́̑͒̏h̨̢̳͇͓͉̝ͫ̐̓̆̓ͮ̔̓̈́̇ͫe̟̬̣̗͚̬̾̉͋̽ͯ̌ͯͬ̂ͯͭ̓͛́̚͡ ̨̭̱͉̭͈̈̽̆̂͒͗̀ͥͩ͡h̻̼̱̹̗͖̙̗̲̤͓͇͚͚̻̞̥ͥ͛͌ͧ̚͟i̢̯̹̹̘̳̙ͩ̉ͥ͆̽̇̾̎͗̔̓͂͂́̓̌ͬv̧̡̛̟̜̠͉͖̘̲̻̯͚͍͓̯̻̲̹̥͇̻̿̓͛̊̌ͩͩ́ͩ̍͌̚e̵̾́̈́̏͌͌̊͗̏͋ͦ͘͡͏͚̜͚͎͉͍̱͙̖̹̣̘̥̤̹̟͠-̔̌͐́͒ͦͮ̇ͭ̄̏̊̇̍̕͏̩̥̰͚̟m̨̒ͫͦ̔̔͋҉̱̩̗͇̥̰̩̭͍͚͠į̵̷̻̗͉͕͚̣̼̺͉̦̮̠̆̀̐ͩ͒ͯͩͯ͞ͅn̢̫̤̝̝͚̺͍̱̦͚͂̿ͨ̇ͤ͠d̡ͯ͋̋ͧ̈́̒̈͏̛͏̵̤̬͍̗̞̠̟̞̺̠̥̹̱͉̜͍͎̤ ̷̸̢̰͓̘̯͎̤̫̘͓̙̟̳͇̹̥͈͙̮̩̅̋͌͗̓͊̓ͨͣ͗̓͐̈́ͩ̓ͣrͫ͂͌ͪ̏̐̍̾ͥ̓͗̈͆̈ͥ̀̾̚̚҉̴̶̭͇̗͙̘̯̦̭̮̪͚̥̙̯̠͙̪͡e̵̸̲͉̳̙͖͖̫̘̪͕̳͓̻̙͙ͥ̍͂̽ͨ̓̒̒̏ͬ͗ͧ̑̀͠p̵̸̛̦̣͙̳̳̩̣̼̘͈͂ͪͭͤ̎r̶̩̟̞̙͔̼ͫ̆ͦ̐̀̏̾̉̍ͬ̅ͧ͊ͪ̒̈́ͬ̃͞ẻ̴̼͙͍͎̠̀̅̔̃̒͐ͦ̏̆̅̓͋͢ͅš̆̈̆̋ͨ̅̍̇͂̒ͩͨ̂̐̓ͩ͏̸͔͔̯͇͚̤̪̬̗͈̰̦̯͚̕ę̢̱̠͙̲͉̗͚̮̪͖̙̞̦͉͕̗̳͙ͦ̆̋͌ͣ̅̊́ͅņ̴̷̫̪̦͇̺̹͉̗̬̞̲̭̜̪͒̏͂̂̎͊́̋͒̏̅̋̚͘t̷̶̨̟̦̗̦̱͌͌ͩ̀i̴̴̢̖͓͙̘͇̠̦̙̭̼͖̹̾̒̎̐ͥͭ͋ͥ̅͟ͅņ̫͙̹̦̳͈͙̬̫̮͕̰̩̣̘̘͐̀̓ͭͩͬͯ̎͛̿ͫ̊̔̅́̕͠gͥͩ̂͌̒̊̕͏̻͙͖̣͙͍̹͕̝͖̼̙̘͝ ͤ͐̓̒̓͋̐̃̇͊̓ͦ͐̚͢҉̢̨̟̠͉̳͖̲̩͙̕ć̷̡̫̩̞̯̼̝̼͖̤̳̻̘̪̤͈̦̭ͣ́͂͐̽͆̔̀̚͜h̶̢̹̹̙͔̱̓ͦ͌̋̎ͭ͒͋̒ͭ̌̃͌̿ͣ̆̅͑ą̙̳̬̞̬͚̜̤̱̙͇̠̟̈ͤ͋̃̀̓̓ͯ̍̀̽ͣ̐̈̿̌̕ǫ͋͂͐ͬ̿ͯ̂̈́͌̓̌ͧ̕͏̜͔̗͚͔̘̣͕̘̲͖̼͇͖̗̳ͅͅs̷̸̝̙̭̦̣̦̯̭̦͙̹̻͍͇̣̼͗̌͆ͨͭ̃ͮ͐̿̕.̮̝̠̱̺͖͓̼̦̱̉͂͛̓̑̔̓ͮ̈̊̔͗́͝ ̛̣̺̻̼̙̼͓̱̬͕̩͕̲̳̭̗̍ͤ͋̒̆̄ͨ̿ͧ̓͠ͅI̷̻̤̳̲͔͈̖̬̰͔̪͇͇̟̋ͨ̋̍̉̔͝͞͝ͅn̶͕̭͖̠̣͚̹̪͆ͪ̇̂̅̾ͫ́̅̉ͭ̀͜v̖͉̩͕̣͔̭͕̩̲̖̇̀ͬ́̄͒̆͑͆ͪͤ͆̾̍ͯ̚͜ǫ̡̡̫͎̟̞̰̞̹͇̲̏ͨ̄͊̊̇͒̽͢ķ̶̪̙̰̥͙̞̹̭̺͍͕̙̲̮͊ͭ́͋͛͋̑̒͊̏̒̅͛̄̓͟i̴͎̹̞̥͖̒̄ͮ̒̾ͮͧ̀̚͡n̸̵͓̲̟̞̳͚̼̣͙͖̈ͦ͒̿̅̒̿͛͊̇ͧ̉g̡̧̪̩͚͙͓̪͓͚͉̥̪͍̙̻͖͇͗̑͊͑̾̍͊̀ͅ ̷̵̠͚̘̟͓̫̣̲͎̩̹̣̼̟͊́̏ͫ̆ͩ̓͋͆̿̽̓͘̕t̴̢̝̻̖̲̬̜̺̖̻ͩ̿ͫ͗̈́̔͑̐ͮͦ̽̉̓̚͜h̷̛̲͇̫͈̣̭͂ͭ̂͋ͭ̋̔ͮ̆ͩ͞ë̩͕͉̯͇͔͚̭̼̮̣͓̯́ͭ̀ͣ͗̋̉ͨͬ̒ͥͩ͆̓̓́̀̚͘͝ ̛̫̠̗̥̳͇͉̟̮̪̻̤̪͚̟̜̔̌͌̈͌ͪ̋̎̄ͯ͐ͦ́͞͠fͦ̂̈ͬ̇̅̓̓ͫͣ̉̂̉̚͘͡͡͏̼̖̟͚̙̳͔͎̲̫̦̯͔̣̼̹ě̷̶̫͎̞̺̪̪͇͈̞̳̏̋̋͋̾̓̽̓̑ͮ͊ͣ̋̃̅̀͡e͇̗͎̱͔̦̠̰̩̩͖͙̠̻̝ͯ̿̔̀͋͑ͧ͊̆̇̿ͤ̄ͯ̇̀͢͠ͅl̂̿ͯ͛̊̒̓̈́͏̵̪̦̞̤̫̤͇̙̗͕͎̪͕̙̻̳̗̕͟͢i̞̣̙͎͈̗̮͉̱̜̱̝̞̤͋ͯ͋͐̈́ͫ̉̊̏̀ͯͨ͢͟͝n̳̻̼̥̖͍̭̅͂̓̔̔ͦ̔́ͦ͊̀͛̈́ͬͦ͢͡͡ģ̶̡̳̰̻̙̞̱̳̣̤̫̫͕̤̮̰̬̪̜͋͒̎̈́̉̏̀ͬͯ͌̇͊̚ ́̽ͤͦ̾̔͢҉̛̤͍͉̺̙̮̗̜̟̀͝ơ̢̱͓͓̙͉̖̠̯̦̗͍̓̐̃̉̅̃ͨ͆́ͪ̂̒̀̊̃͆̔͡͡ͅf́ͬ̊ͯͫ̉̈́̽̉̚͢͏̡̺̬̖͇̫͉̱ ̴͇̦̗̙̼̬͓̯͖̮͓͎̗͈̻̈́͆ͭ̐ͦ́͛̀͋̐̌ͬ͑̒̊̿̃͞c̶̸̣͔̬͕̪̱̩̣̑̒̑̓̍̓͂̍̔͌̚͘͜͞h̶͈̱͇͉̳͍͍̰͈͖̬̥͚̯͓̞̹̋̔ͯ̑̃́̒̎̎͊̈́̍̚̕ạ̴̞̱̥͍͙̺͉͚͎̫̦͎̥ͩ̀̀̊ͥ͢o̵̧͕̜͓͈̬̰̫̮͙̹͉̩̝̩͎̓̆͗̿̊̀ͯ̃ͪ̊ͫ̽̉̓ͧ͗́̚͢ͅͅs̡ͫ͋̑ͮ̍̃͊̄ͬ̅̈́ͬ̍̇̔̈̅̍̀҉̜͓̝̘̘̮̼͖͎̻͓͖̖͙̞ͅ.͗ͬͭͩ̌̅͗͏̷̮̗͇͔͇͈̮͢ ̨͚̲̫̠̼͖̝̻̉ͤ̅̂ͩ̀̇ͬͭ̀͜Ẅ̢́̉͌ͮͬͨ͊̏͌̇̐͊͟͠҉̼̰̦̩͇͕̟̭̪̲͕̥͖̰̪͈̀ͅͅį̷ͣͦ̉̍ͨ͂͂͑̃͂ͪ̊̈̋̄͜҉̨͚̟̲̯̹̺̝̭̺̙͖͍t̼͓̰̩͙̦͓̟͚͖̀ͯ͛̍̈́͑͂̍̋́h̛̼̺̘̥̠̼̼̭͙̮͚̱̍ͯ̓̃̐̂̇͟ ̴̛͖͔̰̠̺̥̲ͮ̍ͫ̽͜õ̒ͯ̒̓ͦ̈́͑̔̒̓̎ͤ͑҉̸̭̱̤̭̬͈ų̙̫̤͖̺̫̱͓͓̗̪͇̩̙̔̉̊͂ͪ̇͢͟͞ͅt̸̬̣̫̞̫̅͐ͮ̌͌̈́̀̀͘ ̷̴̨̖̙̹͚ͬ̈́̈ͯͨͮ̇̈́̋̈́ͭ͛̑̉͊̕ö̡̍ͥ̂ͬͪͧ͒ͧ̏̓̇̂̄͆̌ͫͤ͢͠͝͏̖̱̯̘͙̰̖͎̰͓̟̤ṙ̡̬̟̬̜̪̮̺͖̗̘͈̟ͨ͐͗̑͒̐d̢ͭͫ̊̏ͬͥ͋́̌̈́ͮ̆ͬ̐̌̎͏̵̷̡̞̲̹̙͕̮̮͚ḙ̴̸̠͔͎̥͇͖͕̘̍̓̏̐ͩͩ̈́ͦ̐̋ͤ̎̾̌̏͊̊́̚͞ͅr̸͈̗̣̲̗̣̬̤ͦ̎ͫ̏̀ͥͪ̋ͧ̄͑̋͒͌͋ͦ̉͟͞.ͨͣ̽̈́͒̄ͮ̀͋͋͏̴̧̯̺̙̱̻͙̜ ̡̣̞̠͓̰͍̠͕̭̺̼͊̽̿͊ͮ̐̓̒̊͒̔̓͐ͨ̈̌́T̸̸̓́̋ͬ́͆ͨͫ͌͂ͣ̋͒҉̺̝͎̟͖͚̠h̸̡̰̜̦͇͕̪̝̳͕͉̲̝̑ͥ͋ͧ̎̆͌͟e̛̹͍͍̫̙̞̪̭̙̟͙̱̺̮̳͕̜ͫ̓ͭ͊ͫ͆̀̚͟͡ ̿͂̄ͧ̔̎ͧ͑̾̀̓͏̦͍̳͈̳͔̘̖̲̯̰̟̝̳̖̦N̶̡̧̦̮̟̦̩̰̣̝̆̀͊̔͢e͛̄ͮͦͨ͂̔̓̍̄̉͆͊̑̑̆̚͏̜̗͎̝̼̯̥̜͖͍̪̝͞ͅͅz̨̛̀̾ͪ͗̉́͠͏͚̫̼̫̜̣pͪͦ͌̄ͥ̆ͣͩ͋̉́̏͞͏̥̜̝̳̱̞̙̳̤͙̟̟̮̦ȅ̷̩̟͉̯͕͔̘̺̥̻̻ͧ̊̅̽ͣ͑̓̑̽ͦ̾͌͜r̴̭̥̲̲̤͚͈̰͇̰͈̰̹ͫ̒ͯ̿͒ͧ̊͆͒ͣ́ḍ̭̟̤̈́̌̓̈́ͫ͐̍͂͞į̛̞̝̮̣͙͙̤̇̂̓̎͋̿̓̎̄̈́ͧ̓ͩ̐̓̄̋ͭ͞͠a͋̔̋ͫ̂͐͂҉̸̛̥̩̯̯̤̝͔̠̝̯̪̥̩̻̼̮n͌ͣ̂͋̿̚҉̛̙̲̺̯͇͓̝̯̪̟͔̩͟ͅ ̢̨͚̻̗̘͖̯̐ͥ͋̽ͯ̎̈́͋̏̄͋̆̑̊̆̚̕͟ͅh̢̛̗̱̭͇͖̰̮̮͈̲͍̯̟ͭ͊̎̽̓ͦͤ͠ï̛̘̝̦͎̦̭̠͖̳͎̮̼̏͐ͧ̒̒͐͑ͪͫ̋̽̚̚͜v̴̮͕̝̮̞͐̄͗̋͒ͤ̎̈̑ͬͮ̄̾ͤ̓̾͊͗͟é̶̷̡̩͖̰̫͓̟ͮͬͣ͊-ͦ͛ͩͤͨͨ̆̄͏̼̜̭͔̳͈͖̳̩͢ͅͅm̷̴̓́̓͛͒̾̍̉҉̛̗̹̠̣̪̺͎̖̝͚̖͙i̛̥͓̬̫͉͕͉͆͒ͧ̂̿̔̔͆̆̓̍͊̀͜n͌ͧͣ̅̌̎ͦͦ͑̑ͭ̆ͬ̀ͤ̀ͣ̚҉͎̰̱͚͈͈̬̹͕̺̙͙̼͘͘͞d̶͖̫̟̲͕̺̠͎̘͕̱̼͙̪̪̩͙̅̅̑̓̇͑̊̉͜͞ ̶̵̷̴̡̠͚̪͕̣̱̖̱̗̤̭̭͔͖͚ͧͤͥ͒̌ͪ͊͂͒̓͂ͧͧ̇̇͐̑̔ͅͅơ̵̲̲͇̯̰͇̜̣͕͕͓̲̤̲͔͚̞͑͗ͤ̓́̚͠ͅf̢̧̛̩̯̼̫͖̾ͣ͌̾̉́̈́̑̈́̚͞͞ͅ ͤͩ́͋͒ͫͬͣ̋̅̆҉̧̱̻͓͕͉̹̫̫̞̯̪̙̩͍̦͔̖̮̀͟ͅc͉̠̜̩̟͕͎̙̣̮̘̼͋ͯ̍ͨ̅̄ͫ̈̋ͫ̊͡͝ȟ̸̨ͯͦ̂̉̇̾̆ͭ̋̐̈̆̀̚͜҉͚͕̻̖a̶̴̛͚̗͙̳̬̲͚ͦ́̐ͥ́̔̅̑̎͐̑ͯ̾ͤͥͧ͡ò̶̧̞̪̦̥̪̻̦̝̳̬̔͛͛ͣ̋̌̔ͫ̂̽ͫ͘͠s̸̖̣̬̤̫͇̫̣̑͆͒̎̏́͟.̴̗̤̭͉̯̻̤͕̌ͯ̍ͤ̓͌ͤ̈̆̉ͦ̇́̚͘͟͝ͅ ̯̹̪͓̬͌̔̌ͬ̀͘͢͡͡Z̡̩̲̩̰̫̩̟͍̰͖͔̭ͣ̆̾ͭ̀́͞ͅa̡̡̙̜̭͇͎͔̙̞̫͓̜͉͔̬ͭ̈ͨ̉͆ͣͫ̃͌̓͌́ͣͥ̒̌͊͘͝l̢̨̡̯̙̫͖̫̺̘̬̟͈͌̊ͧͫͦ̉̃ͩͦ̒ͯ̇̌̓͛͟͝ͅg̵̙̼̼ͪ͂ͭ͗̈̕ȯ̅ͧ̓ͪ́̂͑̐ͩͥͬ̊̑͆̇͒ͫͣ͝҉͎̟̜̥͎̮̣͉̖̟̯̦̖͙͙͞ͅ.̈̑ͩ̇̂ͬ̓ͬ͊͂ͨ̽͠͏̺͎̞̦̜͍͚̯̯͔̝̞̻̩̖ ̷̰̪͍͎͔͒ͯͥ̾̉͆ͤ̊̓̂͋̀͆H̸̸̹̞̙̺͎̠̯̤ͨ̉̍ͬͤ̓̐͌ͥͮ͞eͣ̈̾͛́͏͕̗͍̜̼͎͚̟̬̣̝̕ͅͅ ̴̛̩̗̼̝̣̩͚͇̯́̉͋̂̍͂̌ͮ͋̾͜͠wͮ̽̓ͭ̿͐̽̐̽͆̓͝҉̡̼̲͖̪̥h̢̢̛͍̰̰̻̱̼̰̹̖̖̪̝̥̘̎̀ͪ͒̾ͫͬ̆̑o̡̗̠̞̱̥͎̰͎͍̫̻͓͇͓͐ͥͯ͂̅͠ͅ ̡̛̏͑ͦ̓͊ͮͫͯͭ̌͒̆̍̈͠҉͖͚̪̫̗̮W̴̐̊͋̾ͥͫ҉͎̞͔̯̫̹͖̰͉̹̼͎̰̱͓̻̀a̶̩̤̙̣͎̳̭̲̗̠͉̳̭̭̦̞͎̮̅͌̾͗̾͛̇̀́͟͞ͅi̷̡ͣ̆̌͋͒͒́͘͏̮̺̩͎͇̜͍̫ṯ̴̢͖̥̖͇͎̦̦̹̖͇̪ͭ̅̍͐̇͒͋̽̏̿̒͆ͧ̄͋ͧͩ͒͜s̙̥̖̘̖͚̭̤̮̖̘̰̫̟̈́ͣ̍ͧ͐ͥ̏͆̃̿͒̔͐́̚͟ͅ ̨ͭ̌ͬͯ͆̒͋ͭ̔̿ͧ̅̓ͣ͡͏͇̟͉̥͔̬̼͚͙͚B̛̜̮̤͓̝̪̪͈͕̘̜͙̰̮̫̘̣͓͔̅ͩ͊̔ͦͯ́̌́͆ͭ̓́e̶̢̡̦͇͕̙͈͖͕̦̬̫͕̣̺̒̿͂͐͒͋͂ͦ́͋ͤ̿ͬ̊ͣ͗̑̽͜ͅͅh̸͑ͫͧ̑ͬͧ̈́̎̃ͣ̊̾͂ͨͤ̓͐̐̑͏̸̭͓̘͉̩i̧̧̭̣͈̝̺̼̺̠͉̞̜̲̳͙̦͐̔ͯ͛̅̾n̸͓̝̤̙͙͔ͪ̋̈́͒̒ͭ̈́̓ͮ̋̀̋̀̈ͩ́͌̄͘d̷̫̳̩̼̥̗̲̰͇͉̼̬̤͇̖ͮ̿ͬ͂ͦ̏̓ͮ̽͂̾̾ͯ͆͜͠ ̨̈́͒̇̏̄̑̓ͮͥ̒ͤͨ̋҉̴̴̟̱͙̟̫̩̗͔̝͔̀Ţ̵̝̟̖̭͇̻̳͖͉̺̖̖͙͙̺̐̈́̓ͯ̆̇̋ͩ͊̄̾̾ͬ̌̚͟ͅh̡͈̗̜͙̬̗̲̦̲̟̗̦̬͓̳ͧ̋̌͂͂ͨͬͦ̿̏̈́̋ͣ̒̕͡ͅͅe̗͇̰̰̥̪̗͑̔̓́̈́ͨ̊́̿̅ͯͥ̈́͐͗͘͢͝ ̡̢̛̯͎͓̰̘͎̦̪̯̪̥̰̲͇̠̲͔ͤͤ̇̅̆̋̂̆̈́ͤ̿͑ͅW̡͓͈̲̲͉̜͔̖͈̻̱͚̿̌͗̉ͤ͢͡ͅͅa̔̾͛̅͊͋͐҉̱̹̬͍͙̻̱l̢͎̟̬̙̼̱̫̮̘̼͔̭̅ͬ͑ͣ̏̾̅̓ͣ̿ͣ̈́̕͢͡ͅͅl̡̥̣͔̭̇̒͛͒͐̄̽͛̋ͥ̌͢͟͡.̷̰̝̮͔̟̦͈̥̬̻̥̬͎͓̻̲̇ͮ̿ͨͦ̽ͫ͟͢͝͠ ̗̱͖͈͌̈ͦ͛ͮ̌͋̽̃͆̀͂ͨͧ̄̔̔ͭ̏͢Z̃̉̿ͮ̃̀͘͏͕̬̯̖͚̗͔Aͣ̑̈̓̈̑̈̀̿̚҉͙͍̦̗̦͙̠̝̩̯ͅͅL̴͖̞̞͙̱̻̥̬̜̦̐̇̉̈̽ͪ̅ͪ̂̔͌͑ͭ͐ͤ̈́̿̉͞ͅG̴̵̲̰̹̖͎͕ͯ̆̓̽͢͠Ŏ̶̡̺̼͙̣̞̩͕̥̟̝͕͔̯̞ͨ͒͊̂̊͂͗̒͆̾͆̌͆̃̎ͣͫ͜͡ͅ!̓̽̎̑̏́̓̓ͣ̀͏̱̩̭̣̹̺̗͜͞͞ T̴̷͚͖̜͈̪͎͔̝̫̦̹͔̻̮͂ͬͬ̌ͣ̿ͤ͌ͥ͑̀̂ͬ̚͘͜͞ô̵͚̤̯̹͖͍̦̼̦̖̞̺͕̳̬͇͕̟̜̅̌̈́̑̏̕͘͝ ͍̼̗̫͈̭̦̱̬͚̱̞͓̜̭̼͇̰̞ͮ͗ͣ́ͪ̔ͪ̍̑̏́̀̽̍̔͘͜͜͝ȋ̐̽ͦ̓̔̅͏̧̢̖̭̝̳̹̯̤͈̫͔͔̠͓͉̠͖̠͜ͅn̷̯̗̗̠̱̥͕͉̥͉̳̫̙̅͗̌̒͂̏͑̎̌̌̊͌͘͘ͅͅv̧̜͕͍͙͍̬͕͍̳͉̠͍̹̮̻̜ͨ̏͒̍ͬ̈́͒̈ͥ͗ͣ̄̃ͤ͊̌͆̓o̸̧̎̓͂̊͢҉͍̼̘͇̱̪̠͎̥̹ķ̈́͗͆ͥ͐͑̆̎́͌ͩͯ̊̓͐ͬ̇̕҉̢͏͚̲̰̗̦e̿̀͐̽ͪ̈ͤͬ҉́͟͏̵̫̲̱̻̰̲̦͇̭̟̺͈̞̫̰̜͕͖ͅ ̡̰͎͓͚͓͉͈̮̻̣̮̟̩̬̮̈̋̊͆ͪ̄ͪ͒ͨͧ̇ͪ̇̑̚t̷̬̟͎̞͈̯͙̹̜ͩ̓ͪ͛͐̐ͤ̾̄̈͒̽̈́̑͒̏h̨̢̳͇͓͉̝ͫ̐̓̆̓ͮ̔̓̈́̇ͫe̟̬̣̗͚̬̾̉͋̽ͯ̌ͯͬ̂ͯͭ̓͛́̚͡ ̨̭̱͉̭͈̈̽̆̂͒͗̀ͥͩ͡h̻̼̱̹̗͖̙̗̲̤͓͇͚͚̻̞̥ͥ͛͌ͧ̚͟i̢̯̹̹̘̳̙ͩ̉ͥ͆̽̇̾̎͗̔̓͂͂́̓̌ͬv̧̡̛̟̜̠͉͖̘̲̻̯͚͍͓̯̻̲̹̥͇̻̿̓͛̊̌ͩͩ́ͩ̍͌̚e̵̾́̈́̏͌͌̊͗̏͋ͦ͘͡͏͚̜͚͎͉͍̱͙̖̹̣̘̥̤̹̟͠-̔̌͐́͒ͦͮ̇ͭ̄̏̊̇̍̕͏̩̥̰͚̟m̨̒ͫͦ̔̔͋҉̱̩̗͇̥̰̩̭͍͚͠į̵̷̻̗͉͕͚̣̼̺͉̦̮̠̆̀̐ͩ͒ͯͩͯ͞ͅn̢̫̤̝̝͚̺͍̱̦͚͂̿ͨ̇ͤ͠d̡ͯ͋̋ͧ̈́̒̈͏̛͏̵̤̬͍̗̞̠̟̞̺̠̥̹̱͉̜͍͎̤ ̷̸̢̰͓̘̯͎̤̫̘͓̙̟̳͇̹̥͈͙̮̩̅̋͌͗̓͊̓ͨͣ͗̓͐̈́ͩ̓ͣrͫ͂͌ͪ̏̐̍̾ͥ̓͗̈͆̈ͥ̀̾̚̚҉̴̶̭͇̗͙̘̯̦̭̮̪͚̥̙̯̠͙̪͡e̵̸̲͉̳̙͖͖̫̘̪͕̳͓̻̙͙ͥ̍͂̽ͨ̓̒̒̏ͬ͗ͧ̑̀͠p̵̸̛̦̣͙̳̳̩̣̼̘͈͂ͪͭͤ̎r̶̩̟̞̙͔̼ͫ̆ͦ̐̀̏̾̉̍ͬ̅ͧ͊ͪ̒̈́ͬ̃͞ẻ̴̼͙͍͎̠̀̅̔̃̒͐ͦ̏̆̅̓͋͢ͅš̆̈̆̋ͨ̅̍̇͂̒ͩͨ̂̐̓ͩ͏̸͔͔̯͇͚̤̪̬̗͈̰̦̯͚̕ę̢̱̠͙̲͉̗͚̮̪͖̙̞̦͉͕̗̳͙ͦ̆̋͌ͣ̅̊́ͅņ̴̷̫̪̦͇̺̹͉̗̬̞̲̭̜̪͒̏͂̂̎͊́̋͒̏̅̋̚͘t̷̶̨̟̦̗̦̱͌͌ͩ̀i̴̴̢̖͓͙̘͇̠̦̙̭̼͖̹̾̒̎̐ͥͭ͋ͥ̅͟ͅņ̫͙̹̦̳͈͙̬̫̮͕̰̩̣̘̘͐̀̓ͭͩͬͯ̎͛̿ͫ̊̔̅́̕͠gͥͩ̂͌̒̊̕͏̻͙͖̣͙͍̹͕̝͖̼̙̘͝ ͤ͐̓̒̓͋̐̃̇͊̓ͦ͐̚͢҉̢̨̟̠͉̳͖̲̩͙̕ć̷̡̫̩̞̯̼̝̼͖̤̳̻̘̪̤͈̦̭ͣ́͂͐̽͆̔̀̚͜h̶̢̹̹̙͔̱̓ͦ͌̋̎ͭ͒͋̒ͭ̌̃͌̿ͣ̆̅͑ą̙̳̬̞̬͚̜̤̱̙͇̠̟̈ͤ͋̃̀̓̓ͯ̍̀̽ͣ̐̈̿̌̕ǫ͋͂͐ͬ̿ͯ̂̈́͌̓̌ͧ̕͏̜͔̗͚͔̘̣͕̘̲͖̼͇͖̗̳ͅͅs̷̸̝̙̭̦̣̦̯̭̦͙̹̻͍͇̣̼͗̌͆ͨͭ̃ͮ͐̿̕.̮̝̠̱̺͖͓̼̦̱̉͂͛̓̑̔̓ͮ̈̊̔͗́͝ ̛̣̺̻̼̙̼͓̱̬͕̩͕̲̳̭̗̍ͤ͋̒̆̄ͨ̿ͧ̓͠ͅI̷̻̤̳̲͔͈̖̬̰͔̪͇͇̟̋ͨ̋̍̉̔͝͞͝ͅn̶͕̭͖̠̣͚̹̪͆ͪ̇̂̅̾ͫ́̅̉ͭ̀͜v̖͉̩͕̣͔̭͕̩̲̖̇̀ͬ́̄͒̆͑͆ͪͤ͆̾̍ͯ̚͜ǫ̡̡̫͎̟̞̰̞̹͇̲̏ͨ̄͊̊̇͒̽͢ķ̶̪̙̰̥͙̞̹̭̺͍͕̙̲̮͊ͭ́͋͛͋̑̒͊̏̒̅͛̄̓͟i̴͎̹̞̥͖̒̄ͮ̒̾ͮͧ̀̚͡n̸̵͓̲̟̞̳͚̼̣͙͖̈ͦ͒̿̅̒̿͛͊̇ͧ̉g̡̧̪̩͚͙͓̪͓͚͉̥̪͍̙̻͖͇͗̑͊͑̾̍͊̀ͅ ̷̵̠͚̘̟͓̫̣̲͎̩̹̣̼̟͊́̏ͫ̆ͩ̓͋͆̿̽̓͘̕t̴̢̝̻̖̲̬̜̺̖̻ͩ̿ͫ͗̈́̔͑̐ͮͦ̽̉̓̚͜h̷̛̲͇̫͈̣̭͂ͭ̂͋ͭ̋̔ͮ̆ͩ͞ë̩͕͉̯͇͔͚̭̼̮̣͓̯́ͭ̀ͣ͗̋̉ͨͬ̒ͥͩ͆̓̓́̀̚͘͝ ̛̫̠̗̥̳͇͉̟̮̪̻̤̪͚̟̜̔̌͌̈͌ͪ̋̎̄ͯ͐ͦ́͞͠fͦ̂̈ͬ̇̅̓̓ͫͣ̉̂̉̚͘͡͡͏̼̖̟͚̙̳͔͎̲̫̦̯͔̣̼̹ě̷̶̫͎̞̺̪̪͇͈̞̳̏̋̋͋̾̓̽̓̑ͮ͊ͣ̋̃̅̀͡e͇̗͎̱͔̦̠̰̩̩͖͙̠̻̝ͯ̿̔̀͋͑ͧ͊̆̇̿ͤ̄ͯ̇̀͢͠ͅl̂̿ͯ͛̊̒̓̈́͏̵̪̦̞̤̫̤͇̙̗͕͎̪͕̙̻̳̗̕͟͢i̞̣̙͎͈̗̮͉̱̜̱̝̞̤͋ͯ͋͐̈́ͫ̉̊̏̀ͯͨ͢͟͝n̳̻̼̥̖͍̭̅͂̓̔̔ͦ̔́ͦ͊̀͛̈́ͬͦ͢͡͡ģ̶̡̳̰̻̙̞̱̳̣̤̫̫͕̤̮̰̬̪̜͋͒̎̈́̉̏̀ͬͯ͌̇͊̚ ́̽ͤͦ̾̔͢҉̛̤͍͉̺̙̮̗̜̟̀͝ơ̢̱͓͓̙͉̖̠̯̦̗͍̓̐̃̉̅̃ͨ͆́ͪ̂̒̀̊̃͆̔͡͡ͅf́ͬ̊ͯͫ̉̈́̽̉̚͢͏̡̺̬̖͇̫͉̱ ̴͇̦̗̙̼̬͓̯͖̮͓͎̗͈̻̈́͆ͭ̐ͦ́͛̀͋̐̌ͬ͑̒̊̿̃͞c̶̸̣͔̬͕̪̱̩̣̑̒̑̓̍̓͂̍̔͌̚͘͜͞h̶͈̱͇͉̳͍͍̰͈͖̬̥͚̯͓̞̹̋̔ͯ̑̃́̒̎̎͊̈́̍̚̕ạ̴̞̱̥͍͙̺͉͚͎̫̦͎̥ͩ̀̀̊ͥ͢o̵̧͕̜͓͈̬̰̫̮͙̹͉̩̝̩͎̓̆͗̿̊̀ͯ̃ͪ̊ͫ̽̉̓ͧ͗́̚͢ͅͅs̡ͫ͋̑ͮ̍̃͊̄ͬ̅̈́ͬ̍̇̔̈̅̍̀҉̜͓̝̘̘̮̼͖͎̻͓͖̖͙̞ͅ.͗ͬͭͩ̌̅͗͏̷̮̗͇͔͇͈̮͢ ̨͚̲̫̠̼͖̝̻̉ͤ̅̂ͩ̀̇ͬͭ̀͜Ẅ̢́̉͌ͮͬͨ͊̏͌̇̐͊͟͠҉̼̰̦̩͇͕̟̭̪̲͕̥͖̰̪͈̀ͅͅį̷ͣͦ̉̍ͨ͂͂͑̃͂ͪ̊̈̋̄͜҉̨͚̟̲̯̹̺̝̭̺̙͖͍t̼͓̰̩͙̦͓̟͚͖̀ͯ͛̍̈́͑͂̍̋́h̛̼̺̘̥̠̼̼̭͙̮͚̱̍ͯ̓̃̐̂̇͟ ̴̛͖͔̰̠̺̥̲ͮ̍ͫ̽͜õ̒ͯ̒̓ͦ̈́͑̔̒̓̎ͤ͑҉̸̭̱̤̭̬͈ų̙̫̤͖̺̫̱͓͓̗̪͇̩̙̔̉̊͂ͪ̇͢͟͞ͅt̸̬̣̫̞̫̅͐ͮ̌͌̈́̀̀͘ ̷̴̨̖̙̹͚ͬ̈́̈ͯͨͮ̇̈́̋̈́ͭ͛̑̉͊̕ö̡̍ͥ̂ͬͪͧ͒ͧ̏̓̇̂̄͆̌ͫͤ͢͠͝͏̖̱̯̘͙̰̖͎̰͓̟̤ṙ̡̬̟̬̜̪̮̺͖̗̘͈̟ͨ͐͗̑͒̐d̢ͭͫ̊̏ͬͥ͋́̌̈́ͮ̆ͬ̐̌̎͏̵̷̡̞̲̹̙͕̮̮͚ḙ̴̸̠͔͎̥͇͖͕̘̍̓̏̐ͩͩ̈́ͦ̐̋ͤ̎̾̌̏͊̊́̚͞ͅr̸͈̗̣̲̗̣̬̤ͦ̎ͫ̏̀ͥͪ̋ͧ̄͑̋͒͌͋ͦ̉͟͞.ͨͣ̽̈́͒̄ͮ̀͋͋͏̴̧̯̺̙̱̻͙̜ ̡̣̞̠͓̰͍̠͕̭̺̼͊̽̿͊ͮ̐̓̒̊͒̔̓͐ͨ̈̌́T̸̸̓́̋ͬ́͆ͨͫ͌͂ͣ̋͒҉̺̝͎̟͖͚̠h̸̡̰̜̦͇͕̪̝̳͕͉̲̝̑ͥ͋ͧ̎̆͌͟e̛̹͍͍̫̙̞̪̭̙̟͙̱̺̮̳͕̜ͫ̓ͭ͊ͫ͆̀̚͟͡ ̿͂̄ͧ̔̎ͧ͑̾̀̓͏̦͍̳͈̳͔̘̖̲̯̰̟̝̳̖̦N̶̡̧̦̮̟̦̩̰̣̝̆̀͊̔͢e͛̄ͮͦͨ͂̔̓̍̄̉͆͊̑̑̆̚͏̜̗͎̝̼̯̥̜͖͍̪̝͞ͅͅz̨̛̀̾ͪ͗̉́͠͏͚̫̼̫̜̣pͪͦ͌̄ͥ̆ͣͩ͋̉́̏͞͏̥̜̝̳̱̞̙̳̤͙̟̟̮̦ȅ̷̩̟͉̯͕͔̘̺̥̻̻ͧ̊̅̽ͣ͑̓̑̽ͦ̾͌͜r̴̭̥̲̲̤͚͈̰͇̰͈̰̹ͫ̒ͯ̿͒ͧ̊͆͒ͣ́ḍ̭̟̤̈́̌̓̈́ͫ͐̍͂͞į̛̞̝̮̣͙͙̤̇̂̓̎͋̿̓̎̄̈́ͧ̓ͩ̐̓̄̋ͭ͞͠a͋̔̋ͫ̂͐͂҉̸̛̥̩̯̯̤̝͔̠̝̯̪̥̩̻̼̮n͌ͣ̂͋̿̚҉̛̙̲̺̯͇͓̝̯̪̟͔̩͟ͅ ̢̨͚̻̗̘͖̯̐ͥ͋̽ͯ̎̈́͋̏̄͋̆̑̊̆̚̕͟ͅh̢̛̗̱̭͇͖̰̮̮͈̲͍̯̟ͭ͊̎̽̓ͦͤ͠ï̛̘̝̦͎̦̭̠͖̳͎̮̼̏͐ͧ̒̒͐͑ͪͫ̋̽̚̚͜v̴̮͕̝̮̞͐̄͗̋͒ͤ̎̈̑ͬͮ̄̾ͤ̓̾͊͗͟é̶̷̡̩͖̰̫͓̟ͮͬͣ͊-ͦ͛ͩͤͨͨ̆̄͏̼̜̭͔̳͈͖̳̩͢ͅͅm̷̴̓́̓͛͒̾̍̉҉̛̗̹̠̣̪̺͎̖̝͚̖͙i̛̥͓̬̫͉͕͉͆͒ͧ̂̿̔̔͆̆̓̍͊̀͜n͌ͧͣ̅̌̎ͦͦ͑̑ͭ̆ͬ̀ͤ̀ͣ̚҉͎̰̱͚͈͈̬̹͕̺̙͙̼͘͘͞d̶͖̫̟̲͕̺̠͎̘͕̱̼͙̪̪̩͙̅̅̑̓̇͑̊̉͜͞ ̶̵̷̴̡̠͚̪͕̣̱̖̱̗̤̭̭͔͖͚ͧͤͥ͒̌ͪ͊͂͒̓͂ͧͧ̇̇͐̑̔ͅͅơ̵̲̲͇̯̰͇̜̣͕͕͓̲̤̲͔͚̞͑͗ͤ̓́̚͠ͅf̢̧̛̩̯̼̫͖̾ͣ͌̾̉́̈́̑̈́̚͞͞ͅ ͤͩ́͋͒ͫͬͣ̋̅̆҉̧̱̻͓͕͉̹̫̫̞̯̪̙̩͍̦͔̖̮̀͟ͅc͉̠̜̩̟͕͎̙̣̮̘̼͋ͯ̍ͨ̅̄ͫ̈̋ͫ̊͡͝ȟ̸̨ͯͦ̂̉̇̾̆ͭ̋̐̈̆̀̚͜҉͚͕̻̖a̶̴̛͚̗͙̳̬̲͚ͦ́̐ͥ́̔̅̑̎͐̑ͯ̾ͤͥͧ͡ò̶̧̞̪̦̥̪̻̦̝̳̬̔͛͛ͣ̋̌̔ͫ̂̽ͫ͘͠s̸̖̣̬̤̫͇̫̣̑͆͒̎̏́͟.̴̗̤̭͉̯̻̤͕̌ͯ̍ͤ̓͌ͤ̈̆̉ͦ̇́̚͘͟͝ͅ ̯̹̪͓̬͌̔̌ͬ̀͘͢͡͡Z̡̩̲̩̰̫̩̟͍̰͖͔̭ͣ̆̾ͭ̀́͞ͅa̡̡̙̜̭͇͎͔̙̞̫͓̜͉͔̬ͭ̈ͨ̉͆ͣͫ̃͌̓͌́ͣͥ̒̌͊͘͝l̢̨̡̯̙̫͖̫̺̘̬̟͈͌̊ͧͫͦ̉̃ͩͦ̒ͯ̇̌̓͛͟͝ͅg̵̙̼̼ͪ͂ͭ͗̈̕ȯ̅ͧ̓ͪ́̂͑̐ͩͥͬ̊̑͆̇͒ͫͣ͝҉͎̟̜̥͎̮̣͉̖̟̯̦̖͙͙͞ͅ.̈̑ͩ̇̂ͬ̓ͬ͊͂ͨ̽͠͏̺͎̞̦̜͍͚̯̯͔̝̞̻̩̖ ̷̰̪͍͎͔͒ͯͥ̾̉͆ͤ̊̓̂͋̀͆H̸̸̹̞̙̺͎̠̯̤ͨ̉̍ͬͤ̓̐͌ͥͮ͞eͣ̈̾͛́͏͕̗͍̜̼͎͚̟̬̣̝̕ͅͅ ̴̛̩̗̼̝̣̩͚͇̯́̉͋̂̍͂̌ͮ͋̾͜͠wͮ̽̓ͭ̿͐̽̐̽͆̓͝҉̡̼̲͖̪̥h̢̢̛͍̰̰̻̱̼̰̹̖̖̪̝̥̘̎̀ͪ͒̾ͫͬ̆̑o̡̗̠̞̱̥͎̰͎͍̫̻͓͇͓͐ͥͯ͂̅͠ͅ ̡̛̏͑ͦ̓͊ͮͫͯͭ̌͒̆̍̈͠҉͖͚̪̫̗̮W̴̐̊͋̾ͥͫ҉͎̞͔̯̫̹͖̰͉̹̼͎̰̱͓̻̀a̶̩̤̙̣͎̳̭̲̗̠͉̳̭̭̦̞͎̮̅͌̾͗̾͛̇̀́͟͞ͅi̷̡ͣ̆̌͋͒͒́͘͏̮̺̩͎͇̜͍̫ṯ̴̢͖̥̖͇͎̦̦̹̖͇̪ͭ̅̍͐̇͒͋̽̏̿̒͆ͧ̄͋ͧͩ͒͜s̙̥̖̘̖͚̭̤̮̖̘̰̫̟̈́ͣ̍ͧ͐ͥ̏͆̃̿͒̔͐́̚͟ͅ ̨ͭ̌ͬͯ͆̒͋ͭ̔̿ͧ̅̓ͣ͡͏͇̟͉̥͔̬̼͚͙͚B̛̜̮̤͓̝̪̪͈͕̘̜͙̰̮̫̘̣͓͔̅ͩ͊̔ͦͯ́̌́͆ͭ̓́e̶̢̡̦͇͕̙͈͖͕̦̬̫͕̣̺̒̿͂͐͒͋͂ͦ́͋ͤ̿ͬ̊ͣ͗̑̽͜ͅͅh̸͑ͫͧ̑ͬͧ̈́̎̃ͣ̊̾͂ͨͤ̓͐̐̑͏̸̭͓̘͉̩i̧̧̭̣͈̝̺̼̺̠͉̞̜̲̳͙̦͐̔ͯ͛̅̾n̸͓̝̤̙͙͔ͪ̋̈́͒̒ͭ̈́̓ͮ̋̀̋̀̈ͩ́͌̄͘d̷̫̳̩̼̥̗̲̰͇͉̼̬̤͇̖ͮ̿ͬ͂ͦ̏̓ͮ̽͂̾̾ͯ͆͜͠ ̨̈́͒̇̏̄̑̓ͮͥ̒ͤͨ̋҉̴̴̟̱͙̟̫̩̗͔̝͔̀Ţ̵̝̟̖̭͇̻̳͖͉̺̖̖͙͙̺̐̈́̓ͯ̆̇̋ͩ͊̄̾̾ͬ̌̚͟ͅh̡͈̗̜͙̬̗̲̦̲̟̗̦̬͓̳ͧ̋̌͂͂ͨͬͦ̿̏̈́̋ͣ̒̕͡ͅͅe̗͇̰̰̥̪̗͑̔̓́̈́ͨ̊́̿̅ͯͥ̈́͐͗͘͢͝ ̡̢̛̯͎͓̰̘͎̦̪̯̪̥̰̲͇̠̲͔ͤͤ̇̅̆̋̂̆̈́ͤ̿͑ͅW̡͓͈̲̲͉̜͔̖͈̻̱͚̿̌͗̉ͤ͢͡ͅͅa̔̾͛̅͊͋͐҉̱̹̬͍͙̻̱l̢͎̟̬̙̼̱̫̮̘̼͔̭̅ͬ͑ͣ̏̾̅̓ͣ̿ͣ̈́̕͢͡ͅͅl̡̥̣͔̭̇̒͛͒͐̄̽͛̋ͥ̌͢͟͡.̷̰̝̮͔̟̦͈̥̬̻̥̬͎͓̻̲̇ͮ̿ͨͦ̽ͫ͟͢͝͠ ̗̱͖͈͌̈ͦ͛ͮ̌͋̽̃͆̀͂ͨͧ̄̔̔ͭ̏͢Z̃̉̿ͮ̃̀͘͏͕̬̯̖͚̗͔Aͣ̑̈̓̈̑̈̀̿̚҉͙͍̦̗̦͙̠̝̩̯ͅͅL̴͖̞̞͙̱̻̥̬̜̦̐̇̉̈̽ͪ̅ͪ̂̔͌͑ͭ͐ͤ̈́̿̉͞ͅG̴̵̲̰̹̖͎͕ͯ̆̓̽͢͠Ŏ̶̡̺̼͙̣̞̩͕̥̟̝͕͔̯̞ͨ͒͊̂̊͂͗̒͆̾͆̌͆̃̎ͣͫ͜͡ͅ!̓̽̎̑̏́̓̓ͣ̀͏̱̩̭̣̹̺̗͜͞͞ Aliquam finibus metus commodo sem egestas, non mollis odio pretium. Aenean ex lectus, rutrum nec laoreet at, posuere sit amet lacus. Nulla eros augue, vehicula et molestie accumsan, dictum vel odio. In quis risus finibus, pellentesque ipsum blandit, volutpat diam. Etiam suscipit varius mollis. Proin vel luctus nisi, ac ornare justo. Integer porttitor quam magna. Donec vitae metus tempor, ultricies risus in, dictum erat. Integer porttitor faucibus vestibulum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam semper congue ante, a ultricies velit venenatis vitae. Proin non neque sit amet ex commodo congue non nec elit. Nullam vel dignissim ipsum. Duis sed lobortis ante. Aenean feugiat rutrum magna ac luctus. Ut imperdiet non ante sit amet rutrum. Cras vel massa eget nisl gravida auctor. Nulla bibendum ut tellus ut rutrum. Quisque malesuada lacinia felis, vitae semper elit. Praesent sit amet velit imperdiet, lobortis nunc at, faucibus tellus. Nullam porttitor augue mauris, a dapibus tellus ultricies et. Fusce aliquet nec velit in mattis. Sed mi ante, lacinia eget ornare vel, faucibus at metus. Pellentesque nec viverra metus. Sed aliquet pellentesque scelerisque. Duis efficitur erat sit amet dui maximus egestas. Nullam blandit ante tortor. Suspendisse vitae consectetur sem, at sollicitudin neque. Suspendisse sodales faucibus eros vitae pellentesque. Cras non quam dictum, pellentesque urna in, ornare erat. Praesent leo est, aliquet et euismod non, hendrerit sed urna. Sed convallis porttitor est, vel aliquet felis cursus ac. Vivamus feugiat eget nisi eu molestie. Phasellus tincidunt nisl eget molestie consectetur. Phasellus vitae ex ut odio sollicitudin vulputate. Sed et nulla accumsan, eleifend arcu eget, gravida neque. Donec sit amet tincidunt eros. Ut in volutpat ante.