commonlibsse_ng\re\b/
BSString.rs1use core::marker::PhantomData;
2use core::{alloc::Layout, fmt};
3use std::ffi::{CStr, CString};
4
5use std_fork::alloc::SelflessAllocator;
6use stdx::ptr::Unique;
7
8use crate::re::MemoryManager::TESGlobalAlloc;
9
10#[repr(C)]
33pub struct BSString<A = TESGlobalAlloc>
34where
35 A: SelflessAllocator,
36{
37 data: Option<Unique<u8>>,
39 size: u16,
41 capacity: u16,
43
44 pad0C: u32,
45
46 alloc: PhantomData<A>,
48}
49const _: () = assert!(core::mem::size_of::<BSString>() == 0x10);
50
51#[derive(Copy, Clone, PartialEq, Eq, Debug)]
52pub enum BSStringError {
53 TooLong,
55 AllocFailed,
57 InteriorNul,
59}
60
61impl core::error::Error for BSStringError {}
62impl fmt::Display for BSStringError {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 match self {
65 Self::TooLong => f.write_str("string is too long. max is u16::MAX(65535) bytes"),
66 Self::AllocFailed => f.write_str("allocation failed"),
67 Self::InteriorNul => f.write_str("string contains interior null bytes"),
68 }
69 }
70}
71
72impl BSString {
73 #[inline]
83 pub const fn new() -> Self {
84 Self::new_in()
85 }
86}
87
88impl<A> BSString<A>
89where
90 A: SelflessAllocator,
91{
92 #[inline]
104 pub const fn new_in() -> Self {
105 Self { data: None, size: 0, capacity: 0, pad0C: 0, alloc: PhantomData }
106 }
107
108 pub fn from_c_str(s: &CStr) -> Result<Self, BSStringError> {
123 let mut string = Self::new_in();
124 string.set_c_str(s)?;
125 Ok(string)
126 }
127
128 #[inline]
141 pub fn clear(&mut self) {
142 if self.capacity == 0 {
143 return;
144 }
145
146 if let Some(ptr) = self.data.take() {
148 unsafe { A::deallocate(ptr.as_non_null_ptr(), self.layout()) };
149 }
150 self.size = 0;
151 self.capacity = 0;
152 }
153
154 pub fn set_c_str(&mut self, cstr: &CStr) -> Result<(), BSStringError> {
171 let bytes = cstr.to_bytes_with_nul();
172 let len = bytes.len();
173
174 if len == 0 {
175 self.clear();
176 return Ok(());
177 }
178 if len > (u16::MAX as usize) {
179 return Err(BSStringError::TooLong);
180 }
181
182 let len = len as u16;
183
184 let new_layout = Self::new_layout(len);
185
186 let mut reuse = false;
187 let new_ptr = unsafe {
188 match self.data {
189 Some(old_ptr) => {
190 let old_layout = self.layout();
191 if new_layout.size() > old_layout.size() {
192 A::grow(old_ptr.as_non_null_ptr(), old_layout, new_layout)
193 .map_err(|_| BSStringError::AllocFailed)?
194 .cast()
195 } else {
196 reuse = true;
197 old_ptr.as_non_null_ptr() }
199 }
200 None => A::allocate(new_layout).map_err(|_| BSStringError::AllocFailed)?.cast(),
201 }
202 };
203
204 unsafe { core::ptr::copy_nonoverlapping(bytes.as_ptr(), new_ptr.as_ptr(), len as usize) };
205
206 self.data = Some(Unique::from(new_ptr));
207 self.size = len;
208 if !reuse {
209 self.capacity = len;
210 }
211 Ok(())
212 }
213
214 #[inline]
224 pub const fn is_empty(&self) -> bool {
225 self.size == 0
226 }
227
228 #[inline]
239 pub const fn len(&self) -> usize {
240 self.size as usize
241 }
242
243 #[inline]
254 pub const fn capacity(&self) -> u16 {
255 self.capacity
256 }
257
258 #[inline]
269 pub const fn as_bytes_with_null(&self) -> &[u8] {
270 if self.size == 0 {
271 return &[];
272 }
273
274 match self.data {
275 Some(ref data) => unsafe { core::slice::from_raw_parts(data.as_ptr(), self.len()) },
276 None => &[],
277 }
278 }
279
280 #[inline]
291 pub const fn as_c_str(&self) -> &CStr {
292 if self.size == 0 {
293 return c"";
294 }
295 unsafe { CStr::from_bytes_with_nul_unchecked(self.as_bytes_with_null()) }
296 }
297
298 fn layout(&self) -> Layout {
304 Self::new_layout(self.capacity)
305 }
306
307 fn new_layout(n: u16) -> Layout {
313 Layout::array::<u8>(n as usize).expect("BSTString need: alloc size < isize::MAX")
314 }
315}
316
317impl<A> Drop for BSString<A>
318where
319 A: SelflessAllocator,
320{
321 #[inline]
322 fn drop(&mut self) {
323 self.clear();
324 }
325}
326
327impl Default for BSString {
328 #[inline]
329 fn default() -> Self {
330 Self::new()
331 }
332}
333
334impl<A> fmt::Debug for BSString<A>
335where
336 A: SelflessAllocator,
337{
338 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
339 f.debug_struct("BSString")
340 .field("data", &self.as_c_str())
341 .field("size", &self.size)
342 .field("capacity", &self.capacity)
343 .finish()
344 }
345}
346
347impl<A> Clone for BSString<A>
348where
349 A: SelflessAllocator,
350{
351 fn clone(&self) -> Self {
352 if self.size == 0 {
353 return Self::new_in();
354 }
355 let Some(data) = self.data else {
356 return Self::new_in();
357 };
358
359 let len = self.size;
360 let layout = Self::new_layout(len);
361
362 let ptr = unsafe {
363 let new_ptr = A::allocate(layout).expect("allocation failed in clone").cast();
364 core::ptr::copy_nonoverlapping(data.as_ptr(), new_ptr.as_ptr(), len as usize);
365 new_ptr
366 };
367
368 Self {
369 data: Some(unsafe { Unique::new_unchecked(ptr.as_ptr()) }),
370 size: len,
371 capacity: len,
372 pad0C: 0,
373 alloc: PhantomData,
374 }
375 }
376}
377
378impl<A, B> PartialEq<BSString<B>> for BSString<A>
379where
380 A: SelflessAllocator,
381 B: SelflessAllocator,
382{
383 fn eq(&self, other: &BSString<B>) -> bool {
384 self.as_c_str() == other.as_c_str()
385 }
386}
387
388impl<A> PartialEq<CStr> for BSString<A>
389where
390 A: SelflessAllocator,
391{
392 fn eq(&self, other: &CStr) -> bool {
393 self.as_c_str() == other
394 }
395}
396
397impl<A> Eq for BSString<A> where A: SelflessAllocator {}
398
399impl<A, B> PartialOrd<BSString<B>> for BSString<A>
400where
401 A: SelflessAllocator,
402 B: SelflessAllocator,
403{
404 fn partial_cmp(&self, other: &BSString<B>) -> Option<core::cmp::Ordering> {
405 self.as_c_str().partial_cmp(other.as_c_str())
406 }
407}
408
409impl<A> Ord for BSString<A>
410where
411 A: SelflessAllocator,
412{
413 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
414 self.as_c_str().cmp(other.as_c_str())
415 }
416}
417
418impl<A> core::hash::Hash for BSString<A>
419where
420 A: SelflessAllocator,
421{
422 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
423 self.as_c_str().hash(state);
424 }
425}
426
427impl<A: SelflessAllocator> TryFrom<CString> for BSString<A> {
430 type Error = BSStringError;
431
432 fn try_from(c_string: CString) -> Result<Self, Self::Error> {
433 let bytes = c_string.into_bytes_with_nul();
434 let len = bytes.len();
435
436 if len > u16::MAX as usize {
437 return Err(BSStringError::TooLong);
438 }
439
440 let len_u16 = len as u16;
441 let layout = Self::new_layout(len_u16);
442
443 let ptr = unsafe {
444 let ptr = A::allocate(layout).map_err(|_| BSStringError::AllocFailed)?.cast();
445 core::ptr::copy_nonoverlapping(bytes.as_ptr(), ptr.as_ptr(), len);
446 ptr
447 };
448
449 Ok(Self {
450 data: Some(Unique::from(ptr)),
451 size: len_u16,
452 capacity: len_u16,
453 pad0C: 0,
454 alloc: PhantomData,
455 })
456 }
457}
458
459impl<A: SelflessAllocator> TryFrom<&str> for BSString<A> {
460 type Error = BSStringError;
461
462 #[inline]
463 fn try_from(s: &str) -> Result<Self, Self::Error> {
464 let c_string = CString::new(s).map_err(|_| BSStringError::InteriorNul)?;
465 Self::try_from(c_string)
466 }
467}
468
469impl<A: SelflessAllocator> TryFrom<String> for BSString<A> {
470 type Error = BSStringError;
471
472 #[inline]
473 fn try_from(s: String) -> Result<Self, Self::Error> {
474 Self::try_from(s.as_str())
475 }
476}
477
478#[cfg(test)]
479mod tests {
480 use super::*;
481
482 type BSString = super::BSString<stdx::alloc::Global>;
483
484 #[test]
485 fn test_bs_string() {
486 let mut bs = BSString::new_in();
487
488 assert_eq!(bs.len(), 0);
489 assert_eq!(bs.as_c_str().to_str(), Ok(""));
490
491 let input = c"Hello, Rust!";
492 bs.set_c_str(input).unwrap();
493
494 assert_eq!(bs.len(), 13);
495 assert_eq!(bs.as_c_str().to_str(), Ok("Hello, Rust!"));
496
497 let input2 = c"Short";
498 bs.set_c_str(input2).unwrap();
499
500 assert_eq!(bs.len(), 6);
501 assert_eq!(bs.as_c_str().to_str(), Ok("Short"));
502 }
503
504 #[test]
505 fn test_too_long_cstr() {
506 let too_large_vec = {
507 let mut v = vec![b'a'; u16::MAX as usize + 1];
508 v.push(0); v
510 };
511
512 let too_large_c_string =
513 CString::from_vec_with_nul(too_large_vec).expect("CString too long for CStr");
514
515 let too_large_cstr = too_large_c_string.as_c_str();
516
517 let mut bs = BSString::new_in();
518 assert_eq!(bs.set_c_str(too_large_cstr), Err(BSStringError::TooLong));
519 }
520}