├── .github └── workflows │ └── ci.yml ├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src ├── lib.rs └── string.rs /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - '**.rs' 7 | - '**.toml' 8 | - '.github/workflows/ci.yml' 9 | push: 10 | branches: [master] 11 | paths: 12 | - '**.rs' 13 | - '**.toml' 14 | - '.github/workflows/ci.yml' 15 | 16 | jobs: 17 | Test: 18 | strategy: 19 | fail-fast: false 20 | 21 | matrix: 22 | rust: [stable, nightly] 23 | 24 | runs-on: ubuntu-latest 25 | 26 | steps: 27 | - uses: actions/checkout@v1 28 | - uses: hecrj/setup-rust-action@v1 29 | with: 30 | rust-version: ${{ matrix.platform.rust }} 31 | 32 | - name: Build 33 | run: cargo build --verbose 34 | 35 | - name: Test 36 | run: cargo test --verbose 37 | 38 | - name: Build with std 39 | run: cargo build --verbose --features std,ffi 40 | 41 | - name: Test with std 42 | run: cargo build --verbose --features std,ffi 43 | 44 | - name: Build with serde/no_std 45 | run: cargo build --verbose --features serde 46 | 47 | - name: Test with serde/no_std 48 | run: cargo test --verbose --features serde 49 | 50 | - name: Build with serde/std 51 | run: cargo build --verbose --features serde,std 52 | 53 | - name: Test with serde/std 54 | run: cargo test --verbose --features serde,std 55 | 56 | - name: Build with union feature 57 | if: matrix.platform.rust == 'nightly' 58 | run: cargo build --verbose --features union 59 | 60 | - name: Test with with union feature 61 | if: matrix.platform.rust == 'nightly' 62 | run: cargo test --verbose --features union 63 | 64 | env: 65 | RUST_BACKTRACE: 1 66 | RUST_INCREMENTAL: 0 67 | RUSTFLAGS: "-C debuginfo=0" 68 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "smallstr" 3 | version = "0.3.0" 4 | authors = ["Murarth "] 5 | edition = "2018" 6 | 7 | description = "String-like container based on smallvec" 8 | 9 | documentation = "https://docs.rs/smallstr/" 10 | homepage = "https://github.com/murarth/smallstr" 11 | repository = "https://github.com/murarth/smallstr" 12 | 13 | categories = ["data-structures"] 14 | keywords = ["small", "str", "string"] 15 | license = "MIT OR Apache-2.0" 16 | readme = "README.md" 17 | 18 | [features] 19 | ffi = [] 20 | std = ["serde/std"] 21 | union = ["smallvec/union"] 22 | 23 | [dependencies] 24 | serde = { version = "1.0", default-features = false, features = ["alloc"], optional = true } 25 | smallvec = { version = "1.1" } 26 | 27 | [dev-dependencies] 28 | bincode = "1.0" 29 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018-2020 Murarth 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # smallstr 2 | 3 | `String`-like container based on `SmallVec` 4 | 5 | [Documentation](https://docs.rs/smallstr/) 6 | 7 | ## Building 8 | 9 | To include `smallstr` in your project, add the following to your `Cargo.toml`: 10 | 11 | ```toml 12 | [dependencies] 13 | smallstr = "0.3" 14 | ``` 15 | 16 | ## License 17 | 18 | smallstr is distributed under the terms of both the MIT license and the 19 | Apache License (Version 2.0). 20 | 21 | See LICENSE-APACHE and LICENSE-MIT for details. 22 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Implements `SmallString`, a `String`-like container for small strings 2 | //! 3 | //! ## `no_std` support 4 | //! 5 | //! By default, `smallstr` does not depend on `std`. The `std` feature may be enabled 6 | //! to add the `std` dependency. The `ffi` feature also implies `std`. 7 | //! 8 | //! ## `ffi` feature 9 | //! 10 | //! The `ffi` feature will add the following trait implementations to `SmallString`: 11 | //! 12 | //! * `PartialEq` 13 | //! * `PartialEq<&'_ OsStr>` 14 | //! * `PartialEq` 15 | //! * `PartialEq>` 16 | //! 17 | //! This feature also adds `std` as a dependency. 18 | //! 19 | //! ## `serde` support 20 | //! 21 | //! When the `serde` feature is enabled, the traits `serde::Deserialize` and 22 | //! `serde::Serialize` are implemented for `SmallString`. 23 | //! 24 | //! This feature is disabled by default. 25 | //! 26 | //! By default, the `serde` dependency is compiled with `no_std`. 27 | //! If the `std` feature is enabled, `std` is added as a dependency in `serde`, as well. 28 | //! 29 | //! ## `union` feature 30 | //! 31 | //! This feature will enable the `union` feature in `smallvec`, which reduces the size of 32 | //! a `SmallString` instance. This feature requires Rust 1.49 or newer. 33 | 34 | #![cfg_attr(not(any(feature = "ffi", feature = "std")), no_std)] 35 | #![deny(missing_docs)] 36 | 37 | extern crate alloc; 38 | 39 | pub use string::*; 40 | 41 | mod string; 42 | -------------------------------------------------------------------------------- /src/string.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | borrow::{Borrow, BorrowMut}, 3 | cmp::Ordering, 4 | fmt, 5 | hash::{Hash, Hasher}, 6 | iter::FromIterator, 7 | ops, ptr, slice, 8 | str::{self, Chars, Utf8Error}, 9 | }; 10 | 11 | use alloc::{borrow::Cow, boxed::Box, string::String}; 12 | 13 | #[cfg(feature = "ffi")] 14 | use std::ffi::{OsStr, OsString}; 15 | 16 | #[cfg(feature = "serde")] 17 | use core::marker::PhantomData; 18 | #[cfg(feature = "serde")] 19 | use serde::{ 20 | de::{Deserialize, Deserializer, Error, Visitor}, 21 | ser::{Serialize, Serializer}, 22 | }; 23 | 24 | use smallvec::{Array, SmallVec}; 25 | 26 | /// A `String`-like container that can store a small number of bytes inline. 27 | /// 28 | /// `SmallString` uses a `SmallVec<[u8; N]>` as its internal storage. 29 | #[derive(Clone, Default)] 30 | pub struct SmallString> { 31 | data: SmallVec, 32 | } 33 | 34 | impl> SmallString { 35 | /// Construct an empty string. 36 | #[inline] 37 | pub fn new() -> SmallString { 38 | SmallString { 39 | data: SmallVec::new(), 40 | } 41 | } 42 | 43 | /// Construct an empty string with enough capacity pre-allocated to store 44 | /// at least `n` bytes. 45 | /// 46 | /// Will create a heap allocation only if `n` is larger than the inline capacity. 47 | #[inline] 48 | pub fn with_capacity(n: usize) -> SmallString { 49 | SmallString { 50 | data: SmallVec::with_capacity(n), 51 | } 52 | } 53 | 54 | /// Construct a `SmallString` by copying data from a `&str`. 55 | #[inline] 56 | pub fn from_str(s: &str) -> SmallString { 57 | SmallString { 58 | data: SmallVec::from_slice(s.as_bytes()), 59 | } 60 | } 61 | 62 | /// Construct a `SmallString` by using an existing allocation. 63 | #[inline] 64 | pub fn from_string(s: String) -> SmallString { 65 | SmallString { 66 | data: SmallVec::from_vec(s.into_bytes()), 67 | } 68 | } 69 | 70 | /// Constructs a new `SmallString` on the stack using UTF-8 bytes. 71 | /// 72 | /// If the provided byte array is not valid UTF-8, an error is returned. 73 | #[inline] 74 | pub fn from_buf(buf: A) -> Result, FromUtf8Error> { 75 | let data = SmallVec::from_buf(buf); 76 | 77 | match str::from_utf8(&data) { 78 | Ok(_) => Ok(SmallString { data }), 79 | Err(error) => { 80 | let buf = data.into_inner().ok().unwrap(); 81 | 82 | Err(FromUtf8Error { buf, error }) 83 | } 84 | } 85 | } 86 | 87 | /// Constructs a new `SmallString` on the stack using the provided byte array 88 | /// without checking that the array contains valid UTF-8. 89 | /// 90 | /// # Safety 91 | /// 92 | /// This function is unsafe because it does not check that the bytes passed 93 | /// to it are valid UTF-8. If this constraint is violated, it may cause 94 | /// memory unsafety issues, as the Rust standard library functions assume 95 | /// that `&str`s are valid UTF-8. 96 | #[inline] 97 | pub unsafe fn from_buf_unchecked(buf: A) -> SmallString { 98 | SmallString { 99 | data: SmallVec::from_buf(buf), 100 | } 101 | } 102 | 103 | /// The maximum number of bytes this string can hold inline. 104 | #[inline] 105 | pub fn inline_size(&self) -> usize { 106 | A::size() 107 | } 108 | 109 | /// Returns the length of this string, in bytes. 110 | #[inline] 111 | pub fn len(&self) -> usize { 112 | self.data.len() 113 | } 114 | 115 | /// Returns `true` if this string is empty. 116 | #[inline] 117 | pub fn is_empty(&self) -> bool { 118 | self.data.is_empty() 119 | } 120 | 121 | /// Returns the number of bytes this string can hold without reallocating. 122 | #[inline] 123 | pub fn capacity(&self) -> usize { 124 | self.data.capacity() 125 | } 126 | 127 | /// Returns `true` if the data has spilled into a separate heap-allocated buffer. 128 | #[inline] 129 | pub fn spilled(&self) -> bool { 130 | self.data.spilled() 131 | } 132 | 133 | /// Empties the string and returns an iterator over its former contents. 134 | pub fn drain(&mut self) -> Drain { 135 | unsafe { 136 | let len = self.len(); 137 | 138 | self.data.set_len(0); 139 | 140 | let ptr = self.as_ptr(); 141 | 142 | let slice = slice::from_raw_parts(ptr, len); 143 | let s = str::from_utf8_unchecked(slice); 144 | 145 | Drain { iter: s.chars() } 146 | } 147 | } 148 | 149 | /// Appends the given `char` to the end of this string. 150 | /// 151 | /// # Examples 152 | /// 153 | /// ``` 154 | /// use smallstr::SmallString; 155 | /// 156 | /// let mut s: SmallString<[u8; 8]> = SmallString::from("foo"); 157 | /// 158 | /// s.push('x'); 159 | /// 160 | /// assert_eq!(s, "foox"); 161 | /// ``` 162 | #[inline] 163 | pub fn push(&mut self, ch: char) { 164 | match ch.len_utf8() { 165 | 1 => self.data.push(ch as u8), 166 | _ => self.push_str(ch.encode_utf8(&mut [0; 4])), 167 | } 168 | } 169 | 170 | /// Appends the given string slice to the end of this string. 171 | /// 172 | /// # Examples 173 | /// 174 | /// ``` 175 | /// use smallstr::SmallString; 176 | /// 177 | /// let mut s: SmallString<[u8; 8]> = SmallString::from("foo"); 178 | /// 179 | /// s.push_str("bar"); 180 | /// 181 | /// assert_eq!(s, "foobar"); 182 | /// ``` 183 | #[inline] 184 | pub fn push_str(&mut self, s: &str) { 185 | self.data.extend_from_slice(s.as_bytes()); 186 | } 187 | 188 | /// Removes the last character from this string and returns it. 189 | /// 190 | /// Returns `None` if the string is empty. 191 | #[inline] 192 | pub fn pop(&mut self) -> Option { 193 | match self.chars().next_back() { 194 | Some(ch) => unsafe { 195 | let new_len = self.len() - ch.len_utf8(); 196 | self.data.set_len(new_len); 197 | Some(ch) 198 | }, 199 | None => None, 200 | } 201 | } 202 | 203 | /// Reallocates to set the new capacity to `new_cap`. 204 | /// 205 | /// # Panics 206 | /// 207 | /// If `new_cap` is less than the current length. 208 | #[inline] 209 | pub fn grow(&mut self, new_cap: usize) { 210 | self.data.grow(new_cap); 211 | } 212 | 213 | /// Ensures that this string's capacity is at least `additional` bytes larger 214 | /// than its length. 215 | /// 216 | /// The capacity may be increased by more than `additional` bytes in order to 217 | /// prevent frequent reallocations. 218 | #[inline] 219 | pub fn reserve(&mut self, additional: usize) { 220 | self.data.reserve(additional); 221 | } 222 | 223 | /// Ensures that this string's capacity is `additional` bytes larger than 224 | /// its length. 225 | #[inline] 226 | pub fn reserve_exact(&mut self, additional: usize) { 227 | self.data.reserve(additional); 228 | } 229 | 230 | /// Shrink the capacity of the string as much as possible. 231 | /// 232 | /// When possible, this will move the data from an external heap buffer 233 | /// to the string's inline storage. 234 | #[inline] 235 | pub fn shrink_to_fit(&mut self) { 236 | self.data.shrink_to_fit(); 237 | } 238 | 239 | /// Shorten the string, keeping the first `len` bytes. 240 | /// 241 | /// This does not reallocate. If you want to shrink the string's capacity, 242 | /// use `shrink_to_fit` after truncating. 243 | /// 244 | /// # Panics 245 | /// 246 | /// If `len` does not lie on a `char` boundary. 247 | #[inline] 248 | pub fn truncate(&mut self, len: usize) { 249 | assert!(self.is_char_boundary(len)); 250 | self.data.truncate(len); 251 | } 252 | 253 | /// Extracts a string slice containing the entire string. 254 | #[inline] 255 | pub fn as_str(&self) -> &str { 256 | self 257 | } 258 | 259 | /// Extracts a string slice containing the entire string. 260 | #[inline] 261 | pub fn as_mut_str(&mut self) -> &mut str { 262 | self 263 | } 264 | 265 | /// Removes all contents of the string. 266 | #[inline] 267 | pub fn clear(&mut self) { 268 | self.data.clear(); 269 | } 270 | 271 | /// Removes a `char` from this string at a byte position and returns it. 272 | /// 273 | /// # Panics 274 | /// 275 | /// If `idx` does not lie on a `char` boundary. 276 | #[inline] 277 | pub fn remove(&mut self, idx: usize) -> char { 278 | let ch = match self[idx..].chars().next() { 279 | Some(ch) => ch, 280 | None => panic!("cannot remove a char from the end of a string"), 281 | }; 282 | 283 | let ch_len = ch.len_utf8(); 284 | let next = idx + ch_len; 285 | let len = self.len(); 286 | 287 | unsafe { 288 | ptr::copy( 289 | self.as_ptr().add(next), 290 | self.as_mut_ptr().add(idx), 291 | len - next, 292 | ); 293 | self.data.set_len(len - ch_len); 294 | } 295 | 296 | ch 297 | } 298 | 299 | /// Inserts a `char` into this string at the given byte position. 300 | /// 301 | /// # Panics 302 | /// 303 | /// If `idx` does not lie on `char` boundaries. 304 | #[inline] 305 | pub fn insert(&mut self, idx: usize, ch: char) { 306 | assert!(self.is_char_boundary(idx)); 307 | 308 | match ch.len_utf8() { 309 | 1 => self.data.insert(idx, ch as u8), 310 | _ => self.insert_str(idx, ch.encode_utf8(&mut [0; 4])), 311 | } 312 | } 313 | 314 | /// Inserts a `&str` into this string at the given byte position. 315 | /// 316 | /// # Panics 317 | /// 318 | /// If `idx` does not lie on `char` boundaries. 319 | #[inline] 320 | pub fn insert_str(&mut self, idx: usize, s: &str) { 321 | assert!(self.is_char_boundary(idx)); 322 | 323 | let len = self.len(); 324 | let amt = s.len(); 325 | 326 | self.data.reserve(amt); 327 | 328 | unsafe { 329 | ptr::copy( 330 | self.as_ptr().add(idx), 331 | self.as_mut_ptr().add(idx + amt), 332 | len - idx, 333 | ); 334 | ptr::copy_nonoverlapping(s.as_ptr(), self.as_mut_ptr().add(idx), amt); 335 | self.data.set_len(len + amt); 336 | } 337 | } 338 | 339 | /// Returns a mutable reference to the contents of the `SmallString`. 340 | /// 341 | /// # Safety 342 | /// 343 | /// This function is unsafe because it does not check that the bytes passed 344 | /// to it are valid UTF-8. If this constraint is violated, it may cause 345 | /// memory unsafety issues, as the Rust standard library functions assume 346 | /// that `&str`s are valid UTF-8. 347 | #[inline] 348 | pub unsafe fn as_mut_vec(&mut self) -> &mut SmallVec { 349 | &mut self.data 350 | } 351 | 352 | /// Converts the `SmallString` into a `String`, without reallocating if the 353 | /// `SmallString` has already spilled onto the heap. 354 | #[inline] 355 | pub fn into_string(self) -> String { 356 | unsafe { String::from_utf8_unchecked(self.data.into_vec()) } 357 | } 358 | 359 | /// Converts the `SmallString` into a `Box`, without reallocating if the 360 | /// `SmallString` has already spilled onto the heap. 361 | /// 362 | /// Note that this will drop excess capacity. 363 | #[inline] 364 | pub fn into_boxed_str(self) -> Box { 365 | self.into_string().into_boxed_str() 366 | } 367 | 368 | /// Convert the `SmallString` into `A`, if possible. Otherwise, return `Err(self)`. 369 | /// 370 | /// This method returns `Err(self)` if the `SmallString` is too short 371 | /// (and the `A` contains uninitialized elements) or if the `SmallString` is too long 372 | /// (and the elements have been spilled to the heap). 373 | #[inline] 374 | pub fn into_inner(self) -> Result { 375 | self.data.into_inner().map_err(|data| SmallString { data }) 376 | } 377 | 378 | /// Retains only the characters specified by the predicate. 379 | /// 380 | /// In other words, removes all characters `c` such that `f(c)` returns `false`. 381 | /// This method operates in place and preserves the order of retained 382 | /// characters. 383 | /// 384 | /// # Examples 385 | /// 386 | /// ``` 387 | /// use smallstr::SmallString; 388 | /// 389 | /// let mut s: SmallString<[u8; 16]> = SmallString::from("f_o_ob_ar"); 390 | /// 391 | /// s.retain(|c| c != '_'); 392 | /// 393 | /// assert_eq!(s, "foobar"); 394 | /// ``` 395 | #[inline] 396 | pub fn retain bool>(&mut self, mut f: F) { 397 | struct SetLenOnDrop<'a, A: Array> { 398 | s: &'a mut SmallString, 399 | idx: usize, 400 | del_bytes: usize, 401 | } 402 | 403 | impl<'a, A: Array> Drop for SetLenOnDrop<'a, A> { 404 | fn drop(&mut self) { 405 | let new_len = self.idx - self.del_bytes; 406 | debug_assert!(new_len <= self.s.len()); 407 | unsafe { self.s.data.set_len(new_len) }; 408 | } 409 | } 410 | 411 | let len = self.len(); 412 | let mut guard = SetLenOnDrop { 413 | s: self, 414 | idx: 0, 415 | del_bytes: 0, 416 | }; 417 | 418 | while guard.idx < len { 419 | let ch = unsafe { 420 | guard 421 | .s 422 | .get_unchecked(guard.idx..len) 423 | .chars() 424 | .next() 425 | .unwrap() 426 | }; 427 | let ch_len = ch.len_utf8(); 428 | 429 | if !f(ch) { 430 | guard.del_bytes += ch_len; 431 | } else if guard.del_bytes > 0 { 432 | unsafe { 433 | ptr::copy( 434 | guard.s.data.as_ptr().add(guard.idx), 435 | guard.s.data.as_mut_ptr().add(guard.idx - guard.del_bytes), 436 | ch_len, 437 | ); 438 | } 439 | } 440 | 441 | // Point idx to the next char 442 | guard.idx += ch_len; 443 | } 444 | 445 | drop(guard); 446 | } 447 | 448 | fn as_mut_ptr(&mut self) -> *mut u8 { 449 | self.as_ptr() as *mut u8 450 | } 451 | } 452 | 453 | impl> ops::Deref for SmallString { 454 | type Target = str; 455 | 456 | #[inline] 457 | fn deref(&self) -> &str { 458 | let bytes: &[u8] = &self.data; 459 | unsafe { str::from_utf8_unchecked(bytes) } 460 | } 461 | } 462 | 463 | impl> ops::DerefMut for SmallString { 464 | #[inline] 465 | fn deref_mut(&mut self) -> &mut str { 466 | let bytes: &mut [u8] = &mut self.data; 467 | unsafe { str::from_utf8_unchecked_mut(bytes) } 468 | } 469 | } 470 | 471 | impl> AsRef for SmallString { 472 | #[inline] 473 | fn as_ref(&self) -> &str { 474 | self 475 | } 476 | } 477 | 478 | impl> AsMut for SmallString { 479 | #[inline] 480 | fn as_mut(&mut self) -> &mut str { 481 | self 482 | } 483 | } 484 | 485 | impl> Borrow for SmallString { 486 | #[inline] 487 | fn borrow(&self) -> &str { 488 | self 489 | } 490 | } 491 | 492 | impl> BorrowMut for SmallString { 493 | #[inline] 494 | fn borrow_mut(&mut self) -> &mut str { 495 | self 496 | } 497 | } 498 | 499 | impl> AsRef<[u8]> for SmallString { 500 | #[inline] 501 | fn as_ref(&self) -> &[u8] { 502 | self.data.as_ref() 503 | } 504 | } 505 | 506 | impl> fmt::Write for SmallString { 507 | #[inline] 508 | fn write_str(&mut self, s: &str) -> fmt::Result { 509 | self.push_str(s); 510 | Ok(()) 511 | } 512 | 513 | #[inline] 514 | fn write_char(&mut self, ch: char) -> fmt::Result { 515 | self.push(ch); 516 | Ok(()) 517 | } 518 | } 519 | 520 | #[cfg(feature = "serde")] 521 | impl> Serialize for SmallString { 522 | fn serialize(&self, serializer: S) -> Result { 523 | serializer.serialize_str(self) 524 | } 525 | } 526 | 527 | #[cfg(feature = "serde")] 528 | impl<'de, A: Array> Deserialize<'de> for SmallString { 529 | fn deserialize>(deserializer: D) -> Result { 530 | deserializer.deserialize_str(SmallStringVisitor { 531 | phantom: PhantomData, 532 | }) 533 | } 534 | } 535 | 536 | #[cfg(feature = "serde")] 537 | struct SmallStringVisitor { 538 | phantom: PhantomData, 539 | } 540 | 541 | #[cfg(feature = "serde")] 542 | impl<'de, A: Array> Visitor<'de> for SmallStringVisitor { 543 | type Value = SmallString; 544 | 545 | fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { 546 | f.write_str("a string") 547 | } 548 | 549 | fn visit_str(self, v: &str) -> Result { 550 | Ok(v.into()) 551 | } 552 | 553 | fn visit_string(self, v: String) -> Result { 554 | Ok(v.into()) 555 | } 556 | } 557 | 558 | impl> From for SmallString { 559 | #[inline] 560 | fn from(ch: char) -> SmallString { 561 | SmallString::from_str(ch.encode_utf8(&mut [0; 4])) 562 | } 563 | } 564 | 565 | impl<'a, A: Array> From<&'a str> for SmallString { 566 | #[inline] 567 | fn from(s: &str) -> SmallString { 568 | SmallString::from_str(s) 569 | } 570 | } 571 | 572 | impl> From> for SmallString { 573 | #[inline] 574 | fn from(s: Box) -> SmallString { 575 | SmallString::from_string(s.into()) 576 | } 577 | } 578 | 579 | impl> From for SmallString { 580 | #[inline] 581 | fn from(s: String) -> SmallString { 582 | SmallString::from_string(s) 583 | } 584 | } 585 | 586 | impl<'a, A: Array> From> for SmallString { 587 | fn from(value: Cow<'a, str>) -> Self { 588 | match value { 589 | Cow::Borrowed(s) => Self::from_str(s), 590 | Cow::Owned(s) => Self::from_string(s), 591 | } 592 | } 593 | } 594 | 595 | macro_rules! impl_index_str { 596 | ($index_type: ty) => { 597 | impl> ops::Index<$index_type> for SmallString { 598 | type Output = str; 599 | 600 | #[inline] 601 | fn index(&self, index: $index_type) -> &str { 602 | &self.as_str()[index] 603 | } 604 | } 605 | 606 | impl> ops::IndexMut<$index_type> for SmallString { 607 | #[inline] 608 | fn index_mut(&mut self, index: $index_type) -> &mut str { 609 | &mut self.as_mut_str()[index] 610 | } 611 | } 612 | }; 613 | } 614 | 615 | impl_index_str!(ops::Range); 616 | impl_index_str!(ops::RangeFrom); 617 | impl_index_str!(ops::RangeTo); 618 | impl_index_str!(ops::RangeFull); 619 | 620 | impl> FromIterator for SmallString { 621 | fn from_iter>(iter: I) -> SmallString { 622 | let mut s = SmallString::new(); 623 | s.extend(iter); 624 | s 625 | } 626 | } 627 | 628 | impl<'a, A: Array> FromIterator<&'a char> for SmallString { 629 | fn from_iter>(iter: I) -> SmallString { 630 | let mut s = SmallString::new(); 631 | s.extend(iter.into_iter().cloned()); 632 | s 633 | } 634 | } 635 | 636 | impl<'a, A: Array> FromIterator> for SmallString { 637 | fn from_iter>>(iter: I) -> SmallString { 638 | let mut s = SmallString::new(); 639 | s.extend(iter); 640 | s 641 | } 642 | } 643 | 644 | impl<'a, A: Array> FromIterator<&'a str> for SmallString { 645 | fn from_iter>(iter: I) -> SmallString { 646 | let mut s = SmallString::new(); 647 | s.extend(iter); 648 | s 649 | } 650 | } 651 | 652 | impl> FromIterator for SmallString { 653 | fn from_iter>(iter: I) -> SmallString { 654 | let mut s = SmallString::new(); 655 | s.extend(iter); 656 | s 657 | } 658 | } 659 | 660 | impl> Extend for SmallString { 661 | fn extend>(&mut self, iter: I) { 662 | let iter = iter.into_iter(); 663 | let (lo, _) = iter.size_hint(); 664 | 665 | self.reserve(lo); 666 | 667 | for ch in iter { 668 | self.push(ch); 669 | } 670 | } 671 | } 672 | 673 | impl<'a, A: Array> Extend<&'a char> for SmallString { 674 | fn extend>(&mut self, iter: I) { 675 | self.extend(iter.into_iter().cloned()); 676 | } 677 | } 678 | 679 | impl<'a, A: Array> Extend> for SmallString { 680 | fn extend>>(&mut self, iter: I) { 681 | for s in iter { 682 | self.push_str(&s); 683 | } 684 | } 685 | } 686 | 687 | impl<'a, A: Array> Extend<&'a str> for SmallString { 688 | fn extend>(&mut self, iter: I) { 689 | for s in iter { 690 | self.push_str(s); 691 | } 692 | } 693 | } 694 | 695 | impl> Extend for SmallString { 696 | fn extend>(&mut self, iter: I) { 697 | for s in iter { 698 | self.push_str(&s); 699 | } 700 | } 701 | } 702 | 703 | impl> fmt::Debug for SmallString { 704 | #[inline] 705 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 706 | fmt::Debug::fmt(&**self, f) 707 | } 708 | } 709 | 710 | impl> fmt::Display for SmallString { 711 | #[inline] 712 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 713 | fmt::Display::fmt(&**self, f) 714 | } 715 | } 716 | 717 | macro_rules! eq_str { 718 | ( $rhs:ty ) => { 719 | impl<'a, A: Array> PartialEq<$rhs> for SmallString { 720 | #[inline] 721 | fn eq(&self, rhs: &$rhs) -> bool { 722 | &self[..] == &rhs[..] 723 | } 724 | 725 | #[inline] 726 | fn ne(&self, rhs: &$rhs) -> bool { 727 | &self[..] != &rhs[..] 728 | } 729 | } 730 | }; 731 | } 732 | 733 | eq_str!(str); 734 | eq_str!(&'a str); 735 | eq_str!(String); 736 | eq_str!(Cow<'a, str>); 737 | 738 | #[cfg(feature = "ffi")] 739 | impl> PartialEq for SmallString { 740 | #[inline] 741 | fn eq(&self, rhs: &OsStr) -> bool { 742 | &self[..] == rhs 743 | } 744 | 745 | #[inline] 746 | fn ne(&self, rhs: &OsStr) -> bool { 747 | &self[..] != rhs 748 | } 749 | } 750 | 751 | #[cfg(feature = "ffi")] 752 | impl<'a, A: Array> PartialEq<&'a OsStr> for SmallString { 753 | #[inline] 754 | fn eq(&self, rhs: &&OsStr) -> bool { 755 | &self[..] == *rhs 756 | } 757 | 758 | #[inline] 759 | fn ne(&self, rhs: &&OsStr) -> bool { 760 | &self[..] != *rhs 761 | } 762 | } 763 | 764 | #[cfg(feature = "ffi")] 765 | impl> PartialEq for SmallString { 766 | #[inline] 767 | fn eq(&self, rhs: &OsString) -> bool { 768 | &self[..] == rhs 769 | } 770 | 771 | #[inline] 772 | fn ne(&self, rhs: &OsString) -> bool { 773 | &self[..] != rhs 774 | } 775 | } 776 | 777 | #[cfg(feature = "ffi")] 778 | impl<'a, A: Array> PartialEq> for SmallString { 779 | #[inline] 780 | fn eq(&self, rhs: &Cow) -> bool { 781 | self[..] == **rhs 782 | } 783 | 784 | #[inline] 785 | fn ne(&self, rhs: &Cow) -> bool { 786 | self[..] != **rhs 787 | } 788 | } 789 | 790 | impl PartialEq> for SmallString 791 | where 792 | A: Array, 793 | B: Array, 794 | { 795 | #[inline] 796 | fn eq(&self, rhs: &SmallString) -> bool { 797 | &self[..] == &rhs[..] 798 | } 799 | 800 | #[inline] 801 | fn ne(&self, rhs: &SmallString) -> bool { 802 | &self[..] != &rhs[..] 803 | } 804 | } 805 | 806 | impl> Eq for SmallString {} 807 | 808 | impl> PartialOrd for SmallString { 809 | #[inline] 810 | fn partial_cmp(&self, rhs: &SmallString) -> Option { 811 | self[..].partial_cmp(&rhs[..]) 812 | } 813 | } 814 | 815 | impl> Ord for SmallString { 816 | #[inline] 817 | fn cmp(&self, rhs: &SmallString) -> Ordering { 818 | self[..].cmp(&rhs[..]) 819 | } 820 | } 821 | 822 | impl> Hash for SmallString { 823 | #[inline] 824 | fn hash(&self, state: &mut H) { 825 | self[..].hash(state) 826 | } 827 | } 828 | 829 | /// A draining iterator for `SmallString`. 830 | /// 831 | /// This struct is created by the [`drain`] method on [`SmallString`]. 832 | /// 833 | /// [`drain`]: struct.SmallString.html#method.drain 834 | /// [`SmallString`]: struct.SmallString.html 835 | pub struct Drain<'a> { 836 | iter: Chars<'a>, 837 | } 838 | 839 | impl<'a> Iterator for Drain<'a> { 840 | type Item = char; 841 | 842 | #[inline] 843 | fn next(&mut self) -> Option { 844 | self.iter.next() 845 | } 846 | 847 | #[inline] 848 | fn size_hint(&self) -> (usize, Option) { 849 | self.iter.size_hint() 850 | } 851 | } 852 | 853 | impl<'a> DoubleEndedIterator for Drain<'a> { 854 | #[inline] 855 | fn next_back(&mut self) -> Option { 856 | self.iter.next_back() 857 | } 858 | } 859 | 860 | /// A possible error value when creating a `SmallString` from a byte array. 861 | /// 862 | /// This type is the error type for the [`from_buf`] method on [`SmallString`]. 863 | /// 864 | /// [`from_buf`]: struct.SmallString.html#method.from_buf 865 | /// [`SmallString`]: struct.SmallString.html 866 | #[derive(Debug)] 867 | pub struct FromUtf8Error> { 868 | buf: A, 869 | error: Utf8Error, 870 | } 871 | 872 | impl> FromUtf8Error { 873 | /// Returns the slice of `[u8]` bytes that were attempted to convert to a `SmallString`. 874 | #[inline] 875 | pub fn as_bytes(&self) -> &[u8] { 876 | let ptr = &self.buf as *const _ as *const u8; 877 | unsafe { slice::from_raw_parts(ptr, A::size()) } 878 | } 879 | 880 | /// Returns the byte array that was attempted to convert into a `SmallString`. 881 | #[inline] 882 | pub fn into_buf(self) -> A { 883 | self.buf 884 | } 885 | 886 | /// Returns the `Utf8Error` to get more details about the conversion failure. 887 | #[inline] 888 | pub fn utf8_error(&self) -> Utf8Error { 889 | self.error 890 | } 891 | } 892 | 893 | impl> fmt::Display for FromUtf8Error { 894 | #[inline] 895 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 896 | fmt::Display::fmt(&self.error, f) 897 | } 898 | } 899 | 900 | #[cfg(test)] 901 | mod test { 902 | use alloc::{ 903 | borrow::{Cow, ToOwned}, 904 | string::{String, ToString}, 905 | }; 906 | 907 | use super::SmallString; 908 | 909 | #[test] 910 | fn test_drain() { 911 | let mut s: SmallString<[u8; 2]> = SmallString::new(); 912 | 913 | s.push('a'); 914 | assert_eq!(s.drain().collect::(), "a"); 915 | assert!(s.is_empty()); 916 | 917 | // spilling the vec 918 | s.push('x'); 919 | s.push('y'); 920 | s.push('z'); 921 | 922 | assert_eq!(s.drain().collect::(), "xyz"); 923 | assert!(s.is_empty()); 924 | } 925 | 926 | #[test] 927 | fn test_drain_rev() { 928 | let mut s: SmallString<[u8; 2]> = SmallString::new(); 929 | 930 | s.push('a'); 931 | assert_eq!(s.drain().rev().collect::(), "a"); 932 | assert!(s.is_empty()); 933 | 934 | // spilling the vec 935 | s.push('x'); 936 | s.push('y'); 937 | s.push('z'); 938 | 939 | assert_eq!(s.drain().rev().collect::(), "zyx"); 940 | assert!(s.is_empty()); 941 | } 942 | 943 | #[test] 944 | fn test_eq() { 945 | let s: SmallString<[u8; 4]> = SmallString::from("foo"); 946 | 947 | assert_eq!(s, *"foo"); 948 | assert_eq!(s, "foo"); 949 | assert_eq!(s, "foo".to_owned()); 950 | assert_eq!(s, Cow::Borrowed("foo")); 951 | } 952 | 953 | #[cfg(feature = "ffi")] 954 | #[test] 955 | fn test_eq_os_str() { 956 | use std::ffi::OsStr; 957 | 958 | let s: SmallString<[u8; 4]> = SmallString::from("foo"); 959 | let os_s: &OsStr = "foo".as_ref(); 960 | 961 | assert_eq!(s, os_s); 962 | assert_eq!(s, *os_s); 963 | assert_eq!(s, os_s.to_owned()); 964 | assert_eq!(s, Cow::Borrowed(os_s)); 965 | } 966 | 967 | #[test] 968 | fn test_from_buf() { 969 | let s: SmallString<[u8; 2]> = SmallString::from_buf([206, 177]).unwrap(); 970 | assert_eq!(s, "α"); 971 | 972 | assert!(SmallString::<[u8; 2]>::from_buf([206, 0]).is_err()); 973 | } 974 | 975 | #[test] 976 | fn test_insert() { 977 | let mut s: SmallString<[u8; 8]> = SmallString::from("abc"); 978 | 979 | s.insert(1, 'x'); 980 | assert_eq!(s, "axbc"); 981 | 982 | s.insert(3, 'α'); 983 | assert_eq!(s, "axbαc"); 984 | 985 | s.insert_str(0, "foo"); 986 | assert_eq!(s, "fooaxbαc"); 987 | } 988 | 989 | #[test] 990 | #[should_panic] 991 | fn test_insert_panic() { 992 | let mut s: SmallString<[u8; 8]> = SmallString::from("αβγ"); 993 | 994 | s.insert(1, 'x'); 995 | } 996 | 997 | #[test] 998 | fn test_into_string() { 999 | let s: SmallString<[u8; 2]> = SmallString::from("foo"); 1000 | assert_eq!(s.into_string(), "foo"); 1001 | 1002 | let s: SmallString<[u8; 8]> = SmallString::from("foo"); 1003 | assert_eq!(s.into_string(), "foo"); 1004 | } 1005 | 1006 | #[test] 1007 | fn test_to_string() { 1008 | let s: SmallString<[u8; 2]> = SmallString::from("foo"); 1009 | assert_eq!(s.to_string(), "foo"); 1010 | 1011 | let s: SmallString<[u8; 8]> = SmallString::from("foo"); 1012 | assert_eq!(s.to_string(), "foo"); 1013 | } 1014 | 1015 | #[test] 1016 | fn test_pop() { 1017 | let mut s: SmallString<[u8; 8]> = SmallString::from("αβγ"); 1018 | 1019 | assert_eq!(s.pop(), Some('γ')); 1020 | assert_eq!(s.pop(), Some('β')); 1021 | assert_eq!(s.pop(), Some('α')); 1022 | assert_eq!(s.pop(), None); 1023 | } 1024 | 1025 | #[test] 1026 | fn test_remove() { 1027 | let mut s: SmallString<[u8; 8]> = SmallString::from("αβγ"); 1028 | 1029 | assert_eq!(s.remove(2), 'β'); 1030 | assert_eq!(s, "αγ"); 1031 | 1032 | assert_eq!(s.remove(0), 'α'); 1033 | assert_eq!(s, "γ"); 1034 | 1035 | assert_eq!(s.remove(0), 'γ'); 1036 | assert_eq!(s, ""); 1037 | } 1038 | 1039 | #[test] 1040 | #[should_panic] 1041 | fn test_remove_panic_0() { 1042 | let mut s: SmallString<[u8; 8]> = SmallString::from("foo"); 1043 | 1044 | // Attempt to remove at the end 1045 | s.remove(3); 1046 | } 1047 | 1048 | #[test] 1049 | #[should_panic] 1050 | fn test_remove_panic_1() { 1051 | let mut s: SmallString<[u8; 8]> = SmallString::from("αβγ"); 1052 | 1053 | // Attempt to remove mid-character 1054 | s.remove(1); 1055 | } 1056 | 1057 | #[test] 1058 | fn test_retain() { 1059 | let mut s: SmallString<[u8; 8]> = SmallString::from("α_β_γ"); 1060 | 1061 | s.retain(|_| true); 1062 | assert_eq!(s, "α_β_γ"); 1063 | 1064 | s.retain(|c| c != '_'); 1065 | assert_eq!(s, "αβγ"); 1066 | 1067 | s.retain(|c| c != 'β'); 1068 | assert_eq!(s, "αγ"); 1069 | 1070 | s.retain(|c| c == 'α'); 1071 | assert_eq!(s, "α"); 1072 | 1073 | s.retain(|_| false); 1074 | assert_eq!(s, ""); 1075 | } 1076 | 1077 | #[test] 1078 | fn test_truncate() { 1079 | let mut s: SmallString<[u8; 2]> = SmallString::from("foobar"); 1080 | 1081 | s.truncate(6); 1082 | assert_eq!(s, "foobar"); 1083 | 1084 | s.truncate(3); 1085 | assert_eq!(s, "foo"); 1086 | } 1087 | 1088 | #[test] 1089 | #[should_panic] 1090 | fn test_truncate_panic() { 1091 | let mut s: SmallString<[u8; 2]> = SmallString::from("α"); 1092 | 1093 | s.truncate(1); 1094 | } 1095 | 1096 | #[test] 1097 | fn test_write() { 1098 | use core::fmt::Write; 1099 | 1100 | let mut s: SmallString<[u8; 8]> = SmallString::from("foo"); 1101 | 1102 | write!(s, "bar").unwrap(); 1103 | 1104 | assert_eq!(s, "foobar"); 1105 | } 1106 | 1107 | #[cfg(feature = "serde")] 1108 | #[test] 1109 | fn test_serde() { 1110 | use bincode::{deserialize, serialize}; 1111 | 1112 | let mut small_str: SmallString<[u8; 4]> = SmallString::from("foo"); 1113 | 1114 | let encoded = serialize(&small_str).unwrap(); 1115 | let decoded: SmallString<[u8; 4]> = deserialize(&encoded).unwrap(); 1116 | 1117 | assert_eq!(small_str, decoded); 1118 | 1119 | // Spill the vec 1120 | small_str.push_str("bar"); 1121 | 1122 | // Check again after spilling. 1123 | let encoded = serialize(&small_str).unwrap(); 1124 | let decoded: SmallString<[u8; 4]> = deserialize(&encoded).unwrap(); 1125 | 1126 | assert_eq!(small_str, decoded); 1127 | } 1128 | } 1129 | --------------------------------------------------------------------------------