1 // Written in the D programming language. 2 3 module msgpack.buffer; 4 5 //import std.traits; 6 import std.range; 7 8 9 version(Posix) 10 { 11 import core.sys.posix.sys.uio : iovec; 12 } 13 else 14 { 15 /** 16 * from core.sys.posix.sys.uio.iovec for compatibility with posix. 17 */ 18 struct iovec 19 { 20 void* iov_base; 21 size_t iov_len; 22 } 23 } 24 25 26 /** 27 * $(D RefBuffer) is a reference stored buffer for more efficient serialization 28 * 29 * Example: 30 * ----- 31 * auto packer = packer(RefBuffer(16)); // threshold is 16 32 * 33 * // packs data 34 * 35 * writev(fd, cast(void*)packer.buffer.vector.ptr, packer.buffer.vector.length); 36 * ----- 37 */ 38 struct RefBuffer 39 { 40 private: 41 static struct Chunk 42 { 43 ubyte[] data; // storing serialized value 44 size_t used; // used size of data 45 } 46 47 immutable size_t Threshold; 48 immutable size_t ChunkSize; 49 50 // for putCopy 51 Chunk[] chunks_; // memory chunk for buffer 52 size_t index_; // index for cunrrent chunk 53 54 // for putRef 55 iovec[] vecList_; // reference to large data or copied data. 56 57 58 public: 59 /** 60 * Constructs a buffer. 61 * 62 * Params: 63 * threshold = the threshold of writing value or stores reference. 64 * chunkSize = the default size of chunk for allocation. 65 */ 66 @safe 67 this(in size_t threshold, in size_t chunkSize = 8192) 68 { 69 Threshold = threshold; 70 ChunkSize = chunkSize; 71 72 chunks_.length = 1; 73 chunks_[index_].data.length = chunkSize; 74 } 75 76 77 /** 78 * Returns the buffer contents that excluding references. 79 * 80 * Returns: 81 * the non-contiguous copied contents. 82 */ 83 @property @safe 84 nothrow ubyte[] data() 85 { 86 ubyte[] result; 87 88 foreach (ref chunk; chunks_) 89 result ~= chunk.data[0..chunk.used]; 90 91 return result; 92 } 93 94 95 /** 96 * Forwards to all buffer contents. 97 * 98 * Returns: 99 * the array of iovec struct that stores references. 100 */ 101 @property @safe 102 nothrow ref iovec[] vector() return 103 { 104 return vecList_; 105 } 106 107 108 /** 109 * Writes the argument to buffer and stores the reference of writed content 110 * if the argument size is smaller than threshold, 111 * otherwise stores the reference of argument directly. 112 * 113 * Params: 114 * value = the content to write. 115 */ 116 @safe 117 void put(in ubyte value) 118 { 119 ubyte[1] values = [value]; 120 putCopy(values); 121 } 122 123 124 /// ditto 125 @safe 126 void put(in ubyte[] value) 127 { 128 if (value.length < Threshold) 129 putCopy(value); 130 else 131 putRef(value); 132 } 133 134 135 private: 136 /* 137 * Stores the reference of $(D_PARAM value). 138 * 139 * Params: 140 * value = the content to write. 141 */ 142 @trusted 143 void putRef(in ubyte[] value) 144 { 145 vecList_.length += 1; 146 vecList_[$ - 1] = iovec(cast(void*)value.ptr, value.length); 147 } 148 149 150 /* 151 * Writes $(D_PARAM value) to buffer and appends to its reference. 152 * 153 * Params: 154 * value = the contents to write. 155 */ 156 @trusted 157 void putCopy(const scope ubyte[] value) 158 { 159 /* 160 * Helper for expanding new space. 161 */ 162 void expand(in size_t size) 163 { 164 const newSize = size < ChunkSize ? ChunkSize : size; 165 166 index_++; 167 chunks_.length = 1; 168 chunks_[index_].data.length = newSize; 169 } 170 171 const size = value.length; 172 173 // lacks current chunk? 174 if (chunks_[index_].data.length - chunks_[index_].used < size) 175 expand(size); 176 177 const base = chunks_[index_].used; // start index 178 auto data = chunks_[index_].data[base..base + size]; // chunk to write 179 180 data[] = value[]; 181 chunks_[index_].used += size; 182 183 // Optimization for avoiding iovec allocation. 184 if (vecList_.length && data.ptr == (vecList_[$ - 1].iov_base + 185 vecList_[$ - 1].iov_len)) 186 vecList_[$ - 1].iov_len += size; 187 else 188 putRef(data); 189 } 190 } 191 192 193 unittest 194 { 195 static assert(isOutputRange!(RefBuffer, ubyte) && 196 isOutputRange!(RefBuffer, ubyte[])); 197 198 auto buffer = RefBuffer(2, 4); 199 200 ubyte[] tests = [1, 2]; 201 foreach (v; tests) 202 buffer.put(v); 203 buffer.put(tests); 204 205 assert(buffer.data == tests, "putCopy failed"); 206 207 iovec[] vector = buffer.vector; 208 ubyte[] result; 209 210 assert(vector.length == 2, "Optimization failed"); 211 212 foreach (v; vector) 213 result ~= (cast(ubyte*)v.iov_base)[0..v.iov_len]; 214 215 assert(result == tests ~ tests); 216 }