1 /++
2 Ranges.
3 
4 See_also: $(MREF mir,_primitives).
5 
6 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
7 Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
8 Authors: Ilya Yaroshenko, Phobos Authors
9 +/
10 module mir.range;
11 
12 /++
13 Data size counter.
14 
15 Does not store anything.
16 +/
17 struct Counter(T)
18 {
19     import std.range: isInputRange, ElementType;
20     import std.traits: isImplicitlyConvertible, isSomeChar;
21     ///
22     size_t _count;
23 
24     /// Data count.
25     size_t count()() @property
26     {
27         return _count;
28     }
29 
30     private template canPutItem(U)
31     {
32         enum bool canPutItem =
33             isImplicitlyConvertible!(U, T) ||
34             isSomeChar!T && isSomeChar!U;
35     }
36 
37     private template canPutRange(Range)
38     {
39         import mir.primitives: front;
40         enum bool canPutRange =
41             isInputRange!Range &&
42             is(typeof(Counter.init.put(Range.init.front)));
43     }
44 
45     ///
46     void put(U)(auto ref U item) if (canPutItem!U)
47     {
48         static if (isSomeChar!T && isSomeChar!U && T.sizeof < U.sizeof)
49         {
50             import std.utf: codeLength;
51             _count += codeLength!T(item);
52         }
53         else
54         {
55             _count++;
56         }
57     }
58 
59     ///
60     void put(Range)(Range items) if (canPutRange!Range)
61     {
62         // note, we disable this branch for appending one type of char to
63         // another because we can't trust the length portion.
64         static if (!(isSomeChar!T && isSomeChar!(ElementType!Range) &&
65                      !is(immutable Range == immutable T[])))
66         {
67             import mir.primitives: hasLength;
68             static if (hasLength!Range)
69             {
70                 _count += items.length;
71             }
72             else
73             {
74                 for (;!items.empty; items.popFront)
75                     _count++;
76             }
77         }
78         else
79         {
80             import std.utf: codeLength;
81             _count += codeLength!T(items);
82         }
83     }
84 }
85 
86 ///
87 version(mir_test) unittest
88 {
89     Counter!char counter;
90     counter.put("Ми");
91     assert(counter.count == 4);
92     counter.put('р'); // Cyrillic 
93     assert(counter.count == 6);
94 }
95 
96 ///
97 version(mir_test) unittest
98 {
99     Counter!wchar counter;
100     counter.put("Ми");
101     assert(counter.count == 2);
102     counter.put('р'); // Cyrillic
103     assert(counter.count == 3);
104 }
105 
106 ///
107 version(mir_test) unittest
108 {
109     Counter!int counter;
110     import std.algorithm: until;
111     counter.put([1, 2, 3, 4, 5].until(3));
112 }