1 /** Convert ranges to pipelines and pipelines to ranges. 2 * 3 * Authors: $(LINK2 https://github.com/epi, Adrian Matoga) 4 * Copyright: © 2016 Adrian Matoga 5 * License: $(LINK2 http://www.boost.org/users/license.html, BSL-1.0). 6 */ 7 module flod.range; 8 9 import std.range : isInputRange, isOutputRange; 10 11 import flod.pipeline : pipe, isPipeline; 12 import flod.traits; 13 14 package auto pipeFromArray(E)(const(E)[] array) 15 { 16 @peekSource!E 17 static struct ArraySource { 18 const(E)[] array; 19 this(const(E)* ptr, size_t length) 20 { 21 this.array = ptr[0 .. length]; 22 } 23 24 const(E)[] peek()(size_t n) { return array; } 25 void consume()(size_t n) { array = array[n .. $]; } 26 } 27 import std.stdio; 28 29 return .pipe!ArraySource(array.ptr, array.length); 30 } 31 32 unittest { 33 auto arr = [ 1, 2, 37, 98, 123, 12313 ]; 34 auto pl = arr.pipeFromArray.create(); 35 assert(pl.peek(1) == arr[]); 36 assert(pl.peek(123) == arr[]); 37 pl.consume(2); 38 assert(pl.peek(23) == arr[2 .. $]); 39 pl.consume(pl.peek(1).length); 40 assert(pl.peek(1).length == 0); 41 } 42 43 package auto pipeFromInputRange(R)(R r) 44 if (isInputRange!R) 45 { 46 import std.range : ElementType; 47 48 alias E = ElementType!R; 49 @pullSource!E 50 static struct RangeSource 51 { 52 R range; 53 54 this(bool dummy, R range) { cast(void) dummy; this.range = range; } 55 56 size_t pull()(E[] buf) 57 { 58 foreach (i, ref e; buf) { 59 if (range.empty) 60 return i; 61 e = range.front; 62 range.popFront(); 63 } 64 return buf.length; 65 } 66 } 67 68 return .pipe!RangeSource(false, r); 69 } 70 71 unittest { 72 import std.range : iota, hasSlicing, hasLength, isInfinite; 73 import flod.pipeline : isPullPipeline; 74 75 auto r = iota(6, 12); 76 static assert( hasSlicing!(typeof(r))); 77 static assert( hasLength!(typeof(r))); 78 static assert(!isInfinite!(typeof(r))); 79 auto p = r.pipeFromInputRange; 80 static assert(isPullPipeline!(typeof(p))); 81 static assert(is(p.ElementType == int)); 82 auto pl = p.create(); 83 int[4] buf; 84 assert(pl.pull(buf[]) == 4); 85 assert(buf[] == [6, 7, 8, 9]); 86 assert(pl.pull(buf[]) == 2); 87 assert(buf[0 .. 2] == [10, 11]); 88 } 89 90 unittest { 91 import std.range : repeat, hasSlicing, hasLength, isInfinite; 92 93 auto r = repeat(0xdead); 94 static assert( hasSlicing!(typeof(r))); 95 static assert(!hasLength!(typeof(r))); 96 static assert( isInfinite!(typeof(r))); 97 auto pl = r.pipeFromInputRange.create(); 98 int[5] buf; 99 assert(pl.pull(buf[]) == 5); 100 assert(buf[] == [0xdead, 0xdead, 0xdead, 0xdead, 0xdead]); 101 assert(pl.pull(new int[1234567]) == 1234567); 102 } 103 104 unittest { 105 import std.range : generate, take, hasSlicing; 106 107 auto r = generate({ int i = 0; return (){ return i++; }; }()).take(104); 108 static assert(!hasSlicing!(typeof(r))); 109 auto pl = r.pipeFromInputRange.create(); 110 int[5] buf; 111 assert(pl.pull(buf[]) == 5); 112 assert(buf[] == [0, 1, 2, 3, 4]); 113 assert(pl.pull(new int[1234567]) == 99); 114 } 115 116 public auto copy(Pipeline, R)(auto ref Pipeline pipeline, R outputRange) 117 if (isPipeline!Pipeline && isOutputRange!(R, Pipeline.ElementType)) 118 { 119 import std.range : put; 120 121 alias E = Pipeline.ElementType; 122 123 @pushSink!E 124 static struct Copy { 125 R range; 126 127 this()(R range) { this.range = range; } 128 129 size_t push()(const(E)[] buf) 130 { 131 put(range, buf); 132 return buf.length; 133 } 134 } 135 136 return pipeline.pipe!Copy(outputRange); 137 } 138 139 unittest { 140 import std.array : appender; 141 import std.range : iota; 142 143 auto app = appender!(int[]); 144 iota(89, 94).pipeFromInputRange.copy(app); 145 assert(app.data[] == [89, 90, 91, 92, 93]); 146 }