zeno/
stroke.rs

1//! Stroking and dashing of paths.
2
3#![allow(clippy::needless_lifetimes)]
4
5use super::command::Command;
6use super::geometry::*;
7use super::path_builder::*;
8use super::segment::*;
9use super::style::*;
10#[allow(unused)]
11use super::F32Ext;
12
13use crate::lib::Vec;
14use core::borrow::Borrow;
15
16pub fn stroke_into<'a, I>(commands: I, style: &Stroke<'a>, sink: &mut impl PathBuilder)
17where
18    I: Iterator + Clone,
19    I::Item: Borrow<Command>,
20{
21    let mut stroker = Stroker::new(segments(commands, true), sink, style);
22    let (dashes, dash_offset, empty_gaps) = validate_dashes(style.dashes, style.offset);
23    let mut segment_buf = SmallBuf::new();
24    if !dashes.is_empty() {
25        stroker.dash(&mut segment_buf, dashes, dash_offset, empty_gaps);
26    } else {
27        stroker.stroke(&mut segment_buf);
28    }
29}
30
31pub fn stroke_with_storage<'a, I>(
32    commands: I,
33    style: &Stroke<'a>,
34    sink: &mut impl PathBuilder,
35    storage: &mut impl StrokerStorage,
36) where
37    I: Iterator + Clone,
38    I::Item: Borrow<Command>,
39{
40    let mut stroker = Stroker::new(segments(commands, true), sink, style);
41    let (dashes, dash_offset, empty_gaps) = validate_dashes(style.dashes, style.offset);
42    if !dashes.is_empty() {
43        stroker.dash(storage, dashes, dash_offset, empty_gaps);
44    } else {
45        stroker.stroke(storage);
46    }
47}
48
49pub struct Stroker<'a, I, S> {
50    source: Segments<I>,
51    sink: &'a mut S,
52    radius: f32,
53    radius_abs: f32,
54    join: Join,
55    inv_miter_limit: f32,
56    start_cap: Cap,
57    end_cap: Cap,
58}
59
60impl<'a, I, S> Stroker<'a, I, S>
61where
62    I: Iterator + Clone,
63    I::Item: Borrow<Command>,
64    S: PathBuilder,
65{
66    pub(super) fn new(source: Segments<I>, sink: &'a mut S, style: &Stroke) -> Self {
67        let radius = style.width.max(0.01) * 0.5;
68        Self {
69            source,
70            sink,
71            radius,
72            radius_abs: radius.abs(),
73            join: style.join,
74            inv_miter_limit: if style.miter_limit >= 1. {
75                1. / style.miter_limit
76            } else {
77                1.
78            },
79            start_cap: style.start_cap,
80            end_cap: style.end_cap,
81        }
82    }
83
84    fn stroke(&mut self, segment_buf: &mut impl StrokerStorage) {
85        loop {
86            let (closed, done) = segment_buf.collect(&mut self.source);
87            self.stroke_segments(segment_buf.get(), closed);
88            if done {
89                break;
90            }
91        }
92    }
93
94    fn stroke_segments(&mut self, segments: &[Segment], is_closed: bool) {
95        let len = segments.len();
96        if len == 0 {
97            return;
98        }
99        if len == 1
100            && segments[0].length() == 0.
101            && (self.start_cap != Cap::Butt || self.end_cap != Cap::Butt)
102        {
103            let segment = segments[0];
104            let from = match &segment {
105                Segment::Line(_, line) => line.a,
106                Segment::Curve(_, curve) => curve.a,
107                Segment::End(..) => Point::ZERO,
108            };
109            let n = Vector::new(0., 1.);
110            let nr = n * self.radius;
111            let start = from + nr;
112            let rstart = from - nr;
113            self.sink.move_to(start);
114            self.add_end_cap(start, rstart, n);
115            self.add_start_cap(rstart, start, n * -1.);
116            return;
117        }
118        let radius = self.radius;
119        let mut last_dir = Vector::ZERO;
120        let mut first_point = Point::ZERO;
121        let mut last_point = Point::ZERO;
122        let mut pivot = Point::ZERO;
123        let mut last_id = 0xFF;
124        if is_closed {
125            let segment = segments[len - 1].offset(radius);
126            let end_point = segment.end;
127            let out_dir = segment.end_normal;
128            pivot = segment.end_pivot;
129            last_dir = out_dir;
130            last_point = end_point;
131            first_point = end_point;
132            self.sink.move_to(last_point);
133        }
134        // Forward for the outer stroke.
135        let mut is_first = !is_closed;
136        for segment in segments {
137            let segment = segment.offset(radius);
138            let id = segment.id;
139            let start = segment.start;
140            if is_first {
141                self.sink.move_to(start);
142                first_point = start;
143                is_first = false;
144            } else {
145                self.add_join(last_point, start, pivot, last_dir, segment.start_normal);
146            }
147            last_id = id;
148            last_dir = segment.end_normal;
149            pivot = segment.end_pivot;
150            last_point = self.emit(&segment.segment);
151        }
152        // Now backward for the inner stroke.
153        is_first = true;
154        for segment in segments.iter().rev() {
155            let segment = segment.reverse().offset(radius);
156            let id = segment.id;
157            let start = segment.start;
158            if is_first {
159                if is_closed {
160                    let init = segments[0].reverse().offset(self.radius);
161                    last_point = init.end;
162                    last_dir = init.end_normal;
163                    pivot = init.end_pivot;
164                    self.sink.line_to(init.end);
165                    self.add_join(last_point, start, pivot, last_dir, segment.start_normal);
166                } else {
167                    self.add_end_cap(last_point, start, last_dir);
168                }
169                is_first = false;
170            } else if id != last_id {
171                self.add_join(last_point, start, pivot, last_dir, segment.start_normal);
172            } else {
173                self.add_split_join(last_point, start, pivot, last_dir, segment.start_normal);
174            };
175            last_id = id;
176            last_dir = segment.end_normal;
177            pivot = segment.end_pivot;
178            last_point = self.emit(&segment.segment);
179        }
180        if !is_closed {
181            self.add_start_cap(last_point, first_point, last_dir);
182        }
183        self.sink.close();
184    }
185
186    #[allow(clippy::field_reassign_with_default)]
187    fn dash(
188        &mut self,
189        segment_buf: &mut impl StrokerStorage,
190        dashes: &[f32],
191        offset: f32,
192        empty_gaps: bool,
193    ) {
194        let mut dasher = Dasher::default();
195        dasher.empty_gaps = empty_gaps;
196        let mut done = false;
197        while !done {
198            let (is_closed, is_done) = segment_buf.collect(&mut self.source);
199            done = is_done;
200            let segments = segment_buf.get();
201            if segments.is_empty() {
202                continue;
203            }
204            dasher.init(is_closed, dashes, offset);
205            loop {
206                match dasher.next(segments, dashes) {
207                    DashOp::Done => break,
208                    DashOp::Continue => {}
209                    DashOp::Emit => {
210                        let (start, end) = dasher.range;
211                        let (t0, t1) = dasher.trange;
212                        self.dash_segments(segments, start, end, t0, t1);
213                    }
214                    DashOp::Stroke => {
215                        self.stroke_segments(segments, true);
216                        break;
217                    }
218                }
219            }
220        }
221    }
222
223    fn dash_segments(&mut self, segments: &[Segment], start: isize, end: isize, t0: f32, t1: f32) {
224        let radius = self.radius;
225        if t0 == t1 && start == end {
226            if self.start_cap == Cap::Butt && self.end_cap == Cap::Butt {
227                return;
228            }
229            let (t0, t1) = if t0 >= 1. {
230                (t0 - 0.001, t0)
231            } else {
232                (t0, t0 + 0.001)
233            };
234            let segment = get_signed(segments, start).slice(t0, t1).offset(radius);
235            let start = segment.start;
236            let rstart = segment.start - (segment.start_normal * (2. * radius));
237            self.sink.move_to(start);
238            self.add_end_cap(start, rstart, segment.start_normal);
239            self.add_start_cap(rstart, start, segment.start_normal * -1.);
240            self.sink.close();
241            return;
242        }
243        let mut last_dir = Vector::ZERO;
244        let mut first_point = Point::ZERO;
245        let mut last_point = Point::ZERO;
246        let mut pivot = Point::ZERO;
247        let mut is_first = true;
248        let mut last_id = 0xFF;
249        for i in start..=end {
250            let t0 = if i == start { t0 } else { 0. };
251            let t1 = if i == end { t1 } else { 1. };
252            if t0 >= 1. {
253                continue;
254            }
255            let segment = get_signed(segments, i).slice(t0, t1).offset(radius);
256            let id = segment.id;
257            let start = segment.start;
258            if is_first {
259                self.sink.move_to(start);
260                first_point = start;
261                is_first = false;
262            } else if id != last_id {
263                self.add_join(last_point, start, pivot, last_dir, segment.start_normal);
264            } else {
265                self.add_split_join(last_point, start, pivot, last_dir, segment.start_normal);
266            };
267            last_id = id;
268            pivot = segment.end_pivot;
269            last_dir = segment.end_normal;
270            last_point = self.emit(&segment.segment);
271        }
272        is_first = true;
273        last_id = 0xFF;
274        for i in (start..=end).rev() {
275            let t0 = if i == start { t0 } else { 0. };
276            let t1 = if i == end { t1 } else { 1. };
277            if t0 >= 1. {
278                continue;
279            }
280            let segment = get_signed(segments, i)
281                .slice(t0, t1)
282                .reverse()
283                .offset(radius);
284            let id = segment.id;
285            let start = segment.start;
286            if is_first {
287                self.add_end_cap(last_point, start, last_dir);
288                is_first = false;
289            } else if id != last_id {
290                self.add_join(last_point, start, pivot, last_dir, segment.start_normal);
291            } else {
292                self.add_split_join(last_point, start, pivot, last_dir, segment.start_normal);
293            };
294            last_id = id;
295            pivot = segment.end_pivot;
296            last_dir = segment.end_normal;
297            last_point = self.emit(&segment.segment);
298        }
299        self.add_start_cap(last_point, first_point, last_dir);
300        self.sink.close();
301    }
302
303    #[inline(always)]
304    fn emit(&mut self, segment: &Segment) -> Point {
305        match segment {
306            Segment::Line(_, line) => {
307                self.sink.line_to(line.b);
308                line.b
309            }
310            Segment::Curve(_, curve) => {
311                self.sink.curve_to(curve.b, curve.c, curve.d);
312                curve.d
313            }
314            _ => Point::ZERO,
315        }
316    }
317
318    fn add_join(
319        &mut self,
320        from: Point,
321        to: Point,
322        pivot: Point,
323        from_normal: Vector,
324        to_normal: Vector,
325    ) -> Point {
326        if from.nearly_eq(to) {
327            return from;
328        }
329        if !is_clockwise(from_normal, to_normal) {
330            self.sink.line_to(pivot);
331            self.sink.line_to(to);
332            return to;
333        }
334        match self.join {
335            Join::Bevel => {
336                self.sink.line_to(to);
337                to
338            }
339            Join::Round => {
340                let r = self.radius_abs;
341                let (size, sweep) = (ArcSize::Small, ArcSweep::Positive);
342                arc(self.sink, from, r, r, 0., size, sweep, to);
343                to
344            }
345            Join::Miter => {
346                let inv_limit = self.inv_miter_limit;
347                let dot = from_normal.dot(to_normal);
348                let sin_half = ((1. + dot) * 0.5).sqrt();
349                if dot < 0.0 || sin_half < inv_limit {
350                    self.sink.line_to(to);
351                    to
352                } else {
353                    let mid = (from_normal + to_normal).normalize() * (self.radius / sin_half);
354                    let p = pivot + mid;
355                    self.sink.line_to(p);
356                    self.sink.line_to(to);
357                    to
358                }
359            }
360        }
361    }
362
363    fn add_split_join(
364        &mut self,
365        from: Point,
366        to: Point,
367        pivot: Point,
368        from_normal: Vector,
369        to_normal: Vector,
370    ) -> Point {
371        if from.nearly_eq(to) {
372            return from;
373        }
374        if !is_clockwise(from_normal, to_normal) {
375            self.sink.line_to(pivot);
376            self.sink.line_to(to);
377            return to;
378        }
379        let r = self.radius_abs;
380        let (size, sweep) = (ArcSize::Small, ArcSweep::Positive);
381        arc(self.sink, from, r, r, 0., size, sweep, to);
382        to
383    }
384
385    fn add_cap(&mut self, from: Point, to: Point, dir: Vector, cap: Cap) {
386        match cap {
387            Cap::Butt => {
388                self.sink.line_to(to);
389            }
390            Cap::Square => {
391                let dir = Vector::new(-dir.y, dir.x);
392                self.sink.line_to(from + dir * self.radius_abs);
393                self.sink.line_to(to + dir * self.radius_abs);
394                self.sink.line_to(to);
395            }
396            Cap::Round => {
397                let r = self.radius_abs;
398                let (size, sweep) = (ArcSize::Small, ArcSweep::Positive);
399                arc(self.sink, from, r, r, 0., size, sweep, to);
400            }
401        }
402    }
403
404    fn add_start_cap(&mut self, from: Point, to: Point, dir: Vector) {
405        self.add_cap(from, to, dir, self.start_cap);
406    }
407
408    fn add_end_cap(&mut self, from: Point, to: Point, dir: Vector) {
409        self.add_cap(from, to, dir, self.end_cap);
410    }
411}
412
413enum DashOp {
414    Done,
415    Continue,
416    Emit,
417    Stroke,
418}
419
420#[derive(Copy, Clone, Default)]
421struct Dasher {
422    done: bool,
423    is_closed: bool,
424    empty_gaps: bool,
425    on: bool,
426    cur: isize,
427    t0: f32,
428    t0_offset: f32,
429    index: usize,
430    is_first: bool,
431    first_on: bool,
432    first_dash: f32,
433    is_dot: bool,
434    range: (isize, isize),
435    trange: (f32, f32),
436}
437
438impl Dasher {
439    fn init(&mut self, is_closed: bool, dashes: &[f32], offset: f32) {
440        self.done = false;
441        self.is_closed = is_closed;
442        self.on = true;
443        self.cur = 0;
444        self.t0 = 0.;
445        self.t0_offset = 0.;
446        self.index = 0;
447        self.is_first = true;
448        self.first_on = true;
449        let mut first_dash = self.next_dash(dashes);
450        if offset > 0. {
451            let mut accum = first_dash;
452            while accum < offset {
453                self.on = !self.on;
454                accum += self.next_dash(dashes);
455            }
456            self.first_on = self.on;
457            first_dash = accum - offset;
458        }
459        self.first_dash = first_dash;
460    }
461
462    #[inline(always)]
463    fn next_dash(&mut self, dashes: &[f32]) -> f32 {
464        let len = dashes.len();
465        let mut dash = dashes[self.index % len];
466        if self.on && self.empty_gaps {
467            loop {
468                let next_dash = dashes[(self.index + 1) % len];
469                if next_dash != 0. {
470                    break;
471                }
472                self.index += 2;
473                dash += dashes[self.index % len];
474            }
475        }
476        self.index += 1;
477        dash
478    }
479
480    #[inline(always)]
481    fn next_segments(
482        dash: f32,
483        segments: &[Segment],
484        limit: isize,
485        start: isize,
486        start_offset: f32,
487    ) -> (bool, isize, f32, f32) {
488        let mut cur = start;
489        let mut goal = dash + start_offset;
490        let mut segment = get_signed(segments, cur);
491        loop {
492            let td = segment.time(goal, 1.);
493            let dist = td.distance;
494            let t2 = td.time;
495            goal -= dist;
496            if goal <= 0. {
497                return (true, cur, dist, t2);
498            }
499            if cur + 1 >= limit {
500                return (false, cur, dist, t2);
501            }
502            cur += 1;
503            segment = get_signed(segments, cur);
504        }
505    }
506
507    #[inline(always)]
508    fn next(&mut self, segments: &[Segment], dashes: &[f32]) -> DashOp {
509        if self.done {
510            return DashOp::Done;
511        }
512        let first = self.is_first;
513        let first_and_closed = first && self.is_closed;
514        let mut dash = if first {
515            self.first_dash
516        } else {
517            self.next_dash(dashes)
518        };
519        let mut on = self.on;
520        let mut start = self.cur;
521        let limit = segments.len() as isize;
522        if self.t0 == 1. && start < limit - 1 {
523            start += 1;
524            self.t0 = 0.;
525            self.t0_offset = 0.;
526            self.cur = start;
527        }
528        let (cont, mut end, mut t1_offset, mut t1) = if dash == 0. {
529            (true, start, self.t0_offset, self.t0)
530        } else {
531            Self::next_segments(dash, segments, limit, start, self.t0_offset)
532        };
533        if !cont {
534            self.done = true;
535        }
536        // This is tricky. If the subpath is closed and the last dash is
537        // "on" we need to join the last dash to the first. Otherwise, we
538        // need to go back and produce the initial dash that was skipped
539        // in anticipation of joining to the final dash.
540        if self.done && self.is_closed {
541            if on {
542                // Recompute the final dash including the first.
543                if first_and_closed {
544                    // The first dash consumed the whole path: emit a single stroke.
545                    return DashOp::Stroke;
546                }
547                if self.first_on {
548                    self.cur = start - limit;
549                    start = self.cur;
550                    let (_, end2, end_offset, end_t) =
551                        Self::next_segments(self.first_dash, segments, limit, 0, 0.);
552                    end = end2;
553                    t1_offset = end_offset;
554                    t1 = end_t;
555                }
556            } else {
557                // Emit the first dash.
558                if !self.first_on {
559                    return DashOp::Done;
560                }
561                dash = self.first_dash;
562                self.cur = 0;
563                self.t0 = 0.;
564                self.t0_offset = 0.;
565                self.on = true;
566                on = true;
567                start = self.cur;
568                let (_, end2, end_offset, end_t) =
569                    Self::next_segments(self.first_dash, segments, limit, 0, 0.);
570                end = end2;
571                t1_offset = end_offset;
572                t1 = end_t;
573            }
574        } else if self.done && !on {
575            return DashOp::Done;
576        }
577        self.is_dot = dash == 0.;
578        let t0 = self.t0;
579
580        self.is_first = false;
581        self.cur = end;
582        self.t0 = t1;
583        self.t0_offset = t1_offset;
584        self.on = !self.on;
585        if on && !first_and_closed {
586            self.range = (start, end);
587            self.trange = (t0, t1);
588            return DashOp::Emit;
589        }
590        DashOp::Continue
591    }
592}
593
594fn validate_dashes(dashes: &[f32], offset: f32) -> (&[f32], f32, bool) {
595    let len = dashes.len();
596    if len > 0 {
597        // Generate a full stroke under any of the following conditions:
598        // 1. The array contains any negative values.
599        // 2. All dashes are less than 1 unit.
600        // 3. All gap dashes are less than 1 unit.
601        let mut small_count = 0;
602        let mut gap_sum = 0.;
603        let mut empty_gaps = false;
604        let is_odd = len & 1 != 0;
605        for (i, dash) in dashes.iter().enumerate() {
606            let is_gap = i & 1 == 1;
607            if *dash < 1. {
608                small_count += 1;
609                if *dash < 0. {
610                    return (&[], 0., false);
611                } else if *dash == 0. && (is_gap || is_odd) {
612                    empty_gaps = true;
613                }
614            } else if is_gap {
615                gap_sum += *dash;
616            }
617        }
618        if dashes.len() == 1 {
619            gap_sum = 1.;
620        }
621        if small_count < dashes.len() && gap_sum > 0. {
622            let offset = if offset != 0. {
623                let mut s: f32 = dashes.iter().sum();
624                if is_odd {
625                    s *= 2.;
626                }
627                if offset < 0. {
628                    s - (offset.abs() % s)
629                } else {
630                    offset % s
631                }
632            } else {
633                0.
634            };
635            return (dashes, offset, empty_gaps);
636        }
637    }
638    (&[], 0., false)
639}
640
641#[inline(always)]
642fn get_signed(segments: &[Segment], index: isize) -> Segment {
643    let index = if index < 0 {
644        segments.len() - (-index) as usize
645    } else {
646        index as usize
647    };
648    segments[index]
649}
650
651fn is_clockwise(a: Vector, b: Vector) -> bool {
652    a.x * b.y > a.y * b.x
653}
654
655impl Segment {
656    fn offset(&self, radius: f32) -> OffsetSegment {
657        OffsetSegment::new(self, radius)
658    }
659}
660
661#[derive(Copy, Clone)]
662pub struct OffsetSegment {
663    pub segment: Segment,
664    pub id: u8,
665    pub start: Point,
666    pub end: Point,
667    pub start_normal: Vector,
668    pub end_normal: Vector,
669    pub end_pivot: Point,
670}
671
672impl OffsetSegment {
673    fn new(segment: &Segment, radius: f32) -> Self {
674        match segment {
675            Segment::Line(id, Line { a, b }) => {
676                let n = normal(*a, *b);
677                let nr = n * radius;
678                let start = *a + nr;
679                let end = *b + nr;
680                Self {
681                    segment: Segment::Line(*id, Line { a: start, b: end }),
682                    id: *id,
683                    start,
684                    end,
685                    start_normal: n,
686                    end_normal: n,
687                    end_pivot: *b,
688                }
689            }
690            Segment::Curve(id, c) => {
691                const EPS: f32 = 0.5;
692                //const EPS: f32 = CURVE_EPSILON;
693                let normal_ab = if c.a.nearly_eq_by(c.b, EPS) {
694                    if c.a.nearly_eq_by(c.c, EPS) {
695                        normal(c.a, c.d)
696                    } else {
697                        normal(c.a, c.c)
698                    }
699                } else {
700                    normal(c.a, c.b)
701                };
702                let normal_bc = if c.b.nearly_eq_by(c.c, EPS) {
703                    if c.b.nearly_eq_by(c.d, EPS) {
704                        normal(c.a, c.d)
705                    } else {
706                        normal(c.b, c.d)
707                    }
708                } else {
709                    normal(c.b, c.c)
710                };
711                let normal_cd = if c.c.nearly_eq_by(c.d, EPS) {
712                    if c.b.nearly_eq_by(c.d, EPS) {
713                        normal(c.a, c.d)
714                    } else {
715                        normal(c.b, c.d)
716                    }
717                } else {
718                    normal(c.c, c.d)
719                };
720                let mut normal_b = normal_ab + normal_bc;
721                let mut normal_c = normal_cd + normal_bc;
722                let dot = normal_ab.dot(normal_bc);
723                normal_b = normal_b.normalize() * (radius / ((1. + dot) * 0.5).sqrt());
724                let dot = normal_cd.dot(normal_bc);
725                normal_c = normal_c.normalize() * (radius / ((1. + dot) * 0.5).sqrt());
726                let start = c.a + normal_ab * radius;
727                let end = c.d + normal_cd * radius;
728                Self {
729                    segment: Segment::Curve(
730                        *id,
731                        Curve::new(start, c.b + normal_b, c.c + normal_c, end),
732                    ),
733                    id: *id,
734                    start,
735                    end,
736                    start_normal: normal_ab,
737                    end_normal: normal_cd,
738                    end_pivot: c.d,
739                }
740            }
741            Segment::End(..) => Self {
742                segment: *segment,
743                id: 0,
744                start: Point::ZERO,
745                end: Point::ZERO,
746                start_normal: Vector::ZERO,
747                end_normal: Vector::ZERO,
748                end_pivot: Point::ZERO,
749            },
750        }
751    }
752}
753
754pub trait StrokerStorage {
755    fn clear(&mut self);
756    fn push(&mut self, segment: &Segment);
757    fn get(&self) -> &[Segment];
758
759    fn collect(&mut self, segments: &mut impl Iterator<Item = Segment>) -> (bool, bool) {
760        self.clear();
761        let mut is_closed = false;
762        let mut done = false;
763        loop {
764            if let Some(segment) = segments.next() {
765                match segment {
766                    Segment::End(closed) => {
767                        is_closed = closed;
768                        break;
769                    }
770                    _ => self.push(&segment),
771                }
772            } else {
773                done = true;
774                break;
775            }
776        }
777        (is_closed, done)
778    }
779}
780
781impl StrokerStorage for SmallBuf<Segment> {
782    fn clear(&mut self) {
783        self.clear();
784    }
785
786    #[inline(always)]
787    fn push(&mut self, segment: &Segment) {
788        self.push(*segment);
789    }
790
791    fn get(&self) -> &[Segment] {
792        self.data()
793    }
794}
795
796impl StrokerStorage for Vec<Segment> {
797    fn clear(&mut self) {
798        self.clear();
799    }
800
801    #[inline(always)]
802    fn push(&mut self, segment: &Segment) {
803        self.push(*segment);
804    }
805
806    fn get(&self) -> &[Segment] {
807        self
808    }
809}
810
811const MAX_SMALL_BUF: usize = 128;
812
813#[derive(Clone)]
814enum SmallBuf<T> {
815    Array([T; MAX_SMALL_BUF], usize),
816    Vec(Vec<T>),
817}
818
819impl<T: Copy + Default> SmallBuf<T> {
820    pub fn new() -> Self {
821        Self::Array([T::default(); MAX_SMALL_BUF], 0)
822    }
823
824    pub fn data(&self) -> &[T] {
825        match self {
826            Self::Array(ref buf, len) => &buf[..*len],
827            Self::Vec(ref buf) => buf,
828        }
829    }
830
831    pub fn push(&mut self, value: T) {
832        match self {
833            Self::Vec(ref mut buf) => buf.push(value),
834            Self::Array(ref mut buf, ref mut len) => {
835                if *len == MAX_SMALL_BUF {
836                    let mut vec = Vec::from(&buf[..]);
837                    vec.push(value);
838                    *self = Self::Vec(vec);
839                } else {
840                    buf[*len] = value;
841                    *len += 1;
842                }
843            }
844        }
845    }
846
847    pub fn clear(&mut self) {
848        match self {
849            Self::Array(_, ref mut len) => *len = 0,
850            Self::Vec(ref mut buf) => buf.clear(),
851        }
852    }
853}