1 //Written in the D programming language 2 /* 3 * Some rountines for use with ranges 4 * 5 * Copyright (C) 2013-6 Jaypha 6 * 7 * Distributed under the Boost Software License, Version 1.0. 8 * (See http://www.boost.org/LICENSE_1_0.txt) 9 * 10 * Authors: Jason den Dulk 11 */ 12 13 module jaypha.range; 14 15 import std.algorithm; 16 import std.range.primitives; 17 import std.traits; 18 import std.exception; 19 20 enum defaultBufferSize = 8192; 21 22 public import jaypha.algorithm : munch; 23 24 //---------------------------------------------------------------------------- 25 // Consume the rest of the range. 26 27 void drain(R)(ref R r) if (isInputRange!R) 28 { 29 while (!r.empty) r.popFront(); 30 } 31 32 //---------------------------------------------------------------------------- 33 // Splits a range into chunks of given size. Doesn't work with narrow 34 // strings (It might split a character in the middle). 35 36 struct ByChunk(R) if (isInputRange!R && !isNarrowString!R) 37 { 38 alias Unqual!(ElementType!R) E; 39 40 this(R range, size_t chunkSize = defaultBufferSize) 41 { 42 front.length = chunkSize; 43 rng = range; 44 num = chunkSize; 45 popFront(); 46 } 47 48 E[] front; 49 50 bool empty = false; 51 52 void popFront() 53 { 54 if (empty) return; 55 if (rng.empty) 56 { 57 empty = true; 58 } 59 else 60 { 61 front = new E[num]; 62 foreach (i; 0..num) 63 { 64 if (rng.empty) { front.length = i; break; } 65 front[i] = rng.front; 66 rng.popFront(); 67 } 68 } 69 } 70 71 private: 72 R rng; 73 size_t num; 74 } 75 76 ByChunk!R byChunk(R)(R range, size_t chunkSize) 77 { 78 return ByChunk!R(range,chunkSize); 79 } 80 81 //---------------------------------------------------------------------------- 82 83 unittest 84 { 85 import std.array; 86 import std.range.interfaces; 87 88 ubyte[] txt = cast(ubyte[]) "acabacbxyz".dup; 89 auto buff = appender!(ubyte[]); 90 91 auto r1 = inputRangeObject(txt); 92 assert(!r1.empty); 93 r1.drain(); 94 assert(r1.empty); 95 96 buff.clear(); 97 98 dstring ds = "acaba"; 99 100 auto bc = byChunk(ds, 3); 101 assert(bc.front == "aca"d); 102 bc.popFront(); 103 assert(bc.front == "ba"d); 104 bc.popFront(); 105 assert(bc.empty); 106 } 107 108 //---------------------------------------------------------------------------- 109 110 struct ByLines(R) 111 if (isInputRange!R && (isSomeChar!(ElementEncodingType!R) || is(ElementType!R : ubyte))) 112 { 113 import std.array; 114 115 alias ElementEncodingType!R E; 116 117 private: 118 R r; 119 E[] line; 120 121 public: 122 bool empty = false; 123 string eoln = "x"; 124 125 this(R _r) { r = _r; popFront(); } 126 127 void popFront() 128 { 129 assert(!empty); 130 if (eoln == "") 131 empty =true; 132 else 133 { 134 auto napp = appender!(E[])(); 135 136 E[] ln; 137 while (!r.empty && r.front != '\n' && r.front != '\r') 138 { 139 napp.put(r.front); 140 r.popFront(); 141 } 142 if (!r.empty) 143 { 144 auto c = r.front; r.popFront(); 145 if (c == '\r') 146 { 147 if (r.front == '\n') 148 { 149 r.popFront(); 150 eoln = "\r\n"; 151 } 152 else 153 eoln = "\r"; 154 } 155 else 156 eoln = "\n"; 157 } 158 else 159 eoln = ""; 160 161 line = napp.data; 162 } 163 } 164 165 @property E[] front() 166 { 167 return line; 168 } 169 } 170 171 ByLines!R byLines(R)(R range) 172 { 173 return ByLines!R(range); 174 } 175 176 //---------------------------------------------------------------------------- 177 178 unittest 179 { 180 import std.conv; 181 182 // Detect if D behaviour changes. 183 184 assert("" !is null); 185 assert([] is null); 186 187 188 char[] a = "abc".dup; 189 assert(a[0..0] !is null); 190 assert(a[0..0].length == 0); 191 a.length = 0; 192 assert(a !is null); 193 194 // Ripped off from Phobos. 195 void test(string txt, string[] witness) 196 { 197 import std.range.interfaces; 198 199 auto r1 = inputRangeObject(txt); 200 201 static assert(isInputRange!(typeof(r1))); 202 static assert(is(typeof(r1) : InputRange!(dchar))); 203 204 auto lines = new ByLines!(typeof(r1))(r1); 205 uint i; 206 207 while (!lines.empty) 208 { 209 assert(i<witness.length, text(i, ">=", witness.length)); 210 assert(to!string(lines.front) == witness[i++]); 211 lines.popFront(); 212 } 213 assert(i == witness.length, text(i, " != ", witness.length)); 214 } 215 216 test("", [ "" ]); 217 test("\n", [ "", "" ]); 218 test("asd\ndef\nasdf", [ "asd", "def", "asdf" ]); 219 test("asd\ndef\nasdf\n", [ "asd", "def", "asdf", "" ]); 220 test("asd\n\nasdf\n", [ "asd", "", "asdf", "" ]); 221 test("asd\r\ndef\r\nasdf\r\n", [ "asd", "def", "asdf", "" ]); 222 test("asd\r\n\r\nasdf\r\n", [ "asd", "", "asdf", "" ]); 223 } 224 225 //---------------------------------------------------------------------------- 226 // Buffered output. Sort of like a ByChunk for output ranges. 227 228 struct Buffered(W,E, size_t bufferSize = defaultBufferSize) if (isOutputRange!(W,E[]) && bufferSize < 16777216) 229 { 230 this(W w) 231 { 232 writer = w; 233 length = 0; 234 } 235 236 void put(E e) 237 { 238 if (length == bufferSize) 239 { 240 writer.put(buffer[0..$]); 241 length = 0; 242 } 243 buffer[length] = e; 244 ++length; 245 } 246 247 void put(E[] e) 248 { 249 if (length + e.length > bufferSize) 250 { 251 if (length) 252 { 253 writer.put(buffer[0..length]); 254 length = 0; 255 } 256 if (e.length > bufferSize) 257 { 258 writer.put(e); 259 return; 260 } 261 } 262 buffer[length..(length+e.length)] = e; 263 length += e.length; 264 } 265 266 void flush() 267 { 268 writer.put(buffer[0..length]); 269 length = 0; 270 } 271 272 private: 273 W writer; 274 E[bufferSize] buffer; 275 size_t length; 276 } 277 278 auto ref buffered(E, size_t bufferSize = defaultBufferSize, W)(W writer) 279 { 280 return Buffered!(W,E,bufferSize)(writer); 281 } 282 283 unittest 284 { 285 import std.array; 286 287 auto a = appender!(int[])(); 288 289 auto b = buffered!(int,10)(a); 290 291 b.put(12); 292 assert(a.data == []); 293 b.flush(); 294 assert(a.data == [12]); 295 b.put([5,13,6,9,1,0]); 296 assert(a.data == [12]); 297 b.put(3); b.put([55,13]); b.put(20); 298 assert(a.data == [12]); 299 b.put(11); 300 assert(a.data == [12,5,13,6,9,1,0,3,55,13,20]); 301 b.put([34,1,24,42,11,0,67]); 302 assert(a.data == [12,5,13,6,9,1,0,3,55,13,20]); 303 b.put([100,9,43,40]); 304 assert(a.data == [12,5,13,6,9,1,0,3,55,13,20,11,34,1,24,42,11,0,67]); 305 b.put([9,8,7,6,5,4,3,2,1,0,-1]); 306 assert(a.data == [12,5,13,6,9,1,0,3,55,13,20,11,34,1,24,42,11,0,67,100,9,43,40,9,8,7,6,5,4,3,2,1,0,-1]); 307 b.put([19,28,37,46,55,64,73,82,91,102,13]); 308 assert(a.data == [12,5,13,6,9,1,0,3,55,13,20,11,34,1,24,42,11,0,67,100,9,43,40,9,8,7,6,5,4,3,2,1,0,-1,19,28,37,46,55,64,73,82,91,102,13]); 309 } 310 311 //---------------------------------------------------------------------------- 312 // Provides "ungetc" for input ranges. 313 314 import jaypha.container.stack; 315 316 struct UnPopable(R) if (isInputRange!R) 317 { 318 alias ElementType!R E; 319 320 R range; 321 322 @property bool empty() 323 { 324 return range.empty && store.empty; 325 } 326 327 @property E front() 328 { 329 if (!store.empty) 330 return store.front(); 331 else 332 return range.front(); 333 } 334 335 void popFront() 336 { 337 if (!store.empty) 338 store.popFront(); 339 else range.popFront(); 340 } 341 342 void unPopFront(E e) 343 { 344 store.put(e); 345 } 346 347 private: 348 Stack!E store; 349 } 350 351 auto ref unPopable(R)(R range) 352 { 353 return UnPopable!R(range); 354 } 355 356 unittest 357 { 358 string s = "abôd"; 359 360 auto r = unPopable(s); 361 362 assert(!r.empty); 363 assert(r.front == 'a'); 364 r.popFront(); 365 assert(!r.empty); 366 assert(r.front == 'b'); 367 r.unPopFront('x'); 368 assert(!r.empty); 369 assert(r.front == 'x'); 370 r.unPopFront('y'); 371 assert(!r.empty); 372 assert(r.front == 'y'); 373 r.popFront(); 374 assert(!r.empty); 375 assert(r.front == 'x'); 376 r.popFront(); 377 assert(!r.empty); 378 assert(r.front == 'b'); 379 r.popFront(); 380 assert(!r.empty); 381 assert(r.front == 'ô'); 382 r.unPopFront('z'); 383 assert(!r.empty); 384 assert(r.front == 'z'); 385 r.popFront(); 386 assert(!r.empty); 387 assert(r.front == 'ô'); 388 r.popFront(); 389 assert(!r.empty); 390 assert(r.front == 'd'); 391 r.popFront(); 392 assert(r.empty); 393 r.unPopFront('本'); 394 assert(!r.empty); 395 assert(r.front == '本'); 396 r.popFront(); 397 assert(r.empty); 398 }