Skip to content

Commit b18e464

Browse files
author
Thomas Bahn
authored
Merge pull request #59 from burtonageo/cstring_conv
Implement conversion methods on {CStr, CString}
2 parents 4c36826 + 8e2ceea commit b18e464

File tree

3 files changed

+149
-28
lines changed

3 files changed

+149
-28
lines changed

RELEASES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
Unreleased
22
==========
33
* Implement the `std::ops::AddAssign` trait for `AsciiString`.
4+
* Implement the `IntoAsciiString` trait for `std::ffi::CStr` and `std::ffi::CString` types,
5+
and implemented the `AsAsciiStr` trait for `std::ffi::CStr` type.
46
* Implement the `IntoAsciiString` for `std::borrow::Cow`, where the inner types themselves
57
implement `IntoAsciiString`.
68

src/ascii_str.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ use core::slice::{Iter, IterMut};
1111
use std::error::Error;
1212
#[cfg(feature = "std")]
1313
use std::ascii::AsciiExt;
14+
#[cfg(feature = "std")]
15+
use std::ffi::CStr;
1416

1517
use ascii_char::AsciiChar;
1618
#[cfg(feature = "std")]
@@ -805,6 +807,18 @@ impl AsMutAsciiStr for str {
805807
}
806808
}
807809

810+
/// Note that the trailing null byte will be removed in the conversion.
811+
#[cfg(feature = "std")]
812+
impl AsAsciiStr for CStr {
813+
#[inline]
814+
fn as_ascii_str(&self) -> Result<&AsciiStr, AsAsciiStrError> {
815+
self.to_bytes().as_ascii_str()
816+
}
817+
#[inline]
818+
unsafe fn as_ascii_str_unchecked(&self) -> &AsciiStr {
819+
self.to_bytes().as_ascii_str_unchecked()
820+
}
821+
}
808822

