Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
workflow_dispatch:
inputs:
version:
description: "Version to release, for example 1.1.0"
description: "Version to release, for example 1.2.0"
required: true
type: string

Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "cheetah-string"
version = "1.1.0"
version = "1.2.0"
authors = ["mxsm <mxsm@apache.org>"]
edition = "2021"
homepage = "https://github.com/mxsm/cheetah-string"
Expand Down
21 changes: 14 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,21 +46,22 @@ Add this to your `Cargo.toml`:

```toml
[dependencies]
cheetah-string = "1.1.0"
cheetah-string = "1.2.0"
```

### Optional Features

```toml
[dependencies]
cheetah-string = { version = "1.1.0", features = ["bytes", "serde", "simd"] }
cheetah-string = { version = "1.2.0", features = ["bytes", "serde", "simd"] }
```

Available features:
- `std` (default): Enable standard library support
- `bytes`: `CheetahBytes` and integration with the `bytes` crate
- `serde`: Serialization support via serde
- `simd`: SIMD-accelerated string operations (x86_64 SSE2)
- `experimental-packed`: Experimental packed representation prototype

## 🚀 Quick Start

Expand Down Expand Up @@ -94,6 +95,11 @@ builder.push_str("Hello");
builder.push_str(", ");
builder.push_str("World!");

// Explicit String storage policy
let mut owned = CheetahString::from_string_owned(String::with_capacity(128));
owned.push_str("capacity-preserving");
let shared = CheetahString::from_string_shared("clone-cheap".repeat(16));

// Safe UTF-8 validation
let bytes = b"hello";
let s = CheetahString::try_from_bytes(bytes).unwrap();
Expand Down Expand Up @@ -125,18 +131,19 @@ CheetahString intelligently chooses the most efficient storage:
|-------------|---------|------------------|----------|
| ≤ 23 bytes | Inline (SSO) | 0 | Short strings, identifiers |
| Static | `&'static str` | 0 | String literals |
| Dynamic | `Arc<str>` | 1 | Long immutable strings, shared data |
| Builder | `String` | 1 | Reserved capacity, repeated mutation |
| From Arc | `Arc<String>` | 1 | Interop with existing Arc |
| Bytes | `bytes::Bytes` | 1 | Network buffers (with feature) |
| Shared | `Arc<str>` | 1 | Long immutable strings, shared data |
| Owned | `String` | 1 | Reserved capacity, repeated mutation |
| Bytes | `CheetahBytes` | 1 | Byte-oriented network buffers (with feature) |

## 🔧 API Overview

### Construction
- `new()`, `empty()`, `default()` - Create empty strings
- `from(s)` - From `&str`, `String`, `&String`, `char`, etc.
- `from_static_str(s)` - Zero-cost wrapper for `'static str`
- `from_string(s)` - From owned `String`
- `from_string(s)` - From owned `String` with the 1.x shared-long-string policy
- `from_string_owned(s)` - Preserve `String` ownership and spare capacity for mutation
- `from_string_shared(s)` - Convert long owned strings to clone-cheap shared storage
- `try_from_bytes(b)` - Safe construction from bytes with UTF-8 validation
- `CheetahBytes` - Byte-oriented companion type available with the `bytes` feature
- `with_capacity(n)` - Pre-allocate capacity
Expand Down
2 changes: 1 addition & 1 deletion bench-results/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Minimum metadata for generated JSON artifacts:
```json
{
"crate": "cheetah-string",
"version": "1.1.0",
"version": "1.2.0",
"profile": "release",
"target": "x86_64-unknown-linux-gnu",
"rustc": "rustc 1.xx.x",
Expand Down
24 changes: 23 additions & 1 deletion src/cheetah_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,28 @@ impl CheetahString {

#[inline]
pub fn from_string(s: String) -> Self {
CheetahString::from_string_shared(s)
}

/// Creates a `CheetahString` from an owned `String` while preserving
/// ownership and spare capacity for later mutation.
///
/// This constructor is intended for builder-style paths that will continue
/// appending to the string. It keeps long strings in owned storage instead
/// of converting them to shared storage.
#[inline]
pub fn from_string_owned(s: String) -> Self {
CheetahString::from_builder_string(s)
}

/// Creates a `CheetahString` from an owned `String` using shared storage
/// for long immutable strings.
///
/// This is the same storage policy used by `from_string` in the 1.x line.
/// Use `from_string_owned` when spare capacity should be preserved for
/// later mutation.
#[inline]
pub fn from_string_shared(s: String) -> Self {
if s.len() <= INLINE_CAPACITY {
// Use inline storage for short strings
let mut data = [0u8; INLINE_CAPACITY];
Expand Down Expand Up @@ -1206,7 +1228,7 @@ impl Add<String> for CheetahString {
#[inline]
fn add(mut self, rhs: String) -> Self::Output {
if self.is_empty() {
return CheetahString::from_string(rhs);
return CheetahString::from_string_owned(rhs);
}

self.push_str_internal(&rhs);
Expand Down
4 changes: 3 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
//! It is usable in both `std` and `no_std` environments. Additionally, CheetahString supports serde for serialization and deserialization.
//! The `bytes` feature exposes `CheetahBytes` for byte-oriented data.
//! It minimizes allocations across small, shared, and builder-oriented string workloads.
//! The `from_string_owned` and `from_string_shared` constructors make owned
//! mutation and clone-cheap sharing policies explicit.
//! Substring search uses `memchr`/`memmem` by default.
//!
//! # SIMD Acceleration
Expand All @@ -21,7 +23,7 @@
//! To enable SIMD acceleration:
//! ```toml
//! [dependencies]
//! cheetah-string = { version = "1.1.0", features = ["simd"] }
//! cheetah-string = { version = "1.2.0", features = ["simd"] }
//! ```
//!
//! # Examples
Expand Down
23 changes: 23 additions & 0 deletions tests/mutation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,29 @@ fn add_reuses_owned_lhs_capacity() {
assert_eq!(s.as_bytes().as_ptr(), before);
}

#[test]
fn from_string_owned_preserves_spare_capacity_for_push() {
let mut raw = String::with_capacity(128);
raw.push_str("hello");

let mut s = CheetahString::from_string_owned(raw);
let before = s.as_bytes().as_ptr();

s.push_str(" world");

assert_eq!(s, "hello world");
assert_eq!(s.as_bytes().as_ptr(), before);
}

#[test]
fn from_string_shared_keeps_long_immutable_inputs_clone_cheap() {
let s = CheetahString::from_string_shared("a".repeat(128));
let cloned = s.clone();

assert_eq!(s.as_str(), cloned.as_str());
assert_eq!(s.as_bytes().as_ptr(), cloned.as_bytes().as_ptr());
}

#[test]
fn reserve_zero_keeps_existing_buffer() {
let mut s = CheetahString::with_capacity(128);
Expand Down
Loading