cobia/
cape_string_const_win32.rs

1use crate::{C,CapeStringImpl};
2#[cfg(doc)] use crate::CapeOpenMap;
3use crate::cape_data_traits::*;
4use std::fmt;
5use crate::cape_result_value::*;
6
7/// Class to store a platform dependent string encoding for use in case
8/// insensitive hash maps or for use of case insensitive comparisons.
9///
10/// For COBIA, strings go over the pipeline
11///  as null terminated. For Windows, COBIA requires UTF-16 encoding.
12///
13/// Because this translation is always required, there is no 
14/// read-only string implementation that refers to a string slice.
15/// However, this string implementation is immutable and can therefore
16/// be used as static.
17///
18/// This implementation uses a `Vec<u16>` to store the string data.
19///
20/// A common use case in CAPE-OPEN is to to have a string constant
21/// for comparison with CapeString-like objects. This class is a  
22/// specialization for this purpose: the string is stored in 
23/// lower case, and PartialEq does case insensitive comparison.
24/// 
25/// Another common use case in CAPE-OPEN is to make hash maps of
26/// strings for case-insentive lookups. A specialized class is
27/// available for this purpose: [`CapeStringHashKey`].
28/// 
29/// PartialEq can be directly used to for a CapeStringConstNoCase on
30/// the left, and any object implementing CapeStringConstProvider on
31/// the right (but not vice versa)
32///
33/// # Examples
34///
35/// ```
36/// use cobia::*;
37/// use cobia::prelude::*;
38/// let s=cobia::CapeStringConstNoCase::from_string("idealGasEnthalpy");
39/// let s2=cobia::CapeStringImpl::from_string("IDEALGASENTHALPY");
40/// assert_eq!(s,s2);
41///
42/// fn test_eq(s:CapeStringConstNoCase, s3:&CapeStringIn) {
43///		assert_eq!(&s,s3);
44/// }
45///
46///	test_eq(s,&CapeStringInFromProvider::from(&s2).as_cape_string_in()); 
47/// ```
48
49#[derive(Debug)]
50pub struct CapeStringConstNoCase {
51	data: Vec<C::CapeCharacter>,
52}
53
54impl CapeStringConstNoCase {
55
56	///Construct from string
57	///
58	/// # Arguments
59	///
60	/// * `s` - A string slice to be converted to a CapeStringConstNoCase
61	///
62	/// # Examples
63	///
64	/// ```
65	/// use cobia;
66	/// let s=cobia::CapeStringConstNoCase::from_string("idealGasEnthalpy");
67	/// ```
68	pub fn from_string<T:AsRef<str>>(s: T) -> Self {
69		let s=s.as_ref();
70		let mut data=Vec::new();
71		data.reserve(s.len() + 1);
72		for c in s.encode_utf16() {
73			data.push(CapeStringImpl::to_lower_case(c));
74		}
75		data.push(0);
76		CapeStringConstNoCase {
77			data,
78		}
79	}
80
81	///Construct from CapeCharacter pointer
82	///
83	/// # Arguments
84	///
85	/// * `ptr` - A const CapeCharacter pointer
86	/// * `size` - Length of the string pointed to
87	pub fn from_cape_char_const(ptr:*const C::CapeCharacter, size:C::CapeSize) -> Self {
88		let mut data=Vec::new();
89		let size=size as usize;
90		data.reserve(size+1);
91		for i in 0..size {
92			let c=unsafe { *ptr.add(i as usize) };
93			data.push(CapeStringImpl::to_lower_case(c));
94		}
95		data.push(0);
96		CapeStringConstNoCase {
97			data,
98		}
99	}
100
101	/// Create a new CapeStringConstNoCase from a string.
102	///
103	/// # Arguments
104	///
105	/// * `s` - A string
106	pub fn from(s:Option<&str>) -> Self {
107		match s {
108			Some(s) => {
109				Self::from_string(s)
110			}
111			None => {
112				CapeStringConstNoCase {
113					data: vec![0u16]
114				}
115			}
116		}
117	}
118
119	///Return as string
120	///
121	/// # Examples
122	///
123	/// ```
124	/// use cobia;
125	/// let s=cobia::CapeStringConstNoCase::from_string("idealGasEnthalpy");
126	/// assert_eq!(s.as_string(),"idealgasenthalpy"); //note that CapeStringConstNoCase stores strings in lower case
127	/// ```
128	pub fn as_string(&self) -> String {
129		let len = self.data.len() - 1;
130		String::from_utf16_lossy(&self.data[..len])
131	}
132
133}
134
135impl fmt::Display for CapeStringConstNoCase {
136	/// Formats the CapeStringConstNoCase error using the given formatter.
137	///
138	/// # Examples
139	///
140	/// ```
141	/// use cobia;
142	/// let s=cobia::CapeStringConstNoCase::from_string("idealGasEnthalpy");
143	/// assert_eq!(format!("{}",s),"idealgasenthalpy"); //note that CapeStringConstNoCase stores strings in lower case
144	/// ```
145	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
146		if self.data.is_empty() {
147			write!(f, "")
148		} else {
149			let len = self.data.len() - 1;
150			write!(f, "{}", String::from_utf16_lossy(&self.data[..len]))
151		}
152	}
153}
154
155impl CapeStringConstProvider for CapeStringConstNoCase {
156	///Return as CapeCharacter const pointer
157	///
158	/// The caller must ensure that the lifetime of the CapeStringImpl
159	/// is longer than the pointer returned.
160	///
161	/// # Examples
162	///
163	/// ```
164	/// use cobia;
165	/// use cobia::prelude::*;
166	/// let s=cobia::CapeStringConstNoCase::from_string("idealGasEnthalpy"); //must remain in scope....
167	/// let ptr=s.as_capechar_const(); ///... while ptr is used
168	/// assert_eq!(unsafe{*ptr},'i' as u16);
169	/// ```
170	fn as_capechar_const(&self) -> *const C::CapeCharacter {
171		self.data.as_ptr()
172	}
173	///Return as CapeCharacter const pointer with length
174	///
175	/// The caller must ensure that the lifetime of the CapeStringImpl
176	/// is longer than the pointer returned.
177	///
178	/// # Examples
179	///
180	/// ```
181	/// use cobia;
182	/// use cobia::prelude::*;
183	/// let s=cobia::CapeStringConstNoCase::from_string("idealGasEnthalpy"); //must remain in scope....
184	/// let (ptr,len)=s.as_capechar_const_with_length(); ///... while ptr is used
185	/// assert_eq!(len,16);
186	/// ```
187	fn as_capechar_const_with_length(&self) -> (*const C::CapeCharacter, C::CapeSize) {
188		(self.data.as_ptr() as *const C::CapeCharacter, (self.data.len() - 1) as C::CapeSize) //length without the terminating null
189	}
190}
191
192impl<T:AsRef<str>> From<T> for CapeStringConstNoCase {
193	fn from(s: T) -> Self {
194		CapeStringConstNoCase::from_string(s)
195	}
196}
197
198impl Clone for CapeStringConstNoCase {
199	fn clone(&self) -> Self {
200		CapeStringConstNoCase {
201			data: self.data.clone(),
202		}
203	}
204}
205
206impl<T: CapeStringConstProvider> PartialEq<T> for CapeStringConstNoCase {
207	fn eq(&self, other: &T) -> bool {
208		let (ptr,len)=other.as_capechar_const_with_length();
209		let len=len as usize;
210		if self.data.len()-1 == len {
211			let mut ptr=ptr;
212			for i in 0..len {
213				if CapeStringImpl::to_lower_case(unsafe { *ptr }) != self.data[i] {
214					return false;
215				}
216				ptr=unsafe { ptr.add(1) };
217			}
218			return true;			
219		}
220		false
221	}
222}
223
224impl Eq for CapeStringConstNoCase {}
225
226
227impl CapeStringConstNoCase {
228
229	extern "C" fn string_get(
230		me: *mut ::std::os::raw::c_void,
231		data: *mut *const C::CapeCharacter,
232		size: *mut C::CapeSize,
233	) {
234		let p = me as *mut CapeStringConstNoCase;
235		let s: &mut CapeStringConstNoCase = unsafe { &mut *p };
236		unsafe {
237			*data = s.data.as_ptr() as *const C::CapeCharacter;
238			*size = s.data.len() as C::CapeSize - 1; //exclude null terminator
239		}
240	}
241
242	extern "C" fn string_set(
243		me: *mut ::std::os::raw::c_void,
244		data: *const C::CapeCharacter,
245		size: C::CapeSize,
246	) -> C::CapeResult {
247		let p = me as *mut CapeStringConstNoCase;
248		let s: &mut CapeStringConstNoCase = unsafe { &mut *p };
249		s.data.clear();
250		let slice=unsafe{ std::slice::from_raw_parts(data, size as usize)};
251		s.data.reserve(slice.len() + 1);
252		s.data.extend_from_slice(&slice);
253		s.data.push(0); //null terminator
254		COBIAERR_NOERROR
255	}
256
257	const CAPE_STRING_VTABLE: C::ICapeString_VTable = C::ICapeString_VTable {
258		get: Some(CapeStringConstNoCase::string_get),
259		set: Some(CapeStringConstNoCase::string_set),
260	};
261
262}
263
264impl CapeStringProviderIn for CapeStringConstNoCase {
265	fn as_cape_string_in(&self) -> C::ICapeString {
266		C::ICapeString {
267			vTbl:(&CapeStringConstNoCase::CAPE_STRING_VTABLE as *const C::ICapeString_VTable).cast_mut(),
268			me:(self as *const CapeStringConstNoCase).cast_mut() as *mut ::std::os::raw::c_void
269		}
270	}
271}
272
273/// Class to store a platform dependent string encoding for use in case
274/// insensitive hash maps or for use of case insensitive comparisons.
275///
276/// For COBIA, strings go over the pipeline
277///  as null terminated. For Windows, COBIA requires UTF-16 encoding.
278///
279/// The CapeStringHashKey implementation uses a `CapeStringConstNoCase` 
280/// to store, owned string data, or allows reference to data provided 
281/// by any class that implements CapeStringConstProvider, so that 
282/// a copy of the data is not needed for hash lookups.
283///
284/// A convenience class [`CapeOpenMap`] is defined, that uses the 
285/// more performant hasher in the FxHasmap class and wraps members
286/// in accordance with the above requirements.
287///
288/// Note that this type cannot serve as a cape string provider, as
289/// this would require a mutable interface pointer.
290
291#[derive(Debug)]
292pub enum CapeStringHashKey<'a> {
293	Owned(CapeStringConstNoCase),
294	Borrowed(*const C::CapeCharacter, C::CapeSize,std::marker::PhantomData<&'a ()>),
295}
296
297
298//Borrewed keys have a local, limited time span 
299//and are not shared between threads; Owned keys 
300//are constant and safe to share
301unsafe impl<'a> Send for CapeStringHashKey<'a> {}
302unsafe impl<'a> Sync for CapeStringHashKey<'a> {} 
303
304impl<'a> CapeStringHashKey<'a> {
305
306	///Construct from string that owns the data
307	///
308	/// # Arguments
309	///
310	/// * `s` - A string slice to be converted to a CapeStringHashKey
311	///
312	pub fn from_string<T:AsRef<str>>(s: T) -> Self {
313		CapeStringHashKey::Owned(CapeStringConstNoCase::from_string(s))
314	}
315
316	///Construct from string that owns the data
317	///
318	/// # Arguments
319	///
320	/// * `ptr` - A const CapeCharacter pointer
321	/// * `size` - Length of the string pointed to
322	pub fn from_cape_char_const(ptr:*const C::CapeCharacter, size:C::CapeSize) -> Self {
323		CapeStringHashKey::Owned(CapeStringConstNoCase::from_cape_char_const(ptr,size))
324	}
325
326	///Construct from string constant provider reference that does not own the data
327	///
328	/// # Arguments
329	///
330	/// * `c` - A string constant reference 
331	///
332	pub fn from_string_constant<'b,T:CapeStringConstProvider>(c: &'b T) -> CapeStringHashKey<'b> {
333		let (ptr,len)=c.as_capechar_const_with_length();
334		CapeStringHashKey::Borrowed(ptr,len,std::default::Default::default())
335	}
336	///Return as string
337	///
338	/// # Examples
339	///
340	/// ```
341	/// use cobia;
342	/// let s=cobia::CapeStringHashKey::from_string("idealGasEnthalpy");
343	/// assert_eq!(s.as_string(),"idealgasenthalpy"); //note that CapeStringConstNoCase stores strings in lower case
344	/// ```
345	pub fn as_string(&self) -> String {
346		let (ptr,len)=match self {
347			CapeStringHashKey::Owned(str_no_case) => {
348				str_no_case.as_capechar_const_with_length()
349			}
350			CapeStringHashKey::Borrowed(ptr,len,_) => {
351				(*ptr,*len)
352			}
353		};
354		if len==0 {
355			String::new()
356		} else {
357			let data=unsafe { std::slice::from_raw_parts(ptr,len as usize) };
358			String::from_utf16_lossy(&data)
359		}
360	}
361
362}
363
364impl<'a> std::fmt::Display for CapeStringHashKey<'a> {
365	/// Formats the CapeStringConstNoCase error using the given formatter.
366	///
367	/// # Examples
368	///
369	/// ```
370	/// use cobia;
371	/// let s=cobia::CapeStringConstNoCase::from_string("idealGasEnthalpy");
372	/// assert_eq!(format!("{}",s),"idealgasenthalpy"); //note that CapeStringConstNoCase stores strings in lower case
373	/// ```
374	fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
375		let (ptr,len)=match self {
376			CapeStringHashKey::Owned(str_no_case) => {
377				str_no_case.as_capechar_const_with_length()
378			}
379			CapeStringHashKey::Borrowed(ptr,len,_) => {
380				(*ptr,*len)
381			}
382		};
383		if len == (0 as C::CapeSize) {
384			write!(f, "")
385		} else {
386			let slice = unsafe { std::slice::from_raw_parts(ptr, len as usize) };
387			write!(f, "{}", String::from_utf16_lossy(slice))
388		}
389	}
390}
391
392impl<'a> PartialEq for CapeStringHashKey<'a> {
393	fn eq(&self, other: &Self) -> bool {
394		//optimize for owned vs borrowed, owned strings are already in lower case
395		match self {
396			CapeStringHashKey::Owned(data) => {
397				let (my_ptr,my_len)=data.as_capechar_const_with_length();
398				match other {
399					CapeStringHashKey::Owned(other_data) => {
400						let (other_ptr,other_len)=other_data.as_capechar_const_with_length();
401						if other_len==my_len {
402							let mut my_ptr=my_ptr;
403							let mut other_ptr=other_ptr;
404							let my_ptr_end=unsafe { my_ptr.add(my_len as usize) };
405							while my_ptr!=my_ptr_end {
406								if unsafe { *other_ptr } != unsafe { *my_ptr } {
407									return false;
408								}
409								my_ptr=unsafe { my_ptr.add(1) };
410								other_ptr=unsafe { other_ptr.add(1) };
411							}
412							return true;
413						} 
414						false
415					}
416					CapeStringHashKey::Borrowed(other_ptr,other_len,_) => {
417						if *other_len==my_len {
418							let mut my_ptr=my_ptr;
419							let mut other_ptr=*other_ptr;
420							let my_ptr_end=unsafe { my_ptr.add(my_len as usize) };
421							while my_ptr!=my_ptr_end {
422								if CapeStringImpl::to_lower_case(unsafe { *other_ptr }) != unsafe { *my_ptr } {
423									return false;
424								}
425								my_ptr=unsafe { my_ptr.add(1) };
426								other_ptr=unsafe { other_ptr.add(1) };
427							}
428							return true;
429						}
430						false
431					}
432				}
433			},
434			CapeStringHashKey::Borrowed(my_ptr,my_len,_) => {
435				let my_len=*my_len;
436				match other {
437					CapeStringHashKey::Owned(other_data) => {
438						let (other_ptr,other_len)=other_data.as_capechar_const_with_length();
439						if other_len==my_len {
440							let mut my_ptr=*my_ptr;
441							let mut other_ptr=other_ptr;
442							let my_ptr_end=unsafe { my_ptr.add(my_len as usize) };
443							while my_ptr!=my_ptr_end {
444								if unsafe { *other_ptr } != CapeStringImpl::to_lower_case(unsafe { *my_ptr }) {
445									return false;
446								}
447								my_ptr=unsafe { my_ptr.add(1) };
448								other_ptr=unsafe { other_ptr.add(1) };
449							}
450							return true;
451						} 
452						false
453					}
454					CapeStringHashKey::Borrowed(other_ptr,other_len,_) => {
455						if *other_len==my_len {
456							let mut my_ptr=*my_ptr;
457							let mut other_ptr=*other_ptr;
458							let my_ptr_end=unsafe { my_ptr.add(my_len as usize) };
459							while my_ptr!=my_ptr_end {
460								if CapeStringImpl::to_lower_case(unsafe { *other_ptr }) != CapeStringImpl::to_lower_case(unsafe { *my_ptr }) {
461									return false;
462								}
463								my_ptr=unsafe { my_ptr.add(1) };
464								other_ptr=unsafe { other_ptr.add(1) };
465							}
466							return true;
467						}
468						false
469					}
470				}
471			}
472		}
473	}
474}
475
476impl<'a> Eq for CapeStringHashKey<'a> {}
477
478impl<'a> std::hash::Hash for CapeStringHashKey<'a> {
479	fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
480		match self {
481			CapeStringHashKey::Owned(data) => {
482				let (my_ptr,my_len)=data.as_capechar_const_with_length();
483				let mut my_ptr=my_ptr;
484				let my_ptr_end=unsafe { my_ptr.add(my_len as usize) };
485				while my_ptr!=my_ptr_end {
486					unsafe { (*my_ptr).hash(state) }; //already lower case
487					my_ptr=unsafe { my_ptr.add(1) };
488				}
489			}
490			CapeStringHashKey::Borrowed(my_ptr,my_len,_) => {
491				let mut my_ptr=*my_ptr;
492				let my_ptr_end=unsafe { my_ptr.add(*my_len as usize) };
493				while my_ptr!=my_ptr_end {
494					CapeStringImpl::to_lower_case(unsafe { *my_ptr} ).hash(state);
495					my_ptr=unsafe { my_ptr.add(1) };
496				}
497			}
498		}
499	}
500}
501