1 module dkh.scanner;
2 
3 import dkh.container.stackpayload;
4 
5 /**
6 Scanner 速くはないが遅くもない printf/scanfよりちょっと遅いくらい?
7 */
8 class Scanner {
9     import std.stdio : File;
10     import std.conv : to;
11     import std.range : front, popFront, array, ElementType;
12     import std.array : split;
13     import std.traits : isSomeChar, isStaticArray, isArray; 
14     import std.algorithm : map;
15     File f;
16     this(File f) {
17         this.f = f;
18     }
19     char[512] lineBuf;
20     char[] line;
21     private bool succW() {
22         import std.range.primitives : empty, front, popFront;
23         import std.ascii : isWhite;
24         while (!line.empty && line.front.isWhite) {
25             line.popFront;
26         }
27         return !line.empty;
28     }
29     private bool succ() {
30         import std.range.primitives : empty, front, popFront;
31         import std.ascii : isWhite;
32         while (true) {
33             while (!line.empty && line.front.isWhite) {
34                 line.popFront;
35             }
36             if (!line.empty) break;
37             line = lineBuf[];
38             f.readln(line);
39             if (!line.length) return false;
40         }
41         return true;
42     }
43 
44     private bool readSingle(T)(ref T x) {
45         import std.algorithm : findSplitBefore;
46         import std.string : strip;
47         import std.conv : parse;
48         if (!succ()) return false;
49         static if (isArray!T) {
50             alias E = ElementType!T;
51             static if (isSomeChar!E) {
52                 //string or char[10] etc
53                 //todo optimize
54                 auto r = line.findSplitBefore(" ");
55                 x = r[0].strip.dup;
56                 line = r[1];
57             } else static if (isStaticArray!T) {
58                 foreach (i; 0..T.length) {
59                     bool f = succW();
60                     assert(f);
61                     x[i] = line.parse!E;
62                 }
63             } else {
64                 StackPayload!E buf;
65                 while (succW()) {
66                     buf ~= line.parse!E;
67                 }
68                 x = buf.data;
69             }
70         } else {
71             x = line.parse!T;
72         }
73         return true;
74     }
75     int read(T, Args...)(ref T x, auto ref Args args) {
76         if (!readSingle(x)) return 0;
77         static if (args.length == 0) {
78             return 1;
79         } else {
80             return 1 + read(args);
81         }
82     }
83 }
84 
85 
86 ///
87 unittest {
88     import std.path : buildPath;
89     import std.file : tempDir;
90     import std.algorithm : equal;
91     import std.stdio : File;
92     string fileName = buildPath(tempDir, "kyuridenanmaida.txt");
93     auto fout = File(fileName, "w");
94     fout.writeln("1 2 3");
95     fout.writeln("ab cde");
96     fout.writeln("1.0 1.0 2.0");
97     fout.close;
98     Scanner sc = new Scanner(File(fileName, "r"));
99     int a;
100     int[2] b;
101     char[2] c;
102     string d;
103     double e;
104     double[] f;
105     sc.read(a, b, c, d, e, f);
106     assert(a == 1);
107     assert(equal(b[], [2, 3])); // 配列型は行末まで読み込む
108     assert(equal(c[], "ab")); // char型配列はトークンをそのまま返す
109     assert(equal(d, "cde")); // stringもchar型配列と同様
110     assert(e == 1.0); // 小数も可
111     assert(equal(f, [1.0, 2.0]));
112     assert(sc.read(a) == 0); // EOF
113 }
114 
115 unittest {
116     import std.path : buildPath;
117     import std.file : tempDir;
118     import std.algorithm : equal;
119     import std.stdio : File, writeln;
120     import dkh.stopwatch;
121     string fileName = buildPath(tempDir, "kyuridenanmaida.txt");
122     auto fout = File(fileName, "w");
123     foreach (i; 0..1_000_000) {
124         fout.writeln(3*i, " ", 3*i+1, " ", 3*i+2);
125     }
126     fout.close;
127     StopWatch sw;
128     sw.start;
129     Scanner sc = new Scanner(File(fileName, "r"));
130     foreach (i; 0..500_000) {
131         int a, b, c;
132         sc.read(a, b, c);
133         assert(a == 3*i);
134         assert(b == 3*i+1);
135         assert(c == 3*i+2);
136     }
137     foreach (i; 500_000..700_000) {
138         int[3] d;
139         sc.read(d);
140         int a = d[0], b = d[1], c = d[2];
141         assert(a == 3*i);
142         assert(b == 3*i+1);
143         assert(c == 3*i+2);
144     }
145     foreach (i; 700_000..1_000_000) {
146         int[] d;
147         sc.read(d);
148         assert(d.length == 3);
149         int a = d[0], b = d[1], c = d[2];
150         assert(a == 3*i);
151         assert(b == 3*i+1);
152         assert(c == 3*i+2);
153     }
154     writeln("Scanner Speed Test(3*1,000,000 int): ", sw.peek.toMsecs);
155 }