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 }