809823
#[cfg(test)]
810824
mod tests {
@@ -826,6 +840,19 @@ mod tests {
826840
assert_eq!(generic(&mut "A"), Ok(ascii_str));
827841
}
828842

843+
#[cfg(feature = "std")]
844+
#[test]
845+
fn cstring_as_ascii_str() {
846+
use std::ffi::CString;
847+
fn generic<C: AsAsciiStr + ?Sized>(c: &C) -> Result<&AsciiStr, AsAsciiStrError> {
848+
c.as_ascii_str()
849+
}
850+
let arr = [AsciiChar::A];
851+
let ascii_str: &AsciiStr = arr.as_ref().into();
852+
let cstr = CString::new("A").unwrap();
853+
assert_eq!(generic(&*cstr), Ok(ascii_str));
854+
}
855+
829856
#[test]
830857
fn generic_as_mut_ascii_str() {
831858
fn generic_mut<C: AsMutAsciiStr + ?Sized>(

src/ascii_string.rs

Lines changed: 120 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use std::{fmt, mem};
44
use std::borrow::{Borrow, BorrowMut, Cow};
55
use std::error::Error;
6+
use std::ffi::{CStr, CString};
67
use std::any::Any;
78
use std::str::FromStr;
89
use std::ops::{Deref, DerefMut, Add, AddAssign, Index, IndexMut};
@@ -447,6 +448,20 @@ impl Into<Vec<u8>> for AsciiString {
447448
}
448449
}
449450

451+
impl<'a> From<&'a AsciiStr> for AsciiString {
452+
#[inline]
453+
fn from(s: &'a AsciiStr) -> Self {
454+
s.to_ascii_string()
455+
}
456+
}
457+
458+
impl<'a> From<&'a [AsciiChar]> for AsciiString {
459+
#[inline]
460+
fn from(s: &'a [AsciiChar]) -> AsciiString {
461+
s.into_iter().map(|c| *c).collect()
462+
}
463+
}
464+
450465
impl Into<String> for AsciiString {
451466
#[inline]
452467
fn into(self) -> String {
@@ -718,17 +733,6 @@ pub trait IntoAsciiString: Sized {
718733
fn into_ascii_string(self) -> Result<AsciiString, FromAsciiError<Self>>;
719734
}
720735

721-
impl IntoAsciiString for AsciiString {
722-
#[inline]
723-
unsafe fn into_ascii_string_unchecked(self) -> AsciiString {
724-
self
725-
}
726-
#[inline]
727-
fn into_ascii_string(self) -> Result<Self, FromAsciiError<Self>> {
728-
Ok(self)
729-
}
730-
}
731-
732736
impl IntoAsciiString for Vec<AsciiChar> {
733737
#[inline]
734738
unsafe fn into_ascii_string_unchecked(self) -> AsciiString {
@@ -740,47 +744,113 @@ impl IntoAsciiString for Vec<AsciiChar> {
740744
}
741745
}
742746

743-
impl IntoAsciiString for Vec<u8> {
747+
impl<'a> IntoAsciiString for &'a [AsciiChar] {
744748
#[inline]
745749
unsafe fn into_ascii_string_unchecked(self) -> AsciiString {
746-
AsciiString::from_ascii_unchecked(self)
750+
AsciiString::from(self)
747751
}
748752
#[inline]
749753
fn into_ascii_string(self) -> Result<AsciiString, FromAsciiError<Self>> {
750-
AsciiString::from_ascii(self)
754+
Ok(AsciiString::from(self))
751755
}
752756
}
753757

754-
impl<'a> IntoAsciiString for &'a [u8] {
758+
impl<'a> IntoAsciiString for &'a AsciiStr {
755759
#[inline]
756760
unsafe fn into_ascii_string_unchecked(self) -> AsciiString {
757-
AsciiString::from_ascii_unchecked(self)
761+
AsciiString::from(self)
758762
}
759763
#[inline]
760764
fn into_ascii_string(self) -> Result<AsciiString, FromAsciiError<Self>> {
761-
AsciiString::from_ascii(self)
765+
Ok(AsciiString::from(self))
762766
}
763767
}
764768

765-
impl IntoAsciiString for String {
769+
macro_rules! impl_into_ascii_string {
770+
('a, $wider:ty) => {
771+
impl<'a> IntoAsciiString for $wider {
772+
#[inline]
773+
unsafe fn into_ascii_string_unchecked(self) -> AsciiString {
774+
AsciiString::from_ascii_unchecked(self)
775+
}
776+
777+
#[inline]
778+
fn into_ascii_string(self) -> Result<AsciiString, FromAsciiError<Self>> {
779+
AsciiString::from_ascii(self)
780+
}
781+
}
782+
};
783+
784+
($wider:ty) => {
785+
impl IntoAsciiString for $wider {
786+
#[inline]
787+
unsafe fn into_ascii_string_unchecked(self) -> AsciiString {
788+
AsciiString::from_ascii_unchecked(self)
789+
}
790+
791+
#[inline]
792+
fn into_ascii_string(self) -> Result<AsciiString, FromAsciiError<Self>> {
793+
AsciiString::from_ascii(self)
794+
}
795+
}
796+
};
797+
}
798+
799+
impl_into_ascii_string!{AsciiString}
800+
impl_into_ascii_string!{Vec<u8>}
801+
impl_into_ascii_string!{'a, &'a [u8]}
802+
impl_into_ascii_string!{String}
803+
impl_into_ascii_string!{'a, &'a str}
804+
805+
/// Note that the trailing null byte will be removed in the conversion.
806+
impl IntoAsciiString for CString {
766807
#[inline]
767808
unsafe fn into_ascii_string_unchecked(self) -> AsciiString {
768-
AsciiString::from_ascii_unchecked(self)
809+
AsciiString::from_ascii_unchecked(self.into_bytes())
769810
}
770-
#[inline]
771-
fn into_ascii_string(self) -> Result<AsciiString, FromAsciiError<Self>> {
772-
AsciiString::from_ascii(self)
773-
}
774-
}
775811

776-
impl<'a> IntoAsciiString for &'a str {
812+
fn into_ascii_string(self) -> Result<AsciiString, FromAsciiError<Self>> {
813+
AsciiString::from_ascii(self.into_bytes_with_nul())
814+
.map_err(|FromAsciiError { error, owner }| {
815+
FromAsciiError {
816+
owner: unsafe {
817+
// The null byte is preserved from the original
818+
// `CString`, so this is safe.
819+
CString::from_vec_unchecked(owner)
820+
},
821+
error: error,
822+
}
823+
})
824+
.map(|mut s| {
825+
let _nul = s.pop();
826+
debug_assert_eq!(_nul, Some(AsciiChar::Null));
827+
s
828+
})
829+
}
830+
}
831+
832+
/// Note that the trailing null byte will be removed in the conversion.
833+
impl<'a> IntoAsciiString for &'a CStr {
777834
#[inline]
778835
unsafe fn into_ascii_string_unchecked(self) -> AsciiString {
779-
AsciiString::from_ascii_unchecked(self)
836+
AsciiString::from_ascii_unchecked(self.to_bytes())
780837
}
781-
#[inline]
838+
782839
fn into_ascii_string(self) -> Result<AsciiString, FromAsciiError<Self>> {
783-
AsciiString::from_ascii(self)
840+
AsciiString::from_ascii(self.to_bytes_with_nul())
841+
.map_err(|FromAsciiError { error, owner }| {
842+
FromAsciiError {
843+
owner: unsafe {
844+
CStr::from_ptr(owner.as_ptr() as *const _)
845+
},
846+
error: error,
847+
}
848+
})
849+
.map(|mut s| {
850+
let _nul = s.pop();
851+
debug_assert_eq!(_nul, Some(AsciiChar::Null));
852+
s
853+
})
784854
}
785855
}
786856

@@ -844,6 +914,7 @@ impl Arbitrary for AsciiString {
844914
#[cfg(test)]
845915
mod tests {
846916
use std::str::FromStr;
917+
use std::ffi::CString;
847918
use AsciiChar;
848919
use super::{AsciiString, IntoAsciiString};
849920

@@ -865,6 +936,27 @@ mod tests {
865936
assert_eq!(AsciiString::from(vec), AsciiString::from_str("AB").unwrap());
866937
}
867938

939+
#[test]
940+
fn from_cstring() {
941+
let cstring = CString::new("baz").unwrap();
942+
let ascii_str = cstring.clone().into_ascii_string().unwrap();
943+
let expected_chars = &[AsciiChar::b, AsciiChar::a, AsciiChar::z];
944+
assert_eq!(ascii_str.len(), 3);
945+
assert_eq!(ascii_str.as_slice(), expected_chars);
946+
947+
let ascii_str_unchecked = unsafe {
948+
cstring.into_ascii_string_unchecked()
949+
};
950+
assert_eq!(ascii_str_unchecked.len(), 3);
951+
assert_eq!(ascii_str_unchecked.as_slice(), expected_chars);
952+
953+
let sparkle_heart_bytes = vec![240u8, 159, 146, 150];
954+
let cstring = CString::new(sparkle_heart_bytes).unwrap();
955+
let cstr = &*cstring;
956+
let ascii_err = cstr.into_ascii_string().unwrap_err();
957+
assert_eq!(ascii_err.into_source(), &*cstring);
958+
}
959+
868960
#[test]
869961
fn fmt_ascii_string() {
870962
let s = "abc".to_string().into_ascii_string().unwrap();

0 commit comments

Comments
 (0)