1 module dkh.scanner;
2 
3 import dkh.container.stackpayload;
4 
5 /**
6 Scanner, not slow, not fast.
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 
76     int unsafeRead(T, Args...)(ref T x, auto ref Args args) {
77         if (!readSingle(x)) return 0;
78         static if (args.length == 0) {
79             return 1;
80         } else {
81             return 1 + read(args);
82         }
83     }
84     void read(Args...)(auto ref Args args) {
85         import std.exception;
86         static if (args.length != 0) {
87             enforce(readSingle(args[0]));
88             read(args[1..$]);
89         }
90     }
91     bool hasNext() {
92         return succ();
93     }
94 }
95 
96 
97 ///
98 unittest {
99     import std.path : buildPath;
100     import std.file : tempDir;
101     import std.algorithm : equal;
102     import std.stdio : File;
103     string fileName = buildPath(tempDir, "kyuridenanmaida.txt");
104     auto fout = File(fileName, "w");
105     fout.writeln("1 2 3");
106     fout.writeln("ab cde");
107     fout.writeln("1.0 1.0 2.0");
108     fout.close;
109     Scanner sc = new Scanner(File(fileName, "r"));
110     int a;
111     int[2] b;
112     char[2] c;
113     string d;
114     double e;
115     double[] f;
116     sc.read(a, b, c, d, e, f);
117     assert(a == 1);
118     assert(equal(b[], [2, 3])); // 配列型は行末まで読み込む
119     assert(equal(c[], "ab")); // char型配列はトークンをそのまま返す
120     assert(equal(d, "cde")); // stringもchar型配列と同様
121     assert(e == 1.0); // 小数も可
122     assert(equal(f, [1.0, 2.0]));
123     assert(!sc.hasNext);
124 }
125 
126 unittest {
127     import std.path : buildPath;
128     import std.file : tempDir;
129     import std.algorithm : equal;
130     import std.stdio : File, writeln;
131     import dkh.stopwatch;
132     string fileName = buildPath(tempDir, "kyuridenanmaida.txt");
133     auto fout = File(fileName, "w");
134     foreach (i; 0..1_000_000) {
135         fout.writeln(3*i, " ", 3*i+1, " ", 3*i+2);
136     }
137     fout.close;
138     StopWatch sw;
139     sw.start;
140     Scanner sc = new Scanner(File(fileName, "r"));
141     foreach (i; 0..500_000) {
142         int a, b, c;
143         sc.read(a, b, c);
144         assert(a == 3*i);
145         assert(b == 3*i+1);
146         assert(c == 3*i+2);
147     }
148     foreach (i; 500_000..700_000) {
149         int[3] d;
150         sc.read(d);
151         int a = d[0], b = d[1], c = d[2];
152         assert(a == 3*i);
153         assert(b == 3*i+1);
154         assert(c == 3*i+2);
155     }
156     foreach (i; 700_000..1_000_000) {
157         int[] d;
158         sc.read(d);
159         assert(d.length == 3);
160         int a = d[0], b = d[1], c = d[2];
161         assert(a == 3*i);
162         assert(b == 3*i+1);
163         assert(c == 3*i+2);
164     }
165     writeln("Scanner Speed Test(3*1,000,000 int): ", sw.peek.toMsecs);
166 }