1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 20 /** 21 * Transports which operate on generic D ranges. 22 */ 23 module thrift.transport.range; 24 25 import std.array : empty; 26 import std.range; 27 import std.traits : Unqual; 28 import thrift.transport.base; 29 30 /** 31 * Adapts an ubyte input range for reading via the TTransport interface. 32 * 33 * The case where R is a plain ubyte[] is reasonably optimized, so a possible 34 * use case for TInputRangeTransport would be to deserialize some data held in 35 * a memory buffer. 36 */ 37 final class TInputRangeTransport(R) if ( 38 isInputRange!(Unqual!R) && is(ElementType!R : const(ubyte)) 39 ) : TBaseTransport { 40 /** 41 * Constructs a new instance. 42 * 43 * Params: 44 * data = The input range to use as data. 45 */ 46 this(R data) { 47 data_ = data; 48 } 49 50 /** 51 * An input range transport is always open. 52 */ 53 override bool isOpen() @property { 54 return true; 55 } 56 57 override bool peek() { 58 return !data_.empty; 59 } 60 61 /** 62 * Opening is a no-op() for an input range transport. 63 */ 64 override void open() {} 65 66 /** 67 * Closing is a no-op() for a memory buffer. 68 */ 69 override void close() {} 70 71 override size_t read(ubyte[] buf) { 72 auto data = data_.take(buf.length); 73 auto bytes = data.length; 74 75 static if (is(typeof(R.init[1 .. 2]) : const(ubyte)[])) { 76 // put() is currently unnecessarily slow if both ranges are sliceable. 77 buf[0 .. bytes] = data[]; 78 data_ = data_[bytes .. $]; 79 } else { 80 buf.put(data); 81 } 82 83 return bytes; 84 } 85 86 /** 87 * Shortcut version of readAll() for slicable ranges. 88 * 89 * Because readAll() is typically a very hot path during deserialization, 90 * using this over TBaseTransport.readAll() gives us a nice increase in 91 * speed due to the reduced amount of indirections. 92 */ 93 override void readAll(ubyte[] buf) { 94 static if (is(typeof(R.init[1 .. 2]) : const(ubyte)[])) { 95 if (buf.length <= data_.length) { 96 buf[] = data_[0 .. buf.length]; 97 data_ = data_[buf.length .. $]; 98 return; 99 } 100 } 101 super.readAll(buf); 102 } 103 104 override const(ubyte)[] borrow(ubyte* buf, size_t len) { 105 static if (is(R : const(ubyte)[])) { 106 // Can only borrow if our data type is actually an ubyte array. 107 if (len <= data_.length) { 108 return data_; 109 } 110 } 111 return null; 112 } 113 114 override void consume(size_t len) { 115 static if (is(R : const(ubyte)[])) { 116 if (len > data_.length) { 117 throw new TTransportException("Invalid consume length", 118 TTransportException.Type.BAD_ARGS); 119 } 120 data_ = data_[len .. $]; 121 } else { 122 super.consume(len); 123 } 124 } 125 126 /** 127 * Sets a new data range to use. 128 */ 129 void reset(R data) { 130 data_ = data; 131 } 132 133 private: 134 R data_; 135 } 136 137 /** 138 * TInputRangeTransport construction helper to avoid having to explicitly 139 * specify the argument type, i.e. to allow the constructor being called using 140 * IFTI (see $(LINK2 http://d.puremagic.com/issues/show_bug.cgi?id=6082, D 141 * Bugzilla enhancement requet 6082)). 142 */ 143 TInputRangeTransport!R tInputRangeTransport(R)(R data) if ( 144 is (TInputRangeTransport!R) 145 ) { 146 return new TInputRangeTransport!R(data); 147 }