1 /* 2 * Hunt - A redis client library for D programming language. 3 * 4 * Copyright (C) 2018-2019 HuntLabs 5 * 6 * Website: https://www.huntlabs.net/ 7 * 8 * Licensed under the Apache-2.0 License. 9 * 10 */ 11 12 module hunt.redis.util.RedisOutputStream; 13 14 import hunt.Exceptions; 15 import hunt.logging.ConsoleLogger; 16 import hunt.stream.Common; 17 import hunt.stream.FilterInputStream; 18 import hunt.stream.FilterOutputStream; 19 20 21 /** 22 * The class implements a buffered output stream without synchronization There are also special 23 * operations like in-place string encoding. This stream fully ignore mark/reset and should not be 24 * used outside Redis 25 */ 26 class RedisOutputStream : FilterOutputStream { 27 protected byte[] buf; 28 29 protected int count; 30 31 // dfmt off 32 private enum int[] sizeTable = [ 33 9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999, int.max 34 ]; 35 36 private enum byte[] DigitTens = [ 37 '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '1', '1', '1', 38 '1', '1', '1', '1', '1', '1', '1', '2', '2', '2', '2', '2', '2', 39 '2', '2', '2', '2', '3', '3', '3', '3', '3', '3', '3', '3', '3', 40 '3', '4', '4', '4', '4', '4', '4', '4', '4', '4', '4', '5', '5', 41 '5', '5', '5', '5', '5', '5', '5', '5', '6', '6', '6', '6', '6', 42 '6', '6', '6', '6', '6', '7', '7', '7', '7', '7', '7', '7', '7', 43 '7', '7', '8', '8', '8', '8', '8', '8', '8', '8', '8', '8', '9', 44 '9', '9', '9', '9', '9', '9', '9', '9', '9' 45 ]; 46 47 private enum byte[] DigitOnes = [ 48 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', 49 '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', 50 '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', 51 '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', 52 '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', 53 '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', 54 '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 55 '1', '2', '3', '4', '5', '6', '7', '8', '9' 56 ]; 57 58 private enum byte[] digits = [ 59 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 60 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 61 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' 62 ]; 63 // dfmt on 64 65 this(OutputStream outputStream) { 66 this(outputStream, 8192); 67 } 68 69 this(OutputStream outputStream, int size) { 70 super(outputStream); 71 if (size <= 0) { 72 throw new IllegalArgumentException("Buffer size <= 0"); 73 } 74 buf = new byte[size]; 75 } 76 77 private void flushBuffer() { 78 if (count > 0) { 79 version(HUNT_REDIS_DEBUG_MORE) { 80 if(count<32) { 81 tracef("outgoing: %s", cast(string)buf[0 .. count]); 82 } else { 83 tracef("outgoing: %s", cast(string)buf[0 .. 32]); 84 } 85 } 86 outputStream.write(buf, 0, count); 87 count = 0; 88 } 89 } 90 91 void write(byte b) { 92 if (count == buf.length) { 93 flushBuffer(); 94 } 95 buf[count++] = b; 96 } 97 98 alias write = FilterOutputStream.write; 99 alias write = OutputStream.write; 100 101 override void write(byte[] b) { 102 write(b, 0, cast(int) b.length); 103 } 104 105 override void write(byte[] b, int off, int len) { 106 version(HUNT_REDIS_DEBUG_MORE) { 107 infof("%d bytes: %(%02X %)", len, b[off .. off+len]); 108 } 109 110 if (len >= buf.length) { 111 flushBuffer(); 112 outputStream.write(b, off, len); 113 } else { 114 if (len >= buf.length - count) { 115 flushBuffer(); 116 } 117 118 // System.arraycopy(b, off, buf, count, len); 119 buf[count .. count + len] = b[off .. off + len]; 120 count += len; 121 } 122 } 123 124 void writeCrLf() { 125 if (2 >= buf.length - count) { 126 flushBuffer(); 127 } 128 129 buf[count++] = '\r'; 130 buf[count++] = '\n'; 131 } 132 133 void writeIntCrLf(int value) { 134 if (value < 0) { 135 write(cast(byte) '-'); 136 value = -value; 137 } 138 139 int size = 0; 140 while (value > sizeTable[size]) 141 size++; 142 143 size++; 144 if (size >= buf.length - count) { 145 flushBuffer(); 146 } 147 148 int q, r; 149 int charPos = count + size; 150 151 while (value >= 65536) { 152 q = value / 100; 153 r = value - ((q << 6) + (q << 5) + (q << 2)); 154 value = q; 155 buf[--charPos] = DigitOnes[r]; 156 buf[--charPos] = DigitTens[r]; 157 } 158 159 for (;;) { 160 q = (value * 52429) >>> (16 + 3); 161 r = value - ((q << 3) + (q << 1)); 162 buf[--charPos] = digits[r]; 163 value = q; 164 if (value == 0) 165 break; 166 } 167 count += size; 168 169 writeCrLf(); 170 } 171 172 override void flush() { 173 flushBuffer(); 174 outputStream.flush(); 175 } 176 }