]> git.proxmox.com Git - rustc.git/blame - src/libcore/str/pattern.rs
Imported Upstream version 1.2.0+dfsg1
[rustc.git] / src / libcore / str / pattern.rs
CommitLineData
c34b1796
AL
1// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2// file at the top-level directory of this distribution and at
3// http://rust-lang.org/COPYRIGHT.
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
9346a6ac
AL
11//! The string Pattern API.
12//!
13//! For more details, see the traits `Pattern`, `Searcher`,
14//! `ReverseSearcher` and `DoubleEndedSearcher`.
15
62682a34
SL
16#![unstable(feature = "pattern",
17 reason = "API not fully fleshed out and ready to be stabilized")]
18
c34b1796
AL
19use prelude::*;
20
21// Pattern
22
23/// A string pattern.
24///
25/// A `Pattern<'a>` expresses that the implementing type
26/// can be used as a string pattern for searching in a `&'a str`.
27///
28/// For example, both `'a'` and `"aa"` are patterns that
29/// would match at index `1` in the string `"baaaab"`.
30///
31/// The trait itself acts as a builder for an associated
32/// `Searcher` type, which does the actual work of finding
33/// occurrences of the pattern in a string.
34pub trait Pattern<'a>: Sized {
35 /// Associated searcher for this pattern
36 type Searcher: Searcher<'a>;
37
9346a6ac 38 /// Constructs the associated searcher from
c34b1796
AL
39 /// `self` and the `haystack` to search in.
40 fn into_searcher(self, haystack: &'a str) -> Self::Searcher;
41
9346a6ac 42 /// Checks whether the pattern matches anywhere in the haystack
c34b1796
AL
43 #[inline]
44 fn is_contained_in(self, haystack: &'a str) -> bool {
45 self.into_searcher(haystack).next_match().is_some()
46 }
47
9346a6ac 48 /// Checks whether the pattern matches at the front of the haystack
c34b1796
AL
49 #[inline]
50 fn is_prefix_of(self, haystack: &'a str) -> bool {
51 match self.into_searcher(haystack).next() {
52 SearchStep::Match(0, _) => true,
53 _ => false,
54 }
55 }
56
9346a6ac 57 /// Checks whether the pattern matches at the back of the haystack
c34b1796
AL
58 #[inline]
59 fn is_suffix_of(self, haystack: &'a str) -> bool
60 where Self::Searcher: ReverseSearcher<'a>
61 {
62 match self.into_searcher(haystack).next_back() {
63 SearchStep::Match(_, j) if haystack.len() == j => true,
64 _ => false,
65 }
66 }
67}
68
69// Searcher
70
71/// Result of calling `Searcher::next()` or `ReverseSearcher::next_back()`.
72#[derive(Copy, Clone, Eq, PartialEq, Debug)]
73pub enum SearchStep {
74 /// Expresses that a match of the pattern has been found at
75 /// `haystack[a..b]`.
76 Match(usize, usize),
77 /// Expresses that `haystack[a..b]` has been rejected as a possible match
78 /// of the pattern.
79 ///
80 /// Note that there might be more than one `Reject` between two `Match`es,
81 /// there is no requirement for them to be combined into one.
82 Reject(usize, usize),
83 /// Expresses that every byte of the haystack has been visted, ending
84 /// the iteration.
85 Done
86}
87
88/// A searcher for a string pattern.
89///
90/// This trait provides methods for searching for non-overlapping
91/// matches of a pattern starting from the front (left) of a string.
92///
93/// It will be implemented by associated `Searcher`
94/// types of the `Pattern` trait.
95///
96/// The trait is marked unsafe because the indices returned by the
97/// `next()` methods are required to lie on valid utf8 boundaries in
98/// the haystack. This enables consumers of this trait to
99/// slice the haystack without additional runtime checks.
100pub unsafe trait Searcher<'a> {
101 /// Getter for the underlaying string to be searched in
102 ///
103 /// Will always return the same `&str`
104 fn haystack(&self) -> &'a str;
105
106 /// Performs the next search step starting from the front.
107 ///
108 /// - Returns `Match(a, b)` if `haystack[a..b]` matches the pattern.
109 /// - Returns `Reject(a, b)` if `haystack[a..b]` can not match the
110 /// pattern, even partially.
111 /// - Returns `Done` if every byte of the haystack has been visited
112 ///
113 /// The stream of `Match` and `Reject` values up to a `Done`
114 /// will contain index ranges that are adjacent, non-overlapping,
115 /// covering the whole haystack, and laying on utf8 boundaries.
116 ///
117 /// A `Match` result needs to contain the whole matched pattern,
118 /// however `Reject` results may be split up into arbitrary
119 /// many adjacent fragments. Both ranges may have zero length.
120 ///
121 /// As an example, the pattern `"aaa"` and the haystack `"cbaaaaab"`
122 /// might produce the stream
123 /// `[Reject(0, 1), Reject(1, 2), Match(2, 5), Reject(5, 8)]`
124 fn next(&mut self) -> SearchStep;
125
126 /// Find the next `Match` result. See `next()`
127 #[inline]
128 fn next_match(&mut self) -> Option<(usize, usize)> {
129 loop {
130 match self.next() {
131 SearchStep::Match(a, b) => return Some((a, b)),
132 SearchStep::Done => return None,
133 _ => continue,
134 }
135 }
136 }
137
138 /// Find the next `Reject` result. See `next()`
139 #[inline]
140 fn next_reject(&mut self) -> Option<(usize, usize)> {
141 loop {
142 match self.next() {
143 SearchStep::Reject(a, b) => return Some((a, b)),
144 SearchStep::Done => return None,
145 _ => continue,
146 }
147 }
148 }
149}
150
151/// A reverse searcher for a string pattern.
152///
153/// This trait provides methods for searching for non-overlapping
154/// matches of a pattern starting from the back (right) of a string.
155///
156/// It will be implemented by associated `Searcher`
157/// types of the `Pattern` trait if the pattern supports searching
158/// for it from the back.
159///
160/// The index ranges returned by this trait are not required
161/// to exactly match those of the forward search in reverse.
162///
163/// For the reason why this trait is marked unsafe, see them
164/// parent trait `Searcher`.
165pub unsafe trait ReverseSearcher<'a>: Searcher<'a> {
166 /// Performs the next search step starting from the back.
167 ///
168 /// - Returns `Match(a, b)` if `haystack[a..b]` matches the pattern.
169 /// - Returns `Reject(a, b)` if `haystack[a..b]` can not match the
170 /// pattern, even partially.
171 /// - Returns `Done` if every byte of the haystack has been visited
172 ///
173 /// The stream of `Match` and `Reject` values up to a `Done`
174 /// will contain index ranges that are adjacent, non-overlapping,
175 /// covering the whole haystack, and laying on utf8 boundaries.
176 ///
177 /// A `Match` result needs to contain the whole matched pattern,
178 /// however `Reject` results may be split up into arbitrary
179 /// many adjacent fragments. Both ranges may have zero length.
180 ///
181 /// As an example, the pattern `"aaa"` and the haystack `"cbaaaaab"`
182 /// might produce the stream
183 /// `[Reject(7, 8), Match(4, 7), Reject(1, 4), Reject(0, 1)]`
184 fn next_back(&mut self) -> SearchStep;
185
186 /// Find the next `Match` result. See `next_back()`
187 #[inline]
188 fn next_match_back(&mut self) -> Option<(usize, usize)>{
189 loop {
190 match self.next_back() {
191 SearchStep::Match(a, b) => return Some((a, b)),
192 SearchStep::Done => return None,
193 _ => continue,
194 }
195 }
196 }
197
198 /// Find the next `Reject` result. See `next_back()`
199 #[inline]
200 fn next_reject_back(&mut self) -> Option<(usize, usize)>{
201 loop {
202 match self.next_back() {
203 SearchStep::Reject(a, b) => return Some((a, b)),
204 SearchStep::Done => return None,
205 _ => continue,
206 }
207 }
208 }
209}
210
211/// A marker trait to express that a `ReverseSearcher`
212/// can be used for a `DoubleEndedIterator` implementation.
213///
214/// For this, the impl of `Searcher` and `ReverseSearcher` need
215/// to follow these conditions:
216///
217/// - All results of `next()` need to be identical
218/// to the results of `next_back()` in reverse order.
219/// - `next()` and `next_back()` need to behave as
220/// the two ends of a range of values, that is they
221/// can not "walk past each other".
222///
223/// # Examples
224///
225/// `char::Searcher` is a `DoubleEndedSearcher` because searching for a
226/// `char` only requires looking at one at a time, which behaves the same
227/// from both ends.
228///
229/// `(&str)::Searcher` is not a `DoubleEndedSearcher` because
230/// the pattern `"aa"` in the haystack `"aaa"` matches as either
231/// `"[aa]a"` or `"a[aa]"`, depending from which side it is searched.
232pub trait DoubleEndedSearcher<'a>: ReverseSearcher<'a> {}
233
9346a6ac 234/////////////////////////////////////////////////////////////////////////////
c34b1796 235// Impl for a CharEq wrapper
9346a6ac 236/////////////////////////////////////////////////////////////////////////////
c34b1796
AL
237
238#[doc(hidden)]
239trait CharEq {
240 fn matches(&mut self, char) -> bool;
241 fn only_ascii(&self) -> bool;
242}
243
244impl CharEq for char {
245 #[inline]
246 fn matches(&mut self, c: char) -> bool { *self == c }
247
248 #[inline]
249 fn only_ascii(&self) -> bool { (*self as u32) < 128 }
250}
251
252impl<F> CharEq for F where F: FnMut(char) -> bool {
253 #[inline]
254 fn matches(&mut self, c: char) -> bool { (*self)(c) }
255
256 #[inline]
257 fn only_ascii(&self) -> bool { false }
258}
259
260impl<'a> CharEq for &'a [char] {
261 #[inline]
262 fn matches(&mut self, c: char) -> bool {
263 self.iter().any(|&m| { let mut m = m; m.matches(c) })
264 }
265
266 #[inline]
267 fn only_ascii(&self) -> bool {
268 self.iter().all(|m| m.only_ascii())
269 }
270}
271
272struct CharEqPattern<C: CharEq>(C);
273
9346a6ac 274#[derive(Clone)]
c34b1796
AL
275struct CharEqSearcher<'a, C: CharEq> {
276 char_eq: C,
277 haystack: &'a str,
278 char_indices: super::CharIndices<'a>,
279 #[allow(dead_code)]
280 ascii_only: bool,
281}
282
283impl<'a, C: CharEq> Pattern<'a> for CharEqPattern<C> {
284 type Searcher = CharEqSearcher<'a, C>;
285
286 #[inline]
287 fn into_searcher(self, haystack: &'a str) -> CharEqSearcher<'a, C> {
288 CharEqSearcher {
289 ascii_only: self.0.only_ascii(),
290 haystack: haystack,
291 char_eq: self.0,
292 char_indices: haystack.char_indices(),
293 }
294 }
295}
296
297unsafe impl<'a, C: CharEq> Searcher<'a> for CharEqSearcher<'a, C> {
298 #[inline]
299 fn haystack(&self) -> &'a str {
300 self.haystack
301 }
302
303 #[inline]
304 fn next(&mut self) -> SearchStep {
305 let s = &mut self.char_indices;
306 // Compare lengths of the internal byte slice iterator
307 // to find length of current char
308 let (pre_len, _) = s.iter.iter.size_hint();
309 if let Some((i, c)) = s.next() {
310 let (len, _) = s.iter.iter.size_hint();
311 let char_len = pre_len - len;
312 if self.char_eq.matches(c) {
313 return SearchStep::Match(i, i + char_len);
314 } else {
315 return SearchStep::Reject(i, i + char_len);
316 }
317 }
318 SearchStep::Done
319 }
320}
321
322unsafe impl<'a, C: CharEq> ReverseSearcher<'a> for CharEqSearcher<'a, C> {
323 #[inline]
324 fn next_back(&mut self) -> SearchStep {
325 let s = &mut self.char_indices;
326 // Compare lengths of the internal byte slice iterator
327 // to find length of current char
328 let (pre_len, _) = s.iter.iter.size_hint();
329 if let Some((i, c)) = s.next_back() {
330 let (len, _) = s.iter.iter.size_hint();
331 let char_len = pre_len - len;
332 if self.char_eq.matches(c) {
333 return SearchStep::Match(i, i + char_len);
334 } else {
335 return SearchStep::Reject(i, i + char_len);
336 }
337 }
338 SearchStep::Done
339 }
340}
341
342impl<'a, C: CharEq> DoubleEndedSearcher<'a> for CharEqSearcher<'a, C> {}
343
9346a6ac 344/////////////////////////////////////////////////////////////////////////////
c34b1796 345// Impl for &str
9346a6ac 346/////////////////////////////////////////////////////////////////////////////
c34b1796
AL
347
348// Todo: Optimize the naive implementation here
349
9346a6ac 350/// Associated type for `<&str as Pattern<'a>>::Searcher`.
c34b1796 351#[derive(Clone)]
9346a6ac 352pub struct StrSearcher<'a, 'b> {
c34b1796
AL
353 haystack: &'a str,
354 needle: &'b str,
355 start: usize,
356 end: usize,
9346a6ac
AL
357 state: State,
358}
359
360#[derive(Clone, PartialEq)]
361enum State { Done, NotDone, Reject(usize, usize) }
362impl State {
363 #[inline] fn done(&self) -> bool { *self == State::Done }
364 #[inline] fn take(&mut self) -> State { ::mem::replace(self, State::NotDone) }
c34b1796
AL
365}
366
367/// Non-allocating substring search.
368///
369/// Will handle the pattern `""` as returning empty matches at each utf8
370/// boundary.
371impl<'a, 'b> Pattern<'a> for &'b str {
372 type Searcher = StrSearcher<'a, 'b>;
373
374 #[inline]
375 fn into_searcher(self, haystack: &'a str) -> StrSearcher<'a, 'b> {
376 StrSearcher {
377 haystack: haystack,
378 needle: self,
379 start: 0,
380 end: haystack.len(),
9346a6ac 381 state: State::NotDone,
c34b1796
AL
382 }
383 }
384}
385
386unsafe impl<'a, 'b> Searcher<'a> for StrSearcher<'a, 'b> {
387 #[inline]
388 fn haystack(&self) -> &'a str {
389 self.haystack
390 }
391
392 #[inline]
393 fn next(&mut self) -> SearchStep {
394 str_search_step(self,
395 |m: &mut StrSearcher| {
396 // Forward step for empty needle
397 let current_start = m.start;
9346a6ac 398 if !m.state.done() {
c34b1796 399 m.start = m.haystack.char_range_at(current_start).next;
9346a6ac 400 m.state = State::Reject(current_start, m.start);
c34b1796
AL
401 }
402 SearchStep::Match(current_start, current_start)
403 },
404 |m: &mut StrSearcher| {
405 // Forward step for nonempty needle
406 let current_start = m.start;
407 // Compare byte window because this might break utf8 boundaries
408 let possible_match = &m.haystack.as_bytes()[m.start .. m.start + m.needle.len()];
409 if possible_match == m.needle.as_bytes() {
410 m.start += m.needle.len();
411 SearchStep::Match(current_start, m.start)
412 } else {
413 // Skip a char
414 let haystack_suffix = &m.haystack[m.start..];
415 m.start += haystack_suffix.chars().next().unwrap().len_utf8();
416 SearchStep::Reject(current_start, m.start)
417 }
418 })
419 }
420}
421
422unsafe impl<'a, 'b> ReverseSearcher<'a> for StrSearcher<'a, 'b> {
423 #[inline]
424 fn next_back(&mut self) -> SearchStep {
425 str_search_step(self,
426 |m: &mut StrSearcher| {
427 // Backward step for empty needle
428 let current_end = m.end;
9346a6ac 429 if !m.state.done() {
c34b1796 430 m.end = m.haystack.char_range_at_reverse(current_end).next;
9346a6ac 431 m.state = State::Reject(m.end, current_end);
c34b1796
AL
432 }
433 SearchStep::Match(current_end, current_end)
434 },
435 |m: &mut StrSearcher| {
436 // Backward step for nonempty needle
437 let current_end = m.end;
438 // Compare byte window because this might break utf8 boundaries
439 let possible_match = &m.haystack.as_bytes()[m.end - m.needle.len() .. m.end];
440 if possible_match == m.needle.as_bytes() {
441 m.end -= m.needle.len();
442 SearchStep::Match(m.end, current_end)
443 } else {
444 // Skip a char
445 let haystack_prefix = &m.haystack[..m.end];
446 m.end -= haystack_prefix.chars().rev().next().unwrap().len_utf8();
447 SearchStep::Reject(m.end, current_end)
448 }
449 })
450 }
451}
452
453// Helper function for encapsulating the common control flow
454// of doing a search step from the front or doing a search step from the back
455fn str_search_step<F, G>(mut m: &mut StrSearcher,
456 empty_needle_step: F,
457 nonempty_needle_step: G) -> SearchStep
458 where F: FnOnce(&mut StrSearcher) -> SearchStep,
459 G: FnOnce(&mut StrSearcher) -> SearchStep
460{
9346a6ac 461 if m.state.done() {
c34b1796 462 SearchStep::Done
9346a6ac 463 } else if m.needle.is_empty() && m.start <= m.end {
c34b1796 464 // Case for needle == ""
9346a6ac
AL
465 if let State::Reject(a, b) = m.state.take() {
466 SearchStep::Reject(a, b)
467 } else {
468 if m.start == m.end {
469 m.state = State::Done;
470 }
471 empty_needle_step(&mut m)
c34b1796 472 }
c34b1796
AL
473 } else if m.start + m.needle.len() <= m.end {
474 // Case for needle != ""
475 nonempty_needle_step(&mut m)
476 } else if m.start < m.end {
477 // Remaining slice shorter than needle, reject it
9346a6ac 478 m.state = State::Done;
c34b1796
AL
479 SearchStep::Reject(m.start, m.end)
480 } else {
9346a6ac 481 m.state = State::Done;
c34b1796
AL
482 SearchStep::Done
483 }
484}
485
9346a6ac
AL
486/////////////////////////////////////////////////////////////////////////////
487
488macro_rules! pattern_methods {
489 ($t:ty, $pmap:expr, $smap:expr) => {
490 type Searcher = $t;
491
492 #[inline]
493 fn into_searcher(self, haystack: &'a str) -> $t {
494 ($smap)(($pmap)(self).into_searcher(haystack))
c34b1796 495 }
9346a6ac 496
c34b1796
AL
497 #[inline]
498 fn is_contained_in(self, haystack: &'a str) -> bool {
9346a6ac 499 ($pmap)(self).is_contained_in(haystack)
c34b1796 500 }
9346a6ac 501
c34b1796
AL
502 #[inline]
503 fn is_prefix_of(self, haystack: &'a str) -> bool {
9346a6ac 504 ($pmap)(self).is_prefix_of(haystack)
c34b1796 505 }
9346a6ac 506
c34b1796
AL
507 #[inline]
508 fn is_suffix_of(self, haystack: &'a str) -> bool
9346a6ac 509 where $t: ReverseSearcher<'a>
c34b1796 510 {
9346a6ac 511 ($pmap)(self).is_suffix_of(haystack)
c34b1796
AL
512 }
513 }
514}
515
9346a6ac
AL
516macro_rules! searcher_methods {
517 (forward) => {
518 #[inline]
519 fn haystack(&self) -> &'a str {
520 self.0.haystack()
521 }
522 #[inline]
523 fn next(&mut self) -> SearchStep {
524 self.0.next()
525 }
526 #[inline]
527 fn next_match(&mut self) -> Option<(usize, usize)> {
528 self.0.next_match()
529 }
530 #[inline]
531 fn next_reject(&mut self) -> Option<(usize, usize)> {
532 self.0.next_reject()
533 }
534 };
535 (reverse) => {
536 #[inline]
537 fn next_back(&mut self) -> SearchStep {
538 self.0.next_back()
539 }
540 #[inline]
541 fn next_match_back(&mut self) -> Option<(usize, usize)> {
542 self.0.next_match_back()
543 }
544 #[inline]
545 fn next_reject_back(&mut self) -> Option<(usize, usize)> {
546 self.0.next_reject_back()
547 }
548 }
c34b1796
AL
549}
550
9346a6ac
AL
551/////////////////////////////////////////////////////////////////////////////
552// Impl for char
553/////////////////////////////////////////////////////////////////////////////
554
555/// Associated type for `<char as Pattern<'a>>::Searcher`.
556#[derive(Clone)]
557pub struct CharSearcher<'a>(<CharEqPattern<char> as Pattern<'a>>::Searcher);
c34b1796
AL
558
559unsafe impl<'a> Searcher<'a> for CharSearcher<'a> {
9346a6ac 560 searcher_methods!(forward);
c34b1796 561}
9346a6ac 562
c34b1796 563unsafe impl<'a> ReverseSearcher<'a> for CharSearcher<'a> {
9346a6ac 564 searcher_methods!(reverse);
c34b1796 565}
c34b1796 566
9346a6ac 567impl<'a> DoubleEndedSearcher<'a> for CharSearcher<'a> {}
c34b1796 568
9346a6ac
AL
569/// Searches for chars that are equal to a given char
570impl<'a> Pattern<'a> for char {
571 pattern_methods!(CharSearcher<'a>, CharEqPattern, CharSearcher);
c34b1796
AL
572}
573
9346a6ac
AL
574/////////////////////////////////////////////////////////////////////////////
575// Impl for &[char]
576/////////////////////////////////////////////////////////////////////////////
577
578// Todo: Change / Remove due to ambiguity in meaning.
579
580/// Associated type for `<&[char] as Pattern<'a>>::Searcher`.
581#[derive(Clone)]
582pub struct CharSliceSearcher<'a, 'b>(<CharEqPattern<&'b [char]> as Pattern<'a>>::Searcher);
c34b1796
AL
583
584unsafe impl<'a, 'b> Searcher<'a> for CharSliceSearcher<'a, 'b> {
9346a6ac 585 searcher_methods!(forward);
c34b1796 586}
9346a6ac 587
c34b1796 588unsafe impl<'a, 'b> ReverseSearcher<'a> for CharSliceSearcher<'a, 'b> {
9346a6ac 589 searcher_methods!(reverse);
c34b1796 590}
c34b1796 591
9346a6ac 592impl<'a, 'b> DoubleEndedSearcher<'a> for CharSliceSearcher<'a, 'b> {}
c34b1796 593
9346a6ac
AL
594/// Searches for chars that are equal to any of the chars in the array
595impl<'a, 'b> Pattern<'a> for &'b [char] {
596 pattern_methods!(CharSliceSearcher<'a, 'b>, CharEqPattern, CharSliceSearcher);
c34b1796
AL
597}
598
9346a6ac
AL
599/////////////////////////////////////////////////////////////////////////////
600// Impl for F: FnMut(char) -> bool
601/////////////////////////////////////////////////////////////////////////////
602
603/// Associated type for `<F as Pattern<'a>>::Searcher`.
604#[derive(Clone)]
605pub struct CharPredicateSearcher<'a, F>(<CharEqPattern<F> as Pattern<'a>>::Searcher)
606 where F: FnMut(char) -> bool;
c34b1796 607
9346a6ac 608unsafe impl<'a, F> Searcher<'a> for CharPredicateSearcher<'a, F>
c34b1796
AL
609 where F: FnMut(char) -> bool
610{
9346a6ac 611 searcher_methods!(forward);
c34b1796 612}
9346a6ac
AL
613
614unsafe impl<'a, F> ReverseSearcher<'a> for CharPredicateSearcher<'a, F>
c34b1796
AL
615 where F: FnMut(char) -> bool
616{
9346a6ac 617 searcher_methods!(reverse);
c34b1796 618}
c34b1796 619
9346a6ac
AL
620impl<'a, F> DoubleEndedSearcher<'a> for CharPredicateSearcher<'a, F>
621 where F: FnMut(char) -> bool {}
c34b1796 622
9346a6ac
AL
623/// Searches for chars that match the given predicate
624impl<'a, F> Pattern<'a> for F where F: FnMut(char) -> bool {
625 pattern_methods!(CharPredicateSearcher<'a, F>, CharEqPattern, CharPredicateSearcher);
626}
627
628/////////////////////////////////////////////////////////////////////////////
629// Impl for &&str
630/////////////////////////////////////////////////////////////////////////////
631
632/// Delegates to the `&str` impl.
c34b1796 633impl<'a, 'b> Pattern<'a> for &'b &'b str {
9346a6ac 634 pattern_methods!(StrSearcher<'a, 'b>, |&s| s, |s| s);
c34b1796 635}