aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe-external-packages
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-08-01 04:52:50 +0900
committernsfisis <nsfisis@gmail.com>2026-08-01 04:52:50 +0900
commitb8d46b0495d00815a699932ced0b43955c949ab9 (patch)
tree17c340f51cbf259ea631ca7da7549714c91b916f /crates/shirabe-external-packages
parent8e8a3c147aa388c4b0fc021a08b30113eb590727 (diff)
downloadphp-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.rs83
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()
-}