inlinable_string-0.1.15/.cargo_vcs_info.json0000644000000001360000000000100144430ustar { "git": { "sha1": "f7c0a330e1ebc5223925e8c956f5e7075c13b182" }, "path_in_vcs": "" }inlinable_string-0.1.15/.gitignore000064400000000000000000000000220072674642500152450ustar 00000000000000target Cargo.lock inlinable_string-0.1.15/.travis.yml000064400000000000000000000023270072674642500154000ustar 00000000000000sudo: false language: rust addons: apt: packages: - libcurl4-openssl-dev - libelf-dev - libdw-dev - binutils-dev rust: # - nightly - beta - stable before_script: - | pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH script: - | travis-cargo test -- --features serde && travis-cargo bench && travis-cargo --only stable doc after_success: - travis-cargo --only stable doc-upload - travis-cargo coveralls --no-sudo --verify env: global: - TRAVIS_CARGO_NIGHTLY_FEATURE=nightly - secure: ZtdohAQFnky0rW44bPjPGEEHtYOffjh6PDhsyZzFCQfut2UMtlAZ506/l07gyFvpzMK+FFTtOnnJSUpsiu7Ypsa6OtxGDd2iVbZ4vVVitlCTXbSb1R5AUARXCd8F2RlXG6MNmZ7nZc/OAqjQIngNEM44yq+a+5loabU7QtX9a/cw9wDdhFFTch9pwhttQehSJqhsf8dB19W4TiV+pCEgvawede8dP0Gv5YJ5BsU4Jzxwr/tuUOuYPhDVBaSw0Uvyc7iAIq5WNz3STodZhfNeMj4kP4Uy7CD17mHtS1IzF2ftF3hPC0/ALAPXmtpPMJSLzaH1I2yZXiujfSowEAKJY3XvYWoX8pRFT0gzuF6A1WHqCWDgzyZSoRwkWWNYRWMBeX58SMoB48Wj4ZUz6hDwwtvKAmNOBz4ieixEuJuxP4flC17lwt/mX5sYw8fLA0ttuZ8GjFIfmkXI6v3imb40Z8+sdZ0SW/TH8xxiRllqdmJ0Pwes7bgcdvzgWnuNrb6l6dSNyyU4QGWQQrQ6BmR9qAPKiK8blK7bJgsI4J/tj+BR5DjSx+jpCwUjDx9WsXSkwxp0cXM8cR4504S+1n0+efGqdhiq6k6ELVAplWEFRrEA+QSmuVHVb1asTVuUMS4gsA+Uh+fpgU0RxQp/LzXkAEjoKAu2IgjRJOPbWxyP1mY= inlinable_string-0.1.15/COPYRIGHT000064400000000000000000000017100072674642500145550ustar 00000000000000Short version for non-lawyers: The inlinable_string crate is dual-licensed under Apache 2.0 and MIT terms. Longer version: The inlinable_string crate is copyright 2015, The inlinable_string crate Developers (given in the file Cargo.toml). Licensed under the Apache License, Version 2.0 or the MIT license , at your option. All files in the project carrying such notice may not be copied, modified, or distributed except according to those terms. Portions of this code, its interfaces, and documentation are forked from https://github.com/rust-lang/rust/blob/master/src/libcollections/string.rs which is copyright 2014, The Rust Project Developers and licensed under the Apache License, Version 2.0 or the MIT license , at your option. inlinable_string-0.1.15/Cargo.toml0000644000000024200000000000100124370ustar # 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] edition = "2018" name = "inlinable_string" version = "0.1.15" authors = ["Nick Fitzgerald "] description = "The `inlinable_string` crate provides the `InlinableString` type -- an owned, grow-able UTF-8 string that stores small strings inline and avoids heap-allocation -- and the `StringExt` trait which abstracts string operations over both `std::string::String` and `InlinableString` (or even your own custom string type)." documentation = "http://fitzgen.github.io/inlinable_string/inlinable_string/index.html" readme = "./README.md" keywords = ["string", "inline", "inlinable"] license = "Apache-2.0/MIT" repository = "https://github.com/fitzgen/inlinable_string" [dependencies.serde] version = "1" optional = true [dev-dependencies.serde_test] version = "1" [features] nightly = [] no_std = [] inlinable_string-0.1.15/Cargo.toml.orig000064400000000000000000000015110072674642500161500ustar 00000000000000[package] authors = ["Nick Fitzgerald "] name = "inlinable_string" description = "The `inlinable_string` crate provides the `InlinableString` type -- an owned, grow-able UTF-8 string that stores small strings inline and avoids heap-allocation -- and the `StringExt` trait which abstracts string operations over both `std::string::String` and `InlinableString` (or even your own custom string type)." version = "0.1.15" edition = "2018" license = "Apache-2.0/MIT" keywords = ["string", "inline", "inlinable"] readme = "./README.md" documentation = "http://fitzgen.github.io/inlinable_string/inlinable_string/index.html" repository = "https://github.com/fitzgen/inlinable_string" [dependencies] [dependencies.serde] optional = true version = "1" [features] nightly = [] no_std = [] [dev-dependencies] serde_test = "1" inlinable_string-0.1.15/LICENSE-APACHE000064400000000000000000000251370072674642500152170ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. inlinable_string-0.1.15/LICENSE-MIT000064400000000000000000000020570072674642500147230ustar 00000000000000Copyright (c) 2015 The Rust Project Developers 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. inlinable_string-0.1.15/README.md000064400000000000000000000074530072674642500145530ustar 00000000000000# `inlinable_string` [![](http://meritbadge.herokuapp.com/inlinable_string)![](https://img.shields.io/crates/d/inlinable_string.png)](https://crates.io/crates/inlinable_string) [![Build Status](https://travis-ci.org/fitzgen/inlinable_string.png?branch=master)](https://travis-ci.org/fitzgen/inlinable_string) [![Coverage Status](https://coveralls.io/repos/fitzgen/inlinable_string/badge.svg?branch=master&service=github)](https://coveralls.io/github/fitzgen/inlinable_string?branch=master) The `inlinable_string` crate provides the `InlinableString` type — an owned, grow-able UTF-8 string that stores small strings inline and avoids heap-allocation — and the `StringExt` trait which abstracts string operations over both `std::string::String` and `InlinableString` (or even your own custom string type). `StringExt`'s API is mostly identical to `std::string::String`; unstable and deprecated methods are not included. A `StringExt` implementation is provided for both `std::string::String` and `InlinableString`. This enables `InlinableString` to generally work as a drop-in replacement for `std::string::String` and `&StringExt` to work with references to either type. ## But is it actually faster than using `std::string::String`? Here are some current (micro)benchmark results. I encourage you to verify them yourself by running `cargo bench --feature nightly` with a nightly Rust! I am also very open to adding more realistic and representative benchmarks! Share some ideas with me! Constructing from a large `&str`: ``` test benches::bench_inlinable_string_from_large ... bench: 32 ns/iter (+/- 6) test benches::bench_std_string_from_large ... bench: 31 ns/iter (+/- 10) ``` Constructing from a small `&str`: ``` test benches::bench_inlinable_string_from_small ... bench: 1 ns/iter (+/- 0) test benches::bench_std_string_from_small ... bench: 26 ns/iter (+/- 14) ``` Pushing a large `&str` onto an empty string: ``` test benches::bench_inlinable_string_push_str_large_onto_empty ... bench: 37 ns/iter (+/- 12) test benches::bench_std_string_push_str_large_onto_empty ... bench: 30 ns/iter (+/- 9) ``` Pushing a small `&str` onto an empty string: ``` test benches::bench_inlinable_string_push_str_small_onto_empty ... bench: 11 ns/iter (+/- 4) test benches::bench_std_string_push_str_small_onto_empty ... bench: 23 ns/iter (+/- 10) ``` Pushing a large `&str` onto a large string: ``` test benches::bench_inlinable_string_push_str_large_onto_large ... bench: 80 ns/iter (+/- 24) test benches::bench_std_string_push_str_large_onto_large ... bench: 78 ns/iter (+/- 23) ``` Pushing a small `&str` onto a small string: ``` test benches::bench_inlinable_string_push_str_small_onto_small ... bench: 17 ns/iter (+/- 6) test benches::bench_std_string_push_str_small_onto_small ... bench: 60 ns/iter (+/- 15) ``` TLDR: If your string's size tends to stay within `INLINE_STRING_CAPACITY`, then `InlinableString` is much faster. Crossing the threshold and forcing a promotion from inline storage to heap allocation will slow it down more than `std::string::String` and you can see the expected drop off in such cases, but that is generally a one time cost. Once the strings are already larger than `INLINE_STRING_CAPACITY`, then the performance difference is negligible. However, take all this with a grain of salt! These are very micro benchmarks and your (hashtag) Real World workload may differ greatly! ## Install Either $ cargo add inlinable_string or add this to your `Cargo.toml`: [dependencies] inlinable_string = "0.1.0" ## Documentation [Documentation](http://fitzgen.github.io/inlinable_string/inlinable_string/index.html) inlinable_string-0.1.15/src/inline_string.rs000064400000000000000000000471700072674642500172750ustar 00000000000000// Copyright 2015, The inlinable_string crate Developers. See the COPYRIGHT file // at the top-level directory of this distribution. // // Licensed under the Apache License, Version 2.0 or the MIT license , at your option. This file may not be // copied, modified, or distributed except according to those terms. //! A short UTF-8 string that uses inline storage and does no heap //! allocation. It may be no longer than `INLINE_STRING_CAPACITY` bytes long. //! //! The capacity restriction makes many operations that would otherwise be //! infallible on `std::string::String` fallible. Additionally, many trait //! interfaces don't allow returning an error when a string runs out of space, //! and so the trait implementation simply panics. As such, `InlineString` does //! not implement `StringExt` and is ***not*** a drop-in replacement for //! `std::string::String` in the way that `inlinable_string::InlinableString` //! aims to be, and is generally difficult to work with. It is not recommended //! to use this type directly unless you really, really want to avoid heap //! allocation, can live with the imposed size restrictions, and are willing //! work around potential sources of panics (eg, in the `From` trait //! implementation). //! //! # Examples //! //! ``` //! use inlinable_string::InlineString; //! //! let mut s = InlineString::new(); //! assert!(s.push_str("hi world").is_ok()); //! assert_eq!(s, "hi world"); //! //! assert!(s.push_str("a really long string that is much bigger than `INLINE_STRING_CAPACITY`").is_err()); //! assert_eq!(s, "hi world"); //! ``` use alloc::borrow; use core::fmt; use core::hash; use core::ops; use core::ptr; use core::str; /// The capacity (in bytes) of inline storage for small strings. /// `InlineString::len()` may never be larger than this. /// /// Sometime in the future, when Rust's generics support specializing with /// compile-time static integers, this number should become configurable. #[cfg(target_pointer_width = "64")] pub const INLINE_STRING_CAPACITY: usize = 30; #[cfg(target_pointer_width = "32")] pub const INLINE_STRING_CAPACITY: usize = 14; /// A short UTF-8 string that uses inline storage and does no heap allocation. /// /// See the [module level documentation](./index.html) for more. #[derive(Clone, Debug, Eq)] pub struct InlineString { length: u8, bytes: [u8; INLINE_STRING_CAPACITY], } /// The error returned when there is not enough space in a `InlineString` for the /// requested operation. #[derive(Debug, PartialEq)] pub struct NotEnoughSpaceError; impl AsRef for InlineString { fn as_ref(&self) -> &str { self.assert_sanity(); unsafe { str::from_utf8_unchecked(&self.bytes[..self.len()]) } } } impl AsRef<[u8]> for InlineString { #[inline] fn as_ref(&self) -> &[u8] { self.as_bytes() } } impl AsMut for InlineString { fn as_mut(&mut self) -> &mut str { self.assert_sanity(); let length = self.len(); unsafe { str::from_utf8_unchecked_mut(&mut self.bytes[..length]) } } } impl AsMut<[u8]> for InlineString { #[inline] fn as_mut(&mut self) -> &mut [u8] { self.assert_sanity(); let length = self.len(); &mut self.bytes[0..length] } } /// Create a `InlineString` from the given `&str`. /// /// # Panics /// /// If the given string's size is greater than `INLINE_STRING_CAPACITY`, this /// method panics. impl<'a> From<&'a str> for InlineString { fn from(string: &'a str) -> InlineString { let string_len = string.len(); assert!(string_len <= INLINE_STRING_CAPACITY); let mut ss = InlineString::new(); unsafe { ptr::copy_nonoverlapping(string.as_ptr(), ss.bytes.as_mut_ptr(), string_len); } ss.length = string_len as u8; ss.assert_sanity(); ss } } impl fmt::Display for InlineString { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { self.assert_sanity(); write!(f, "{}", self as &str) } } impl fmt::Write for InlineString { fn write_char(&mut self, ch: char) -> Result<(), fmt::Error> { self.push(ch).map_err(|_| fmt::Error) } fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> { self.push_str(s).map_err(|_| fmt::Error) } } impl hash::Hash for InlineString { #[inline] fn hash(&self, hasher: &mut H) { (**self).hash(hasher) } } impl ops::Index> for InlineString { type Output = str; #[inline] fn index(&self, index: ops::Range) -> &str { self.assert_sanity(); &self[..][index] } } impl ops::Index> for InlineString { type Output = str; #[inline] fn index(&self, index: ops::RangeTo) -> &str { self.assert_sanity(); &self[..][index] } } impl ops::Index> for InlineString { type Output = str; #[inline] fn index(&self, index: ops::RangeFrom) -> &str { self.assert_sanity(); &self[..][index] } } impl ops::Index for InlineString { type Output = str; #[inline] fn index(&self, _index: ops::RangeFull) -> &str { self.assert_sanity(); unsafe { str::from_utf8_unchecked(&self.bytes[..self.len()]) } } } impl ops::IndexMut> for InlineString { #[inline] fn index_mut(&mut self, index: ops::Range) -> &mut str { self.assert_sanity(); &mut self[..][index] } } impl ops::IndexMut> for InlineString { #[inline] fn index_mut(&mut self, index: ops::RangeTo) -> &mut str { self.assert_sanity(); &mut self[..][index] } } impl ops::IndexMut> for InlineString { #[inline] fn index_mut(&mut self, index: ops::RangeFrom) -> &mut str { self.assert_sanity(); &mut self[..][index] } } impl ops::IndexMut for InlineString { #[inline] fn index_mut(&mut self, _index: ops::RangeFull) -> &mut str { self.assert_sanity(); let length = self.len(); unsafe { str::from_utf8_unchecked_mut(&mut self.bytes[..length]) } } } impl ops::Deref for InlineString { type Target = str; #[inline] fn deref(&self) -> &str { self.assert_sanity(); unsafe { str::from_utf8_unchecked(&self.bytes[..self.len()]) } } } impl ops::DerefMut for InlineString { #[inline] fn deref_mut(&mut self) -> &mut str { self.assert_sanity(); let length = self.len(); unsafe { str::from_utf8_unchecked_mut(&mut self.bytes[..length]) } } } impl Default for InlineString { #[inline] fn default() -> InlineString { InlineString::new() } } impl PartialEq for InlineString { #[inline] fn eq(&self, rhs: &InlineString) -> bool { self.assert_sanity(); rhs.assert_sanity(); PartialEq::eq(&self[..], &rhs[..]) } } macro_rules! impl_eq { ($lhs:ty, $rhs: ty) => { impl<'a> PartialEq<$rhs> for $lhs { #[inline] fn eq(&self, other: &$rhs) -> bool { PartialEq::eq(&self[..], &other[..]) } } impl<'a> PartialEq<$lhs> for $rhs { #[inline] fn eq(&self, other: &$lhs) -> bool { PartialEq::eq(&self[..], &other[..]) } } }; } impl_eq! { InlineString, str } impl_eq! { InlineString, &'a str } impl_eq! { borrow::Cow<'a, str>, InlineString } impl InlineString { #[cfg_attr(feature = "nightly", allow(inline_always))] #[inline(always)] fn assert_sanity(&self) { debug_assert!( self.length as usize <= INLINE_STRING_CAPACITY, "inlinable_string: internal error: length greater than capacity" ); debug_assert!( str::from_utf8(&self.bytes[0..self.length as usize]).is_ok(), "inlinable_string: internal error: contents are not valid UTF-8!" ); } /// Creates a new string buffer initialized with the empty string. /// /// # Examples /// /// ``` /// use inlinable_string::InlineString; /// /// let s = InlineString::new(); /// ``` #[inline] pub fn new() -> InlineString { InlineString { length: 0, bytes: [0; INLINE_STRING_CAPACITY], } } /// Returns the underlying byte buffer, encoded as UTF-8. Trailing bytes are /// zeroed. /// /// # Examples /// /// ``` /// use inlinable_string::InlineString; /// /// let s = InlineString::from("hello"); /// let bytes = s.into_bytes(); /// assert_eq!(&bytes[0..5], [104, 101, 108, 108, 111]); /// ``` #[inline] pub fn into_bytes(mut self) -> [u8; INLINE_STRING_CAPACITY] { self.assert_sanity(); for i in self.len()..INLINE_STRING_CAPACITY { self.bytes[i] = 0; } self.bytes } /// Pushes the given string onto this string buffer. /// /// # Examples /// /// ``` /// use inlinable_string::InlineString; /// /// let mut s = InlineString::from("foo"); /// s.push_str("bar"); /// assert_eq!(s, "foobar"); /// ``` #[inline] pub fn push_str(&mut self, string: &str) -> Result<(), NotEnoughSpaceError> { self.assert_sanity(); let string_len = string.len(); let new_length = self.len() + string_len; if new_length > INLINE_STRING_CAPACITY { return Err(NotEnoughSpaceError); } unsafe { ptr::copy_nonoverlapping( string.as_ptr(), self.bytes.as_mut_ptr().offset(self.length as isize), string_len, ); } self.length = new_length as u8; self.assert_sanity(); Ok(()) } /// Adds the given character to the end of the string. /// /// # Examples /// /// ``` /// use inlinable_string::InlineString; /// /// let mut s = InlineString::from("abc"); /// s.push('1'); /// s.push('2'); /// s.push('3'); /// assert_eq!(s, "abc123"); /// ``` #[inline] pub fn push(&mut self, ch: char) -> Result<(), NotEnoughSpaceError> { self.assert_sanity(); let char_len = ch.len_utf8(); let new_length = self.len() + char_len; if new_length > INLINE_STRING_CAPACITY { return Err(NotEnoughSpaceError); } { let mut slice = &mut self.bytes[self.length as usize..INLINE_STRING_CAPACITY]; ch.encode_utf8(&mut slice); } self.length = new_length as u8; self.assert_sanity(); Ok(()) } /// Works with the underlying buffer as a byte slice. /// /// # Examples /// /// ``` /// use inlinable_string::InlineString; /// /// let s = InlineString::from("hello"); /// assert_eq!(s.as_bytes(), [104, 101, 108, 108, 111]); /// ``` #[inline] pub fn as_bytes(&self) -> &[u8] { self.assert_sanity(); &self.bytes[0..self.len()] } /// Shortens a string to the specified length. /// /// # Panics /// /// Panics if `new_len` > current length, or if `new_len` is not a character /// boundary. /// /// # Examples /// /// ``` /// use inlinable_string::InlineString; /// /// let mut s = InlineString::from("hello"); /// s.truncate(2); /// assert_eq!(s, "he"); /// ``` #[inline] pub fn truncate(&mut self, new_len: usize) { self.assert_sanity(); assert!( self.is_char_boundary(new_len), "inlinable_string::InlineString::truncate: new_len is not a character boundary" ); assert!(new_len <= self.len()); self.length = new_len as u8; self.assert_sanity(); } /// Removes the last character from the string buffer and returns it. /// Returns `None` if this string buffer is empty. /// /// # Examples /// /// ``` /// use inlinable_string::InlineString; /// /// let mut s = InlineString::from("foo"); /// assert_eq!(s.pop(), Some('o')); /// assert_eq!(s.pop(), Some('o')); /// assert_eq!(s.pop(), Some('f')); /// assert_eq!(s.pop(), None); /// ``` #[inline] pub fn pop(&mut self) -> Option { self.assert_sanity(); match self.char_indices().rev().next() { None => None, Some((idx, ch)) => { self.length = idx as u8; self.assert_sanity(); Some(ch) } } } /// Removes the character from the string buffer at byte position `idx` and /// returns it. /// /// # Panics /// /// If `idx` does not lie on a character boundary, or if it is out of /// bounds, then this function will panic. /// /// # Examples /// /// ``` /// use inlinable_string::InlineString; /// /// let mut s = InlineString::from("foo"); /// assert_eq!(s.remove(0), 'f'); /// assert_eq!(s.remove(1), 'o'); /// assert_eq!(s.remove(0), 'o'); /// ``` #[inline] pub fn remove(&mut self, idx: usize) -> char { self.assert_sanity(); assert!(idx < self.len()); let ch = self .get(idx..) .expect( "inlinable_string::InlineString::remove: idx does not lie on a character boundary", ) .chars() .next() .expect("Should be `Some` because `idx < self.len()`"); let char_len = ch.len_utf8(); let next = idx + char_len; unsafe { let ptr = self.bytes.as_mut_ptr(); ptr::copy( ptr.add(next), ptr.add(idx), self.len() - next, ); } self.length -= char_len as u8; self.assert_sanity(); ch } /// Inserts the given bytes at the given position of the string. unsafe fn insert_bytes(&mut self, idx: usize, bytes: &[u8]) -> Result<(), NotEnoughSpaceError> { let len = self.len(); let amt = bytes.len(); // This subtraction does not overflow because `INLINE_STRING_CAPACITY >= self.len()` holds. if amt > INLINE_STRING_CAPACITY - len { return Err(NotEnoughSpaceError); } let ptr = self.bytes.as_mut_ptr().add(idx); // Shift the latter part. ptr::copy( ptr, ptr.add(amt), len - idx, ); // Copy the bytes into the buffer. ptr::copy(bytes.as_ptr(), self.bytes.as_mut_ptr().add(idx), amt); // `amt` is less than `u8::MAX` becuase `INLINE_STRING_CAPACITY < u8::MAX` holds. self.length += amt as u8; Ok(()) } /// Inserts a character into the string buffer at byte position `idx`. /// /// # Examples /// /// ``` /// use inlinable_string::InlineString; /// /// let mut s = InlineString::from("foo"); /// s.insert(2, 'f'); /// assert!(s == "fofo"); /// ``` /// /// # Panics /// /// If `idx` does not lie on a character boundary or is out of bounds, then /// this function will panic. #[inline] pub fn insert(&mut self, idx: usize, ch: char) -> Result<(), NotEnoughSpaceError> { self.assert_sanity(); assert!(idx <= self.len()); let mut bits = [0; 4]; let bits = ch.encode_utf8(&mut bits).as_bytes(); unsafe { self.insert_bytes(idx, bits)?; } self.assert_sanity(); Ok(()) } /// Inserts a string into the string buffer at byte position `idx`. /// /// # Examples /// /// ``` /// use inlinable_string::InlineString; /// /// let mut s = InlineString::from("foo"); /// s.insert_str(2, "bar"); /// assert!(s == "fobaro"); /// ``` #[inline] pub fn insert_str(&mut self, idx: usize, string: &str) -> Result<(), NotEnoughSpaceError> { self.assert_sanity(); assert!(idx <= self.len()); unsafe { self.insert_bytes(idx, string.as_bytes())?; } self.assert_sanity(); Ok(()) } /// Views the internal string buffer as a mutable sequence of bytes. /// /// # Safety /// /// This is unsafe because it does not check to ensure that the resulting /// string will be valid UTF-8. /// /// # Examples /// /// ``` /// use inlinable_string::InlineString; /// /// let mut s = InlineString::from("hello"); /// unsafe { /// let slice = s.as_mut_slice(); /// assert!(slice == &[104, 101, 108, 108, 111]); /// slice.reverse(); /// } /// assert_eq!(s, "olleh"); /// ``` #[inline] pub unsafe fn as_mut_slice(&mut self) -> &mut [u8] { self.assert_sanity(); &mut self.bytes[0..self.length as usize] } /// Returns the number of bytes in this string. /// /// # Examples /// /// ``` /// use inlinable_string::InlineString; /// /// let a = InlineString::from("foo"); /// assert_eq!(a.len(), 3); /// ``` #[inline] pub fn len(&self) -> usize { self.assert_sanity(); self.length as usize } /// Returns true if the string contains no bytes /// /// # Examples /// /// ``` /// use inlinable_string::InlineString; /// /// let mut v = InlineString::new(); /// assert!(v.is_empty()); /// v.push('a'); /// assert!(!v.is_empty()); /// ``` #[inline] pub fn is_empty(&self) -> bool { self.assert_sanity(); self.length == 0 } /// Truncates the string, returning it to 0 length. /// /// # Examples /// /// ``` /// use inlinable_string::InlineString; /// /// let mut s = InlineString::from("foo"); /// s.clear(); /// assert!(s.is_empty()); /// ``` #[inline] pub fn clear(&mut self) { self.assert_sanity(); self.length = 0; self.assert_sanity(); } } #[cfg(test)] mod tests { use alloc::string::String; use super::{InlineString, NotEnoughSpaceError, INLINE_STRING_CAPACITY}; #[test] fn test_push_str() { let mut s = InlineString::new(); assert!(s.push_str("small").is_ok()); assert_eq!(s, "small"); let long_str = "this is a really long string that is much larger than INLINE_STRING_CAPACITY and so cannot be stored inline."; assert_eq!(s.push_str(long_str), Err(NotEnoughSpaceError)); assert_eq!(s, "small"); } #[test] fn test_push() { let mut s = InlineString::new(); for _ in 0..INLINE_STRING_CAPACITY { assert!(s.push('a').is_ok()); } assert_eq!(s.push('a'), Err(NotEnoughSpaceError)); } #[test] fn test_insert() { let mut s = InlineString::new(); for _ in 0..INLINE_STRING_CAPACITY { assert!(s.insert(0, 'a').is_ok()); } assert_eq!(s.insert(0, 'a'), Err(NotEnoughSpaceError)); } #[test] fn test_write() { use core::fmt::{Error, Write}; let mut s = InlineString::new(); let mut normal_string = String::new(); for _ in 0..INLINE_STRING_CAPACITY { assert!(write!(&mut s, "a").is_ok()); assert!(write!(&mut normal_string, "a").is_ok()); } assert_eq!(write!(&mut s, "a"), Err(Error)); assert_eq!(&normal_string[..], &s[..]); } } #[cfg(test)] #[cfg(feature = "nightly")] mod benches { use test::Bencher; #[bench] fn its_fast(_b: &mut Bencher) {} } inlinable_string-0.1.15/src/lib.rs000064400000000000000000000721470072674642500152010ustar 00000000000000// Copyright 2015, The inlinable_string crate Developers. See the COPYRIGHT file // at the top-level directory of this distribution. // // Licensed under the Apache License, Version 2.0 or the MIT license , at your option. This file may not be // copied, modified, or distributed except according to those terms. //! The `inlinable_string` crate provides the //! [`InlinableString`](./enum.InlinableString.html) type — an owned, //! grow-able UTF-8 string that stores small strings inline and avoids //! heap-allocation — and the //! [`StringExt`](./string_ext/trait.StringExt.html) trait which abstracts //! string operations over both `std::string::String` and `InlinableString` (or //! even your own custom string type). //! //! `StringExt`'s API is mostly identical to `std::string::String`; unstable and //! deprecated methods are not included. A `StringExt` implementation is //! provided for both `std::string::String` and `InlinableString`. This enables //! `InlinableString` to generally work as a drop-in replacement for //! `std::string::String` and `&StringExt` to work with references to either //! type. //! //! # Examples //! //! ``` //! use inlinable_string::{InlinableString, StringExt}; //! //! // Small strings are stored inline and don't perform heap-allocation. //! let mut s = InlinableString::from("small"); //! assert_eq!(s.capacity(), inlinable_string::INLINE_STRING_CAPACITY); //! //! // Inline strings are transparently promoted to heap-allocated strings when //! // they grow too big. //! s.push_str("a really long string that's bigger than `INLINE_STRING_CAPACITY`"); //! assert!(s.capacity() > inlinable_string::INLINE_STRING_CAPACITY); //! //! // This method can work on strings potentially stored inline on the stack, //! // on the heap, or plain old `std::string::String`s! //! fn takes_a_string_reference(string: &mut StringExt) { //! // Do something with the string... //! string.push_str("it works!"); //! } //! //! let mut s1 = String::from("this is a plain std::string::String"); //! let mut s2 = InlinableString::from("inline"); //! //! // Both work! //! takes_a_string_reference(&mut s1); //! takes_a_string_reference(&mut s2); //! ``` //! //! # Porting Your Code //! //! * If `my_string` is always on the stack: `let my_string = String::new();` → //! `let my_string = InlinableString::new();` //! //! * `fn foo(string: &mut String) { ... }` → `fn foo(string: &mut StringExt) { ... }` //! //! * `fn foo(string: &str) { ... }` does not need to be modified. //! //! * `struct S { member: String }` is a little trickier. If `S` is always stack //! allocated, it probably makes sense to make `member` be of type //! `InlinableString`. If `S` is heap-allocated and `member` is *always* small, //! consider using the more restrictive //! [`InlineString`](./inline_string/struct.InlineString.html) type. If `member` is //! not always small, then it should probably be left as a `String`. //! //! # Serialization //! //! `InlinableString` implements [`serde`][serde-docs]'s `Serialize` and `Deserialize` traits. //! Add the `serde` feature to your `Cargo.toml` to enable serialization. //! //! [serde-docs]: https://serde.rs #![forbid(missing_docs)] #![cfg_attr(feature = "nightly", feature(plugin))] #![cfg_attr(all(test, feature = "nightly"), feature(test))] #![cfg_attr(feature = "no_std", no_std)] #[allow(unused_imports)] #[cfg_attr(feature = "no_std", macro_use)] extern crate alloc; #[cfg(test)] #[cfg(feature = "nightly")] extern crate test; #[cfg(feature = "serde")] mod serde_impl; pub mod inline_string; pub mod string_ext; pub use inline_string::{InlineString, INLINE_STRING_CAPACITY}; pub use string_ext::StringExt; use alloc::borrow::{Borrow, Cow}; use alloc::vec::Vec; use alloc::string::{FromUtf16Error, FromUtf8Error, String}; use core::cmp::Ordering; use core::convert; use core::fmt; use core::hash; use core::iter; use core::mem; use core::ops; use core::str::FromStr; /// An owned, grow-able UTF-8 string that allocates short strings inline on the /// stack. /// /// See the [module level documentation](./index.html) for more. #[derive(Clone, Eq)] pub enum InlinableString { /// A heap-allocated string. Heap(String), /// A small string stored inline. Inline(InlineString), } impl fmt::Debug for InlinableString { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&self as &str, f) } } impl iter::FromIterator for InlinableString { fn from_iter>(iter: I) -> InlinableString { let mut buf = InlinableString::new(); buf.extend(iter); buf } } impl<'a> iter::FromIterator<&'a str> for InlinableString { fn from_iter>(iter: I) -> InlinableString { let mut buf = InlinableString::new(); buf.extend(iter); buf } } impl Extend for InlinableString { fn extend>(&mut self, iterable: I) { let iterator = iterable.into_iter(); let (lower_bound, _) = iterator.size_hint(); self.reserve(lower_bound); for ch in iterator { self.push(ch); } } } impl<'a> Extend<&'a char> for InlinableString { fn extend>(&mut self, iter: I) { self.extend(iter.into_iter().cloned()); } } impl<'a> Extend<&'a str> for InlinableString { fn extend>(&mut self, iterable: I) { let iterator = iterable.into_iter(); let (lower_bound, _) = iterator.size_hint(); self.reserve(lower_bound); for s in iterator { self.push_str(s); } } } impl<'a> ops::Add<&'a str> for InlinableString { type Output = InlinableString; #[inline] fn add(mut self, other: &str) -> InlinableString { self.push_str(other); self } } impl PartialOrd for InlinableString { fn partial_cmp(&self, rhs: &InlinableString) -> Option { Some(Ord::cmp(&self[..], &rhs[..])) } } impl Ord for InlinableString { #[inline] fn cmp(&self, rhs: &InlinableString) -> Ordering { Ord::cmp(&self[..], &rhs[..]) } } impl hash::Hash for InlinableString { #[inline] fn hash(&self, hasher: &mut H) { (**self).hash(hasher) } } impl Borrow for InlinableString { fn borrow(&self) -> &str { &*self } } impl AsRef for InlinableString { fn as_ref(&self) -> &str { match *self { InlinableString::Heap(ref s) => &*s, InlinableString::Inline(ref s) => &*s, } } } impl AsMut for InlinableString { fn as_mut(&mut self) -> &mut str { match *self { InlinableString::Heap(ref mut s) => s.as_mut_str(), InlinableString::Inline(ref mut s) => &mut s[..], } } } impl<'a> From<&'a str> for InlinableString { #[inline] fn from(string: &'a str) -> InlinableString { if string.len() <= INLINE_STRING_CAPACITY { InlinableString::Inline(string.into()) } else { InlinableString::Heap(string.into()) } } } impl From for InlinableString { #[inline] fn from(string: String) -> InlinableString { if string.len() <= INLINE_STRING_CAPACITY { InlinableString::Inline(string.as_str().into()) } else { InlinableString::Heap(string) } } } impl FromStr for InlinableString { type Err = convert::Infallible; #[inline] fn from_str(s: &str) -> Result { Ok(InlinableString::from(s)) } } impl Default for InlinableString { fn default() -> Self { InlinableString::new() } } impl fmt::Display for InlinableString { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { match *self { InlinableString::Heap(ref s) => s.fmt(f), InlinableString::Inline(ref s) => s.fmt(f), } } } impl fmt::Write for InlinableString { fn write_char(&mut self, ch: char) -> Result<(), fmt::Error> { self.push(ch); Ok(()) } fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> { self.push_str(s); Ok(()) } } impl ops::Index> for InlinableString { type Output = str; #[inline] fn index(&self, index: ops::Range) -> &str { match *self { InlinableString::Heap(ref s) => s.index(index), InlinableString::Inline(ref s) => s.index(index), } } } impl ops::Index> for InlinableString { type Output = str; #[inline] fn index(&self, index: ops::RangeTo) -> &str { match *self { InlinableString::Heap(ref s) => s.index(index), InlinableString::Inline(ref s) => s.index(index), } } } impl ops::Index> for InlinableString { type Output = str; #[inline] fn index(&self, index: ops::RangeFrom) -> &str { match *self { InlinableString::Heap(ref s) => s.index(index), InlinableString::Inline(ref s) => s.index(index), } } } impl ops::Index for InlinableString { type Output = str; #[inline] fn index(&self, index: ops::RangeFull) -> &str { match *self { InlinableString::Heap(ref s) => s.index(index), InlinableString::Inline(ref s) => s.index(index), } } } impl ops::IndexMut> for InlinableString { #[inline] fn index_mut(&mut self, index: ops::Range) -> &mut str { match *self { InlinableString::Heap(ref mut s) => s.index_mut(index), InlinableString::Inline(ref mut s) => s.index_mut(index), } } } impl ops::IndexMut> for InlinableString { #[inline] fn index_mut(&mut self, index: ops::RangeTo) -> &mut str { match *self { InlinableString::Heap(ref mut s) => s.index_mut(index), InlinableString::Inline(ref mut s) => s.index_mut(index), } } } impl ops::IndexMut> for InlinableString { #[inline] fn index_mut(&mut self, index: ops::RangeFrom) -> &mut str { match *self { InlinableString::Heap(ref mut s) => s.index_mut(index), InlinableString::Inline(ref mut s) => s.index_mut(index), } } } impl ops::IndexMut for InlinableString { #[inline] fn index_mut(&mut self, index: ops::RangeFull) -> &mut str { match *self { InlinableString::Heap(ref mut s) => s.index_mut(index), InlinableString::Inline(ref mut s) => s.index_mut(index), } } } impl ops::Deref for InlinableString { type Target = str; #[inline] fn deref(&self) -> &str { match *self { InlinableString::Heap(ref s) => s.deref(), InlinableString::Inline(ref s) => s.deref(), } } } impl ops::DerefMut for InlinableString { #[inline] fn deref_mut(&mut self) -> &mut str { match *self { InlinableString::Heap(ref mut s) => s.deref_mut(), InlinableString::Inline(ref mut s) => s.deref_mut(), } } } impl PartialEq for InlinableString { #[inline] fn eq(&self, rhs: &InlinableString) -> bool { PartialEq::eq(&self[..], &rhs[..]) } } macro_rules! impl_eq { ($lhs:ty, $rhs: ty) => { impl<'a> PartialEq<$rhs> for $lhs { #[inline] fn eq(&self, other: &$rhs) -> bool { PartialEq::eq(&self[..], &other[..]) } } impl<'a> PartialEq<$lhs> for $rhs { #[inline] fn eq(&self, other: &$lhs) -> bool { PartialEq::eq(&self[..], &other[..]) } } }; } impl_eq! { InlinableString, str } impl_eq! { InlinableString, String } impl_eq! { InlinableString, &'a str } impl_eq! { InlinableString, InlineString } impl_eq! { Cow<'a, str>, InlinableString } impl<'a> StringExt<'a> for InlinableString { #[inline] fn new() -> Self { InlinableString::Inline(InlineString::new()) } #[inline] fn with_capacity(capacity: usize) -> Self { if capacity <= INLINE_STRING_CAPACITY { InlinableString::Inline(InlineString::new()) } else { InlinableString::Heap(String::with_capacity(capacity)) } } #[inline] fn from_utf8(vec: Vec) -> Result { String::from_utf8(vec).map(InlinableString::Heap) } #[inline] fn from_utf16(v: &[u16]) -> Result { String::from_utf16(v).map(InlinableString::Heap) } #[inline] fn from_utf16_lossy(v: &[u16]) -> Self { InlinableString::Heap(String::from_utf16_lossy(v)) } #[inline] unsafe fn from_raw_parts(buf: *mut u8, length: usize, capacity: usize) -> Self { InlinableString::Heap(String::from_raw_parts(buf, length, capacity)) } #[inline] unsafe fn from_utf8_unchecked(bytes: Vec) -> Self { InlinableString::Heap(String::from_utf8_unchecked(bytes)) } #[inline] fn into_bytes(self) -> Vec { match self { InlinableString::Heap(s) => s.into_bytes(), InlinableString::Inline(s) => Vec::from(&s[..]), } } #[inline] fn push_str(&mut self, string: &str) { let promoted = match *self { InlinableString::Inline(ref mut s) => { if s.push_str(string).is_ok() { return; } let mut promoted = String::with_capacity(string.len() + s.len()); promoted.push_str(&*s); promoted.push_str(string); promoted } InlinableString::Heap(ref mut s) => { s.push_str(string); return; } }; mem::swap(self, &mut InlinableString::Heap(promoted)); } #[inline] fn capacity(&self) -> usize { match *self { InlinableString::Heap(ref s) => s.capacity(), InlinableString::Inline(_) => INLINE_STRING_CAPACITY, } } #[inline] fn reserve(&mut self, additional: usize) { let promoted = match *self { InlinableString::Inline(ref s) => { let new_capacity = s.len() + additional; if new_capacity <= INLINE_STRING_CAPACITY { return; } let mut promoted = String::with_capacity(new_capacity); promoted.push_str(&s); promoted } InlinableString::Heap(ref mut s) => { s.reserve(additional); return; } }; mem::swap(self, &mut InlinableString::Heap(promoted)); } #[inline] fn reserve_exact(&mut self, additional: usize) { let promoted = match *self { InlinableString::Inline(ref s) => { let new_capacity = s.len() + additional; if new_capacity <= INLINE_STRING_CAPACITY { return; } let mut promoted = String::with_capacity(new_capacity); promoted.push_str(&s); promoted } InlinableString::Heap(ref mut s) => { s.reserve_exact(additional); return; } }; mem::swap(self, &mut InlinableString::Heap(promoted)); } #[inline] fn shrink_to_fit(&mut self) { if self.len() <= INLINE_STRING_CAPACITY { let demoted = if let InlinableString::Heap(ref s) = *self { InlineString::from(&s[..]) } else { return; }; mem::swap(self, &mut InlinableString::Inline(demoted)); return; } match *self { InlinableString::Heap(ref mut s) => s.shrink_to_fit(), _ => panic!("inlinable_string: internal error: this branch should be unreachable"), }; } #[inline] fn push(&mut self, ch: char) { let promoted = match *self { InlinableString::Inline(ref mut s) => { if s.push(ch).is_ok() { return; } let mut promoted = String::with_capacity(s.len() + 1); promoted.push_str(&*s); promoted.push(ch); promoted } InlinableString::Heap(ref mut s) => { s.push(ch); return; } }; mem::swap(self, &mut InlinableString::Heap(promoted)); } #[inline] fn as_bytes(&self) -> &[u8] { match *self { InlinableString::Heap(ref s) => s.as_bytes(), InlinableString::Inline(ref s) => s.as_bytes(), } } #[inline] fn truncate(&mut self, new_len: usize) { match *self { InlinableString::Heap(ref mut s) => s.truncate(new_len), InlinableString::Inline(ref mut s) => s.truncate(new_len), }; } #[inline] fn pop(&mut self) -> Option { match *self { InlinableString::Heap(ref mut s) => s.pop(), InlinableString::Inline(ref mut s) => s.pop(), } } #[inline] fn remove(&mut self, idx: usize) -> char { match *self { InlinableString::Heap(ref mut s) => s.remove(idx), InlinableString::Inline(ref mut s) => s.remove(idx), } } #[inline] fn insert(&mut self, idx: usize, ch: char) { let promoted = match *self { InlinableString::Heap(ref mut s) => { s.insert(idx, ch); return; } InlinableString::Inline(ref mut s) => { if s.insert(idx, ch).is_ok() { return; } let mut promoted = String::with_capacity(s.len() + 1); promoted.push_str(&s[..idx]); promoted.push(ch); promoted.push_str(&s[idx..]); promoted } }; mem::swap(self, &mut InlinableString::Heap(promoted)); } #[inline] fn insert_str(&mut self, idx: usize, string: &str) { let promoted = match *self { InlinableString::Heap(ref mut s) => { s.insert_str(idx, string); return; } InlinableString::Inline(ref mut s) => { if s.insert_str(idx, string).is_ok() { return; } let mut promoted = String::with_capacity(s.len() + string.len()); promoted.push_str(&s[..idx]); promoted.push_str(string); promoted.push_str(&s[idx..]); promoted } }; mem::swap(self, &mut InlinableString::Heap(promoted)); } #[inline] unsafe fn as_mut_slice(&mut self) -> &mut [u8] { match *self { InlinableString::Heap(ref mut s) => &mut s.as_mut_vec()[..], InlinableString::Inline(ref mut s) => s.as_mut_slice(), } } #[inline] fn len(&self) -> usize { match *self { InlinableString::Heap(ref s) => s.len(), InlinableString::Inline(ref s) => s.len(), } } } #[cfg(test)] mod tests { use alloc::string::{String, ToString}; use core::iter::FromIterator; use super::{InlinableString, StringExt, INLINE_STRING_CAPACITY}; use core::cmp::Ordering; use core::str::FromStr; #[test] fn test_size() { use core::mem::size_of; assert_eq!(size_of::(), 4 * size_of::()); } // First, specifically test operations that overflow InlineString's capacity // and require promoting the string to heap allocation. #[test] fn test_push_str() { let mut s = InlinableString::new(); s.push_str("small"); assert_eq!(s, "small"); let long_str = "this is a really long string that is much larger than INLINE_STRING_CAPACITY and so cannot be stored inline."; s.push_str(long_str); assert_eq!(s, String::from("small") + long_str); } #[test] fn test_write() { use core::fmt::Write; let mut s = InlinableString::new(); write!(&mut s, "small").expect("!write"); assert_eq!(s, "small"); let long_str = "this is a really long string that is much larger than INLINE_STRING_CAPACITY and so cannot be stored inline."; write!(&mut s, "{}", long_str).expect("!write"); assert_eq!(s, String::from("small") + long_str); } #[test] fn test_push() { let mut s = InlinableString::new(); for _ in 0..INLINE_STRING_CAPACITY { s.push('a'); } s.push('a'); assert_eq!( s, String::from_iter((0..INLINE_STRING_CAPACITY + 1).map(|_| 'a')) ); } #[test] fn test_insert() { let mut s = InlinableString::new(); for _ in 0..INLINE_STRING_CAPACITY { s.insert(0, 'a'); } s.insert(0, 'a'); assert_eq!( s, String::from_iter((0..INLINE_STRING_CAPACITY + 1).map(|_| 'a')) ); } #[test] fn test_insert_str() { let mut s = InlinableString::new(); for _ in 0..(INLINE_STRING_CAPACITY / 3) { s.insert_str(0, "foo"); } s.insert_str(0, "foo"); assert_eq!( s, String::from_iter((0..(INLINE_STRING_CAPACITY / 3) + 1).map(|_| "foo")) ); } // Next, some general sanity tests. #[test] fn test_new() { let s = ::new(); assert!(StringExt::is_empty(&s)); } #[test] fn test_with_capacity() { let s = ::with_capacity(10); assert!(StringExt::capacity(&s) >= 10); } #[test] fn test_from_utf8() { let s = ::from_utf8(vec![104, 101, 108, 108, 111]); assert_eq!(s.unwrap(), "hello"); } #[test] fn test_from_utf16() { let v = &mut [0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0x0069, 0x0063]; let s = ::from_utf16(v); assert_eq!(s.unwrap(), "𝄞music"); } #[test] fn test_from_utf16_lossy() { let input = b"Hello \xF0\x90\x80World"; let output = ::from_utf8_lossy(input); assert_eq!(output, "Hello \u{FFFD}World"); } #[test] fn test_into_bytes() { let s = InlinableString::from("hello"); let bytes = StringExt::into_bytes(s); assert_eq!(bytes, [104, 101, 108, 108, 111]); } #[test] fn test_capacity() { let s = ::with_capacity(100); assert!(InlinableString::capacity(&s) >= 100); } #[test] fn test_reserve() { let mut s = ::new(); StringExt::reserve(&mut s, 100); assert!(InlinableString::capacity(&s) >= 100); } #[test] fn test_reserve_exact() { let mut s = ::new(); StringExt::reserve_exact(&mut s, 100); assert!(InlinableString::capacity(&s) >= 100); } #[test] fn test_shrink_to_fit() { let mut s = ::with_capacity(100); StringExt::push_str(&mut s, "foo"); StringExt::shrink_to_fit(&mut s); assert_eq!(InlinableString::capacity(&s), INLINE_STRING_CAPACITY); } #[test] fn test_truncate() { let mut s = InlinableString::from("foo"); StringExt::truncate(&mut s, 1); assert_eq!(s, "f"); } #[test] fn test_pop() { let mut s = InlinableString::from("foo"); assert_eq!(StringExt::pop(&mut s), Some('o')); assert_eq!(StringExt::pop(&mut s), Some('o')); assert_eq!(StringExt::pop(&mut s), Some('f')); assert_eq!(StringExt::pop(&mut s), None); } #[test] fn test_ord() { let s1 = InlinableString::from("foo"); let s2 = InlinableString::from("bar"); assert_eq!(Ord::cmp(&s1, &s2), Ordering::Greater); assert_eq!(Ord::cmp(&s1, &s1), Ordering::Equal); } #[test] fn test_display() { let short = InlinableString::from("he"); let long = InlinableString::from("hello world"); assert_eq!(format!("{}", short), "he".to_string()); assert_eq!(format!("{}", long), "hello world".to_string()); } #[test] fn test_debug() { let short = InlinableString::from("he"); let long = InlinableString::from("hello world hello world hello world"); assert_eq!(format!("{:?}", short), "\"he\""); assert_eq!( format!("{:?}", long), "\"hello world hello world hello world\"" ); } // example generic function where impl FromStr for InlinableString is useful fn parse_non_empty(s: &str) -> Option { if s.len() == 0 { None } else { let val = T::from_str(s).unwrap_or_else(|_| panic!("unwrap")); Some(val) } } #[test] fn test_fromstr() { assert_eq!(parse_non_empty::(""), None); assert_eq!(parse_non_empty::("10"), Some(10u8)); assert_eq!( parse_non_empty::("foo"), Some(InlinableString::from("foo")) ); } } #[cfg(test)] #[cfg(feature = "nightly")] mod benches { #[cfg(feature = "no_std")] use alloc::string::String; use super::{InlinableString, StringExt}; use test::{black_box, Bencher}; const SMALL_STR: &'static str = "foobar"; const LARGE_STR: &'static str = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"; #[bench] fn bench_std_string_push_str_small_onto_empty(b: &mut Bencher) { b.iter(|| { let mut s = String::new(); s.push_str(SMALL_STR); black_box(s); }); } #[bench] fn bench_inlinable_string_push_str_small_onto_empty(b: &mut Bencher) { b.iter(|| { let mut s = InlinableString::new(); s.push_str(SMALL_STR); black_box(s); }); } #[bench] fn bench_std_string_push_str_large_onto_empty(b: &mut Bencher) { b.iter(|| { let mut s = String::new(); s.push_str(LARGE_STR); black_box(s); }); } #[bench] fn bench_inlinable_string_push_str_large_onto_empty(b: &mut Bencher) { b.iter(|| { let mut s = InlinableString::new(); s.push_str(LARGE_STR); black_box(s); }); } #[bench] fn bench_std_string_push_str_small_onto_small(b: &mut Bencher) { b.iter(|| { let mut s = String::from(SMALL_STR); s.push_str(SMALL_STR); black_box(s); }); } #[bench] fn bench_inlinable_string_push_str_small_onto_small(b: &mut Bencher) { b.iter(|| { let mut s = InlinableString::from(SMALL_STR); s.push_str(SMALL_STR); black_box(s); }); } #[bench] fn bench_std_string_push_str_large_onto_large(b: &mut Bencher) { b.iter(|| { let mut s = String::from(LARGE_STR); s.push_str(LARGE_STR); black_box(s); }); } #[bench] fn bench_inlinable_string_push_str_large_onto_large(b: &mut Bencher) { b.iter(|| { let mut s = InlinableString::from(LARGE_STR); s.push_str(LARGE_STR); black_box(s); }); } #[bench] fn bench_std_string_from_small(b: &mut Bencher) { b.iter(|| { let s = String::from(SMALL_STR); black_box(s); }); } #[bench] fn bench_inlinable_string_from_small(b: &mut Bencher) { b.iter(|| { let s = InlinableString::from(SMALL_STR); black_box(s); }); } #[bench] fn bench_std_string_from_large(b: &mut Bencher) { b.iter(|| { let s = String::from(LARGE_STR); black_box(s); }); } #[bench] fn bench_inlinable_string_from_large(b: &mut Bencher) { b.iter(|| { let s = InlinableString::from(LARGE_STR); black_box(s); }); } } inlinable_string-0.1.15/src/serde_impl.rs000064400000000000000000000027350072674642500165520ustar 00000000000000use alloc::string::String; use crate::InlinableString; use serde::de::{Deserialize, Deserializer, Error as DeError, Visitor}; use serde::{Serialize, Serializer}; use core::fmt; impl Serialize for InlinableString { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(self) } } impl<'de> Deserialize<'de> for InlinableString { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { struct InlinableStringVisitor; impl<'de> Visitor<'de> for InlinableStringVisitor { type Value = InlinableString; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a string") } fn visit_str(self, v: &str) -> Result where E: DeError, { Ok(v.into()) } fn visit_string(self, v: String) -> Result where E: DeError, { Ok(v.into()) } } deserializer.deserialize_str(InlinableStringVisitor) } } #[cfg(test)] mod tests { use crate::InlinableString; use serde_test::{assert_tokens, Token}; #[test] fn test_ser_de() { let s = InlinableString::from("small"); assert_tokens(&s, &[Token::String("small")]); } } inlinable_string-0.1.15/src/string_ext.rs000064400000000000000000000453130072674642500166140ustar 00000000000000// Copyright 2015, The inlinable_string crate Developers. See the COPYRIGHT file // at the top-level directory of this distribution. // // Licensed under the Apache License, Version 2.0 or the MIT license , at your option. This file may not be // copied, modified, or distributed except according to those terms. //! A trait that exists to abstract string operations over any number of //! concrete string type implementations. //! //! See the [crate level documentation](./../index.html) for more. use alloc::borrow::{Borrow, Cow}; use alloc::vec::Vec; use alloc::string::{String, FromUtf16Error, FromUtf8Error}; use core::cmp::PartialEq; use core::fmt::Display; /// A trait that exists to abstract string operations over any number of /// concrete string type implementations. /// /// See the [crate level documentation](./../index.html) for more. pub trait StringExt<'a>: Borrow + Display + PartialEq + PartialEq<&'a str> + PartialEq + PartialEq> { /// Creates a new string buffer initialized with the empty string. /// /// # Examples /// /// ``` /// use inlinable_string::{InlinableString, StringExt}; /// /// let s = InlinableString::new(); /// ``` fn new() -> Self where Self: Sized; /// Creates a new string buffer with the given capacity. The string will be /// able to hold at least `capacity` bytes without reallocating. If /// `capacity` is less than or equal to `INLINE_STRING_CAPACITY`, the string /// will not heap allocate. /// /// # Examples /// /// ``` /// use inlinable_string::{InlinableString, StringExt}; /// /// let s = InlinableString::with_capacity(10); /// ``` fn with_capacity(capacity: usize) -> Self where Self: Sized; /// Returns the vector as a string buffer, if possible, taking care not to /// copy it. /// /// # Failure /// /// If the given vector is not valid UTF-8, then the original vector and the /// corresponding error is returned. /// /// # Examples /// /// ``` /// use inlinable_string::{InlinableString, StringExt}; /// /// let hello_vec = vec![104, 101, 108, 108, 111]; /// let s = InlinableString::from_utf8(hello_vec).unwrap(); /// assert_eq!(s, "hello"); /// /// let invalid_vec = vec![240, 144, 128]; /// let s = InlinableString::from_utf8(invalid_vec).err().unwrap(); /// let err = s.utf8_error(); /// assert_eq!(s.into_bytes(), [240, 144, 128]); /// ``` fn from_utf8(vec: Vec) -> Result where Self: Sized; /// Converts a vector of bytes to a new UTF-8 string. /// Any invalid UTF-8 sequences are replaced with U+FFFD REPLACEMENT CHARACTER. /// /// # Examples /// /// ``` /// use inlinable_string::{InlinableString, StringExt}; /// /// let input = b"Hello \xF0\x90\x80World"; /// let output = InlinableString::from_utf8_lossy(input); /// assert_eq!(output, "Hello \u{FFFD}World"); /// ``` fn from_utf8_lossy(v: &'a [u8]) -> Cow<'a, str> where Self: Sized, { String::from_utf8_lossy(v) } /// Decode a UTF-16 encoded vector `v` into a `InlinableString`, returning `None` /// if `v` contains any invalid data. /// /// # Examples /// /// ``` /// use inlinable_string::{InlinableString, StringExt}; /// /// // 𝄞music /// let mut v = &mut [0xD834, 0xDD1E, 0x006d, 0x0075, /// 0x0073, 0x0069, 0x0063]; /// assert_eq!(InlinableString::from_utf16(v).unwrap(), /// InlinableString::from("𝄞music")); /// /// // 𝄞muic /// v[4] = 0xD800; /// assert!(InlinableString::from_utf16(v).is_err()); /// ``` fn from_utf16(v: &[u16]) -> Result where Self: Sized; /// Decode a UTF-16 encoded vector `v` into a string, replacing /// invalid data with the replacement character (U+FFFD). /// /// # Examples /// /// ``` /// use inlinable_string::{InlinableString, StringExt}; /// /// // 𝄞music /// let v = &[0xD834, 0xDD1E, 0x006d, 0x0075, /// 0x0073, 0xDD1E, 0x0069, 0x0063, /// 0xD834]; /// /// assert_eq!(InlinableString::from_utf16_lossy(v), /// InlinableString::from("𝄞mus\u{FFFD}ic\u{FFFD}")); /// ``` fn from_utf16_lossy(v: &[u16]) -> Self where Self: Sized; /// Creates a new `InlinableString` from a length, capacity, and pointer. /// /// # Safety /// /// This is _very_ unsafe because: /// /// * We call `String::from_raw_parts` to get a `Vec`. Therefore, this /// function inherits all of its unsafety, see [its /// documentation](https://doc.rust-lang.org/nightly/collections/vec/struct.Vec.html#method.from_raw_parts) /// for the invariants it expects, they also apply to this function. /// /// * We assume that the `Vec` contains valid UTF-8. unsafe fn from_raw_parts(buf: *mut u8, length: usize, capacity: usize) -> Self where Self: Sized; /// Converts a vector of bytes to a new `InlinableString` without checking /// if it contains valid UTF-8. /// /// # Safety /// /// This is unsafe because it assumes that the UTF-8-ness of the vector has /// already been validated. unsafe fn from_utf8_unchecked(bytes: Vec) -> Self where Self: Sized; /// Returns the underlying byte buffer, encoded as UTF-8. /// /// # Examples /// /// ``` /// use inlinable_string::{InlinableString, StringExt}; /// /// let s = InlinableString::from("hello"); /// let bytes = s.into_bytes(); /// assert_eq!(bytes, [104, 101, 108, 108, 111]); /// ``` fn into_bytes(self) -> Vec; /// Pushes the given string onto this string buffer. /// /// # Examples /// /// ``` /// use inlinable_string::{InlinableString, StringExt}; /// /// let mut s = InlinableString::from("foo"); /// s.push_str("bar"); /// assert_eq!(s, "foobar"); /// ``` fn push_str(&mut self, string: &str); /// Returns the number of bytes that this string buffer can hold without /// reallocating. /// /// # Examples /// /// ``` /// use inlinable_string::{InlinableString, StringExt}; /// /// let s = InlinableString::with_capacity(10); /// assert!(s.capacity() >= 10); /// ``` fn capacity(&self) -> usize; /// Reserves capacity for at least `additional` more bytes to be inserted /// in the given `InlinableString`. The collection may reserve more space to avoid /// frequent reallocations. /// /// # Panics /// /// Panics if the new capacity overflows `usize`. /// /// # Examples /// /// ``` /// use inlinable_string::{InlinableString, StringExt}; /// /// let mut s = InlinableString::new(); /// s.reserve(10); /// assert!(s.capacity() >= 10); /// ``` fn reserve(&mut self, additional: usize); /// Reserves the minimum capacity for exactly `additional` more bytes to be /// inserted in the given `InlinableString`. Does nothing if the capacity is already /// sufficient. /// /// Note that the allocator may give the collection more space than it /// requests. Therefore capacity can not be relied upon to be precisely /// minimal. Prefer `reserve` if future insertions are expected. /// /// # Panics /// /// Panics if the new capacity overflows `usize`. /// /// # Examples /// /// ``` /// use inlinable_string::{InlinableString, StringExt}; /// /// let mut s = InlinableString::new(); /// s.reserve_exact(10); /// assert!(s.capacity() >= 10); /// ``` fn reserve_exact(&mut self, additional: usize); /// Shrinks the capacity of this string buffer to match its length. If the /// string's length is less than `INLINE_STRING_CAPACITY` and the string is /// heap-allocated, then it is demoted to inline storage. /// /// # Examples /// /// ``` /// use inlinable_string::{InlinableString, StringExt}; /// /// let mut s = InlinableString::from("foo"); /// s.reserve(100); /// assert!(s.capacity() >= 100); /// s.shrink_to_fit(); /// assert_eq!(s.capacity(), inlinable_string::INLINE_STRING_CAPACITY); /// ``` fn shrink_to_fit(&mut self); /// Adds the given character to the end of the string. /// /// # Examples /// /// ``` /// use inlinable_string::{InlinableString, StringExt}; /// /// let mut s = InlinableString::from("abc"); /// s.push('1'); /// s.push('2'); /// s.push('3'); /// assert_eq!(s, "abc123"); /// ``` fn push(&mut self, ch: char); /// Works with the underlying buffer as a byte slice. /// /// # Examples /// /// ``` /// use inlinable_string::{InlinableString, StringExt}; /// /// let s = InlinableString::from("hello"); /// assert_eq!(s.as_bytes(), [104, 101, 108, 108, 111]); /// ``` fn as_bytes(&self) -> &[u8]; /// Shortens a string to the specified length. /// /// # Panics /// /// Panics if `new_len` > current length, or if `new_len` is not a character /// boundary. /// /// # Examples /// /// ``` /// use inlinable_string::{InlinableString, StringExt}; /// /// let mut s = InlinableString::from("hello"); /// s.truncate(2); /// assert_eq!(s, "he"); /// ``` fn truncate(&mut self, new_len: usize); /// Removes the last character from the string buffer and returns it. /// Returns `None` if this string buffer is empty. /// /// # Examples /// /// ``` /// use inlinable_string::{InlinableString, StringExt}; /// /// let mut s = InlinableString::from("foo"); /// assert_eq!(s.pop(), Some('o')); /// assert_eq!(s.pop(), Some('o')); /// assert_eq!(s.pop(), Some('f')); /// assert_eq!(s.pop(), None); /// ``` fn pop(&mut self) -> Option; /// Removes the character from the string buffer at byte position `idx` and /// returns it. /// /// # Warning /// /// This is an O(n) operation as it requires copying every element in the /// buffer. /// /// # Panics /// /// If `idx` does not lie on a character boundary, or if it is out of /// bounds, then this function will panic. /// /// # Examples /// /// ``` /// use inlinable_string::{InlinableString, StringExt}; /// /// let mut s = InlinableString::from("foo"); /// assert_eq!(s.remove(0), 'f'); /// assert_eq!(s.remove(1), 'o'); /// assert_eq!(s.remove(0), 'o'); /// ``` fn remove(&mut self, idx: usize) -> char; /// Inserts a character into the string buffer at byte position `idx`. /// /// # Warning /// /// This is an O(n) operation as it requires copying every element in the /// buffer. /// /// # Examples /// /// ``` /// use inlinable_string::{InlinableString, StringExt}; /// /// let mut s = InlinableString::from("foo"); /// s.insert(2, 'f'); /// assert!(s == "fofo"); /// ``` /// /// # Panics /// /// If `idx` does not lie on a character boundary or is out of bounds, then /// this function will panic. fn insert(&mut self, idx: usize, ch: char); /// Inserts a string into the string buffer at byte position `idx`. /// /// # Warning /// /// This is an O(n) operation as it requires copying every element in the /// buffer. /// /// # Examples /// /// ``` /// use inlinable_string::{InlinableString, StringExt}; /// /// let mut s = InlinableString::from("foo"); /// s.insert_str(2, "bar"); /// assert!(s == "fobaro"); /// ``` /// /// # Panics /// /// If `idx` does not lie on a character boundary or is out of bounds, then /// this function will panic. fn insert_str(&mut self, idx: usize, string: &str); /// Views the string buffer as a mutable sequence of bytes. /// /// # Safety /// /// This is unsafe because it does not check to ensure that the resulting /// string will be valid UTF-8. /// /// # Examples /// /// ``` /// use inlinable_string::{InlinableString, StringExt}; /// /// let mut s = InlinableString::from("hello"); /// unsafe { /// let slice = s.as_mut_slice(); /// assert!(slice == &[104, 101, 108, 108, 111]); /// slice.reverse(); /// } /// assert_eq!(s, "olleh"); /// ``` unsafe fn as_mut_slice(&mut self) -> &mut [u8]; /// Returns the number of bytes in this string. /// /// # Examples /// /// ``` /// use inlinable_string::{InlinableString, StringExt}; /// /// let a = InlinableString::from("foo"); /// assert_eq!(a.len(), 3); /// ``` fn len(&self) -> usize; /// Returns true if the string contains no bytes /// /// # Examples /// /// ``` /// use inlinable_string::{InlinableString, StringExt}; /// /// let mut v = InlinableString::new(); /// assert!(v.is_empty()); /// v.push('a'); /// assert!(!v.is_empty()); /// ``` #[inline] fn is_empty(&self) -> bool { self.len() == 0 } /// Truncates the string, returning it to 0 length. /// /// # Examples /// /// ``` /// use inlinable_string::{InlinableString, StringExt}; /// /// let mut s = InlinableString::from("foo"); /// s.clear(); /// assert!(s.is_empty()); /// ``` #[inline] fn clear(&mut self) { self.truncate(0); } } impl<'a> StringExt<'a> for String { #[inline] fn new() -> Self { String::new() } #[inline] fn with_capacity(capacity: usize) -> Self { String::with_capacity(capacity) } #[inline] fn from_utf8(vec: Vec) -> Result { String::from_utf8(vec) } #[inline] fn from_utf16(v: &[u16]) -> Result { String::from_utf16(v) } #[inline] fn from_utf16_lossy(v: &[u16]) -> Self { String::from_utf16_lossy(v) } #[inline] unsafe fn from_raw_parts(buf: *mut u8, length: usize, capacity: usize) -> Self { String::from_raw_parts(buf, length, capacity) } #[inline] unsafe fn from_utf8_unchecked(bytes: Vec) -> Self { String::from_utf8_unchecked(bytes) } #[inline] fn into_bytes(self) -> Vec { String::into_bytes(self) } #[inline] fn push_str(&mut self, string: &str) { String::push_str(self, string) } #[inline] fn capacity(&self) -> usize { String::capacity(self) } #[inline] fn reserve(&mut self, additional: usize) { String::reserve(self, additional) } #[inline] fn reserve_exact(&mut self, additional: usize) { String::reserve_exact(self, additional) } #[inline] fn shrink_to_fit(&mut self) { String::shrink_to_fit(self) } #[inline] fn push(&mut self, ch: char) { String::push(self, ch) } #[inline] fn as_bytes(&self) -> &[u8] { String::as_bytes(self) } #[inline] fn truncate(&mut self, new_len: usize) { String::truncate(self, new_len) } #[inline] fn pop(&mut self) -> Option { String::pop(self) } #[inline] fn remove(&mut self, idx: usize) -> char { String::remove(self, idx) } #[inline] fn insert(&mut self, idx: usize, ch: char) { String::insert(self, idx, ch) } #[inline] fn insert_str(&mut self, idx: usize, string: &str) { String::insert_str(self, idx, string) } #[inline] unsafe fn as_mut_slice(&mut self) -> &mut [u8] { &mut *(self.as_mut_str() as *mut str as *mut [u8]) } #[inline] fn len(&self) -> usize { String::len(self) } } #[cfg(test)] mod std_string_stringext_sanity_tests { // Sanity tests for std::string::String's StringExt implementation. use alloc::string::String; use super::StringExt; #[test] fn test_new() { let s = ::new(); assert!(StringExt::is_empty(&s)); } #[test] fn test_with_capacity() { let s = ::with_capacity(10); assert!(StringExt::capacity(&s) >= 10); } #[test] fn test_from_utf8() { let s = ::from_utf8(vec![104, 101, 108, 108, 111]); assert_eq!(s.unwrap(), "hello"); } #[test] fn test_from_utf16() { let v = &mut [0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0x0069, 0x0063]; let s = ::from_utf16(v); assert_eq!(s.unwrap(), "𝄞music"); } #[test] fn test_from_utf16_lossy() { let input = b"Hello \xF0\x90\x80World"; let output = ::from_utf8_lossy(input); assert_eq!(output, "Hello \u{FFFD}World"); } #[test] fn test_into_bytes() { let s = String::from("hello"); let bytes = StringExt::into_bytes(s); assert_eq!(bytes, [104, 101, 108, 108, 111]); } #[test] fn test_push_str() { let mut s = String::from("hello"); StringExt::push_str(&mut s, " world"); assert_eq!(s, "hello world"); } #[test] fn test_capacity() { let s = ::with_capacity(100); assert!(String::capacity(&s) >= 100); } #[test] fn test_reserve() { let mut s = ::new(); StringExt::reserve(&mut s, 100); assert!(String::capacity(&s) >= 100); } #[test] fn test_reserve_exact() { let mut s = ::new(); StringExt::reserve_exact(&mut s, 100); assert!(String::capacity(&s) >= 100); } #[test] fn test_shrink_to_fit() { let mut s = ::with_capacity(100); StringExt::push_str(&mut s, "foo"); StringExt::shrink_to_fit(&mut s); assert_eq!(String::capacity(&s), 3); } #[test] fn test_push() { let mut s = String::new(); StringExt::push(&mut s, 'a'); assert_eq!(s, "a"); } #[test] fn test_truncate() { let mut s = String::from("foo"); StringExt::truncate(&mut s, 1); assert_eq!(s, "f"); } #[test] fn test_pop() { let mut s = String::from("foo"); assert_eq!(StringExt::pop(&mut s), Some('o')); assert_eq!(StringExt::pop(&mut s), Some('o')); assert_eq!(StringExt::pop(&mut s), Some('f')); assert_eq!(StringExt::pop(&mut s), None); } }