1 module shark.util; 2 3 import std.array : Appender; 4 import std.conv : to; 5 import std.socket : Socket, SocketException, lastSocketError; 6 import std.system : Endian, endian; 7 8 import xbuffer : Buffer; 9 10 // debug 11 import std.stdio; 12 13 class Stream(size_t idLength, Endian endianness, size_t length, bool lengthIncludesItself, Endian sequenceEndianness=Endian.littleEndian, S=Object) { 14 15 private enum usesSequence = !is(S == Object); 16 17 protected Socket _socket; 18 19 private void[] _recv; 20 private Buffer _buffer; 21 private union { 22 version(LittleEndian) struct { 23 ubyte[length] _lengthData; 24 ubyte[size_t.sizeof-length] filler; 25 } 26 version(BigEndian) struct { 27 ubyte[size_t.sizeof-length] filler; 28 ubyte[length] _lengthData; 29 } 30 size_t _length; 31 } 32 33 private Buffer _returnBuffer; 34 35 static if(idLength) private void[idLength] _id; 36 37 static if(usesSequence) private S sequence; 38 39 public this(Socket socket, size_t buffer) { 40 _socket = socket; 41 _recv = new void[buffer]; 42 _buffer = new Buffer(buffer); 43 _returnBuffer = new Buffer(buffer); 44 } 45 46 public @property Socket socket() { 47 return _socket; 48 } 49 50 static if(idLength) public @property T[idLength] id(T=void)() { 51 return cast(T[idLength])_id; 52 } 53 54 static if(idLength) public @property T[idLength] id(T)(T[idLength] id) { 55 _id = cast(void[])id; 56 return id; 57 } 58 59 static if(usesSequence) public void resetSequence() { 60 sequence = 0; 61 } 62 63 public Buffer receive() { 64 if(_length == 0) return readLength(); 65 else return readBody(); 66 } 67 68 private void receiveImpl() { 69 immutable recv = _socket.receive(_recv); 70 if(recv == Socket.ERROR) throw new SocketException(lastSocketError); 71 else if(recv == 0) throw new SocketException("Connection timed out"); 72 _buffer.writeData(_recv[0..recv]); 73 } 74 75 private Buffer readLength() { 76 static if(idLength) immutable requiredLength = idLength + length; 77 else immutable requiredLength = length; 78 if(_buffer.canRead(requiredLength)) { 79 static if(idLength) _id = _buffer.readData(idLength); 80 _lengthData = _buffer.read!(ubyte[])(length); 81 static if(endianness != endian) reverse(_lengthData); 82 static if(lengthIncludesItself) _length -= length; 83 return readSequence(); 84 } else { 85 receiveImpl(); 86 return readLength(); 87 } 88 } 89 90 private Buffer readSequence() { 91 static if(usesSequence) { 92 if(_buffer.canRead(S.sizeof)) { 93 static if(usesSequence) { 94 _buffer.read!(sequenceEndianness, S)(); 95 sequence++; 96 } 97 return readBody(); 98 } else { 99 receiveImpl(); 100 return readSequence(); 101 } 102 } else { 103 return readBody(); 104 } 105 } 106 107 private Buffer readBody() { 108 if(_buffer.data.length >= _length) { 109 _returnBuffer.data = _buffer.readData(_length); 110 _length = 0; 111 return _returnBuffer; 112 } else { 113 receiveImpl(); 114 return readBody(); 115 } 116 } 117 118 public void send(Buffer buffer) { 119 writeLength(buffer); 120 static if(usesSequence) buffer.write!(sequenceEndianness, S)(sequence++, length); 121 static if(idLength) buffer.write(_id, 0); 122 if(_socket.send(buffer.data) == Socket.ERROR) throw new SocketException(lastSocketError); 123 } 124 125 protected void writeLength(Buffer buffer) { 126 immutable rlength = _length; 127 _length = buffer.data.length; 128 static if(lengthIncludesItself) _length += length; 129 static if(endianness != endian) reverse(_lengthData); 130 buffer.write(_lengthData, 0); 131 _length = rlength; 132 } 133 134 } 135 136 private void reverse(T)(ref T array) { 137 T ret; 138 foreach(i ; 0..array.length) { 139 ret[$-1-i] = array[i]; 140 } 141 array = ret; 142 } 143 144 string read0String(Buffer buffer) { 145 Appender!string ret; 146 char c; 147 while((c = buffer.read!char()) != '\0') { 148 ret.put(c); 149 } 150 return ret.data; 151 } 152 153 void write0String(Buffer buffer, string str) { 154 buffer.writeData(cast(void[])str); 155 buffer.write(ubyte(0)); 156 } 157 158 ubyte[] fromHexString(string hex) { 159 ubyte[] ret = new ubyte[hex.length / 2]; 160 foreach(i ; 0..ret.length) { 161 ret[i] = to!ubyte(hex[i*2..i*2+2], 16); 162 } 163 return ret; 164 } 165 166 string toSnakeCase(string input) { 167 Appender!string output; 168 foreach(c ; input) { 169 if(c >= 'A' && c <= 'Z') { 170 output.put('_'); 171 output.put(cast(char)(c + 32)); 172 } else { 173 output.put(c); 174 } 175 } 176 return output.data; 177 } 178 179 unittest { 180 181 assert("testTest".toSnakeCase() == "test_test"); 182 183 }