diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-08-01 04:52:50 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-08-01 04:52:50 +0900 |
| commit | b8d46b0495d00815a699932ced0b43955c949ab9 (patch) | |
| tree | 17c340f51cbf259ea631ca7da7549714c91b916f /crates/shirabe-external-packages | |
| parent | 8e8a3c147aa388c4b0fc021a08b30113eb590727 (diff) | |
| download | php-shirabe-b8d46b0495d00815a699932ced0b43955c949ab9.tar.gz php-shirabe-b8d46b0495d00815a699932ced0b43955c949ab9.tar.zst php-shirabe-b8d46b0495d00815a699932ced0b43955c949ab9.zip | |
feat(php-src): add a BSD-licensed crate for php-src derived code
The audit in .ken/php-shim-copying.md judged 14 functions in
shirabe-php-shim (plus php_wordwrap in shirabe-external-packages) to be
line-by-line transcriptions or structural imitations of php-src. PHP's
relicensing to 3-clause BSD makes keeping them legal, but the boundary
between BSD-derived and MIT code was invisible in the source tree.
Moving them into their own crate puts the license into the build
metadata (so NOTICE generation follows the binary), makes a reverse
dependency a compile error, and encodes the origin in the module path,
which mirrors php-src's ext tree. Each function records its origin in a
fixed-format doc comment, and a new php_src_derivation_boundary linter
fails if `php-src` appears in any Rust source outside the crate.
Public paths under shirabe_php_shim:: are unchanged: functions that are
themselves derived are re-exported with `pub use`, and the wrappers that
only validate arguments stay on the MIT side.
This also resolves the duplicate wordwrap implementation.
shirabe_php_shim::wordwrap was todo!(), so SymfonyStyle::block panicked,
while shirabe-external-packages carried its own copy. Both now go
through the single port, verified against real PHP on 13 cases covering
multi-character breaks and cut.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe-external-packages')
| -rw-r--r-- | crates/shirabe-external-packages/src/symfony/string/code_point_string.rs | 83 |
1 files changed, 1 insertions, 82 deletions
diff --git a/crates/shirabe-external-packages/src/symfony/string/code_point_string.rs b/crates/shirabe-external-packages/src/symfony/string/code_point_string.rs index 97ee2508..dcbaa1f6 100644 --- a/crates/shirabe-external-packages/src/symfony/string/code_point_string.rs +++ b/crates/shirabe-external-packages/src/symfony/string/code_point_string.rs @@ -42,7 +42,7 @@ impl CodePointString { let mut j: usize = 0; // PHP seeds both `$b` and `$i` at -1; mirror with signed indices. let mut i: i64 = -1; - let mask = php_wordwrap(&mask, width, "#", cut); + let mask = shirabe_php_shim::wordwrap(&mask, width, "#", cut); let mask_bytes = mask.as_bytes(); let mut b: i64 = -1; @@ -91,84 +91,3 @@ impl CodePointString { ) } } - -/// Port of PHP's built-in `wordwrap()` (`PHP_FUNCTION(wordwrap)` in ext/standard/string.c). -/// Byte-based, matching PHP's single-byte/multi-byte break and cut handling. -fn php_wordwrap(text: &str, linelength: i64, breakchar: &str, docut: bool) -> String { - let text = text.as_bytes(); - let breakchar = breakchar.as_bytes(); - let textlen = text.len() as i64; - let breaklen = breakchar.len() as i64; - - if textlen == 0 { - return String::new(); - } - - let mut laststart: i64 = 0; - let mut lastspace: i64 = 0; - - // Special case for a single-character break that needs no extra storage. - if breaklen == 1 && !docut { - let mut out = text.to_vec(); - let mut current = 0i64; - while current < textlen { - let c = out[current as usize]; - if c == breakchar[0] { - laststart = current + 1; - lastspace = current + 1; - } else if c == b' ' { - if current - laststart >= linelength { - out[current as usize] = breakchar[0]; - laststart = current + 1; - } - lastspace = current; - } else if current - laststart >= linelength && laststart != lastspace { - out[lastspace as usize] = breakchar[0]; - laststart = lastspace + 1; - } - current += 1; - } - return String::from_utf8_lossy(&out).into_owned(); - } - - // Multiple character line break or forced cut. - let mut out: Vec<u8> = Vec::new(); - let mut current = 0i64; - while current < textlen { - // When we hit an existing break, copy to the new buffer and fix up laststart/lastspace. - if text[current as usize] == breakchar[0] - && current + breaklen < textlen - && &text[current as usize..(current + breaklen) as usize] == breakchar - { - out.extend_from_slice(&text[laststart as usize..(current + breaklen) as usize]); - current += breaklen - 1; - laststart = current + 1; - lastspace = current + 1; - } else if text[current as usize] == b' ' { - if current - laststart >= linelength { - out.extend_from_slice(&text[laststart as usize..current as usize]); - out.extend_from_slice(breakchar); - laststart = current + 1; - } - lastspace = current; - } else if current - laststart >= linelength && docut && laststart >= lastspace { - out.extend_from_slice(&text[laststart as usize..current as usize]); - out.extend_from_slice(breakchar); - laststart = current; - lastspace = current; - } else if current - laststart >= linelength && laststart < lastspace { - out.extend_from_slice(&text[laststart as usize..lastspace as usize]); - out.extend_from_slice(breakchar); - laststart = lastspace + 1; - lastspace += 1; - } - current += 1; - } - - // Copy over any stragglers. - if laststart != current { - out.extend_from_slice(&text[laststart as usize..current as usize]); - } - - String::from_utf8_lossy(&out).into_owned() -} |
