1 // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
2 // Licensed under the MIT License:
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE.
21 
22 module capnproto.StructBuilder;
23 
24 import std.string : startsWith;
25 import std.traits;
26 
27 import java.nio.ByteBuffer;
28 
29 import capnproto.AnyPointer;
30 import capnproto.Constants;
31 import capnproto.Data;
32 import capnproto.SegmentBuilder;
33 import capnproto.SegmentReader;
34 import capnproto.StructList;
35 import capnproto.StructReader;
36 import capnproto.StructSize;
37 import capnproto.Text;
38 import capnproto.WireHelpers;
39 
40 struct StructBuilder
41 {
42 public: //Variables.
43 	SegmentBuilder* segment;
44 	int data; //Byte offset to data section.
45 	int pointers; //Word offset of pointer section.
46 	int dataSize; //In bits.
47 	short pointerCount;
48 
49 public: //Methods.
50 	this(SegmentBuilder* segment, int data, int pointers, int dataSize, short pointerCount)
51 	{
52 		this.segment = segment;
53 		this.data = data;
54 		this.pointers = pointers;
55 		this.dataSize = dataSize;
56 		this.pointerCount = pointerCount;
57 	}
58 	
59 	T asReader(T)()
60 	{
61 		return T(segment.asReader(), data, pointers, dataSize, pointerCount, 0x7fffffff);
62 	}
63 	
64 	bool _getBoolField(int offset)
65 	{
66 		int bitOffset = offset;
67 		int position = this.data + (bitOffset / 8);
68 		return (this.segment.buffer.get!ubyte(position) & (1 << (bitOffset % 8))) != 0;
69 	}
70 	
71 	bool _getBoolField(int offset, bool mask)
72 	{
73 		return this._getBoolField(offset) ^ mask;
74 	}
75 	
76 	void _setBoolField(int offset, bool value)
77 	{
78 		int bitOffset = offset;
79 		byte bitnum = cast(byte)(bitOffset % 8);
80 		int position = this.data + (bitOffset / 8);
81 		byte oldValue = this.segment.buffer.get!byte(position);
82 		this.segment.buffer.put!ubyte(position, cast(byte)((oldValue & (~(1 << bitnum))) | ((value? 1 : 0) << bitnum)));
83 	}
84 	
85 	void _setBoolField(int offset, bool value, bool mask)
86 	{
87 		this._setBoolField(offset, value ^ mask);
88 	}
89 	
90 	byte _getByteField(int offset)
91 	{
92 		return this.segment.buffer.get!byte(this.data + offset);
93 	}
94 	
95 	byte _getByteField(int offset, byte mask)
96 	{
97 		return cast(byte)(this._getByteField(offset) ^ mask);
98 	}
99 	
100 	void _setByteField(int offset, byte value)
101 	{
102 		this.segment.buffer.put!byte(this.data + offset, value);
103 	}
104 	
105 	void _setByteField(int offset, byte value, byte mask)
106 	{
107 		this._setByteField(offset, cast(byte)(value ^ mask));
108 	}
109 	
110 	ubyte _getUbyteField(int offset)
111 	{
112 		return this.segment.buffer.get!ubyte(this.data + offset);
113 	}
114 	
115 	ubyte _getUbyteField(int offset, ubyte mask)
116 	{
117 		return cast(ubyte)(this._getByteField(offset) ^ mask);
118 	}
119 	
120 	void _setUbyteField(int offset, ubyte value)
121 	{
122 		this.segment.buffer.put!ubyte(this.data + offset, value);
123 	}
124 	
125 	void _setUbyteField(int offset, ubyte value, ubyte mask)
126 	{
127 		this._setUbyteField(offset, cast(ubyte)(value ^ mask));
128 	}
129 	
130 	short _getShortField(int offset)
131 	{
132 		return this.segment.buffer.get!short(this.data + offset * 2);
133 	}
134 	
135 	short _getShortField(int offset, short mask)
136 	{
137 		return cast(short)(this._getShortField(offset) ^ mask);
138 	}
139 	
140 	void _setShortField(int offset, short value)
141 	{
142 		this.segment.buffer.put!short(this.data + offset * 2, value);
143 	}
144 	
145 	void _setShortField(int offset, short value, short mask)
146 	{
147 		this._setShortField(offset, cast(short)(value ^ mask));
148 	}
149 	
150 	ushort _getUshortField(int offset)
151 	{
152 		return this.segment.buffer.get!short(this.data + offset * 2);
153 	}
154 	
155 	ushort _getUshortField(int offset, ushort mask)
156 	{
157 		return cast(ushort)(this._getShortField(offset) ^ mask);
158 	}
159 	
160 	void _setUshortField(int offset, ushort value)
161 	{
162 		this.segment.buffer.put!short(this.data + offset * 2, value);
163 	}
164 	
165 	void _setUshortField(int offset, ushort value, ushort mask)
166 	{
167 		this._setShortField(offset, cast(ushort)(value ^ mask));
168 	}
169 	
170 	int _getIntField(int offset)
171 	{
172 		return this.segment.buffer.get!int(this.data + offset * 4);
173 	}
174 	
175 	int _getIntField(int offset, int mask)
176 	{
177 		return this._getIntField(offset) ^ mask;
178 	}
179 	
180 	void _setIntField(int offset, int value)
181 	{
182 		this.segment.buffer.put!int(this.data + offset * 4, value);
183 	}
184 	
185 	void _setIntField(int offset, int value, int mask)
186 	{
187 		this._setIntField(offset, value ^ mask);
188 	}
189 	
190 	uint _getUintField(int offset)
191 	{
192 		return this.segment.buffer.get!int(this.data + offset * 4);
193 	}
194 	
195 	uint _getUintField(int offset, uint mask)
196 	{
197 		return this._getIntField(offset) ^ mask;
198 	}
199 	
200 	void _setUintField(int offset, uint value)
201 	{
202 		this.segment.buffer.put!int(this.data + offset * 4, value);
203 	}
204 	
205 	void _setUintField(int offset, uint value, uint mask)
206 	{
207 		this._setIntField(offset, value ^ mask);
208 	}
209 	
210 	long _getLongField(int offset)
211 	{
212 		return this.segment.buffer.get!long(this.data + offset * 8);
213 	}
214 	
215 	long _getLongField(int offset, long mask)
216 	{
217 		return this._getLongField(offset) ^ mask;
218 	}
219 	
220 	void _setLongField(int offset, long value)
221 	{
222 		this.segment.buffer.put!long(this.data + offset * 8, value);
223 	}
224 	
225 	void _setLongField(int offset, long value, long mask)
226 	{
227 		this._setLongField(offset, value ^ mask);
228 	}
229 	
230 	ulong _getUlongField(int offset)
231 	{
232 		return this.segment.buffer.get!long(this.data + offset * 8);
233 	}
234 	
235 	ulong _getUlongField(int offset, ulong mask)
236 	{
237 		return this._getLongField(offset) ^ mask;
238 	}
239 	
240 	void _setUlongField(int offset, ulong value)
241 	{
242 		this.segment.buffer.put!long(this.data + offset * 8, value);
243 	}
244 	
245 	void _setUlongField(int offset, ulong value, ulong mask)
246 	{
247 		this._setLongField(offset, value ^ mask);
248 	}
249 	
250 	float _getFloatField(int offset)
251 	{
252 		return this.segment.buffer.get!float(this.data + offset * 4);
253 	}
254 	
255 	float _getFloatField(int offset, int mask)
256 	{
257 		return intBitsToFloat(this.segment.buffer.get!int(this.data + offset * 4) ^ mask);
258 	}
259 	
260 	void _setFloatField(int offset, float value)
261 	{
262 		this.segment.buffer.put!float(this.data + offset * 4, value);
263 	}
264 	
265 	void _setFloatField(int offset, float value, int mask)
266 	{
267 		this.segment.buffer.put!int(this.data + offset * 4, floatToIntBits(value) ^ mask);
268 	}
269 	
270 	double _getDoubleField(int offset)
271 	{
272 		return this.segment.buffer.get!double(this.data + offset * 8);
273 	}
274 	
275 	double _getDoubleField(int offset, long mask)
276 	{
277 		return longBitsToDouble(this.segment.buffer.get!long(this.data + offset * 8) ^ mask);
278 	}
279 	
280 	void _setDoubleField(int offset, double value)
281 	{
282 		this.segment.buffer.put!double(this.data + offset * 8, value);
283 	}
284 	
285 	void _setDoubleField(int offset, double value, long mask)
286 	{
287 		this.segment.buffer.put!long(this.data + offset * 8, doubleToLongBits(value) ^ mask);
288 	}
289 	
290 	bool _pointerFieldIsNull(int ptrIndex)
291 	{
292 		return this.segment.buffer.get!long((this.pointers + ptrIndex) * Constants.BYTES_PER_WORD) == 0;
293 	}
294 	
295 	void _clearPointerField(int ptrIndex)
296 	{
297 		int pointer = this.pointers + ptrIndex;
298 		WireHelpers.zeroObject(this.segment, pointer);
299 		this.segment.buffer.put!long(pointer * 8, 0L);
300 	}
301 	
302 	T.Builder _getPointerField(T)(int index)
303 	{
304 		alias name = fullyQualifiedName!T;
305 		
306 		static if(is(T : AnyPointer))
307 			return T.Builder(this.segment, this.pointers + index);
308 		else static if(is(T : Data))
309 			return WireHelpers.getWritableDataPointer(this.pointers + index, this.segment, null, 0, 0);
310 		else static if(is(T : Text))
311 			return WireHelpers.getWritableTextPointer(this.pointers + index, this.segment, null, 0, 0);
312 		else static if(name.startsWith("capnproto.DataList") || name.startsWith("capnproto.EnumList") || name.startsWith("capnproto.PrimitiveList") || name.startsWith("capnproto.ListList") || name.startsWith("capnproto.StructList") || name.startsWith("capnproto.TextList"))
313 			return WireHelpers.getWritableListPointer!(T.Builder)(this.pointers + index, this.segment, T.elementSize, null, 0);
314 		else
315 			return WireHelpers.getWritableStructPointer!(T.Builder)(this.pointers + index, this.segment, T.structSize, null, 0);
316 	}
317 	
318 	T.Builder _getPointerField(T)(int index, SegmentReader* defaultSegment, int defaultOffset)
319 	{
320 		alias name = fullyQualifiedName!T;
321 		
322 		static if(is(T : Data))
323 			return WireHelpers.getWritableDataPointer(this.pointers + index, this.segment, defaultSegment, defaultOffset, 0);
324 		else static if(is(T : Text))
325 			return WireHelpers.getWritableTextPointer(this.pointers + index, this.segment, defaultSegment, defaultOffset, 0);
326 		else static if(name.startsWith("capnproto.DataList") || name.startsWith("capnproto.EnumList") || name.startsWith("capnproto.PrimitiveList") || name.startsWith("capnproto.ListList") || name.startsWith("capnproto.TextList"))
327 			return WireHelpers.getWritableListPointer!(T.Builder)(this.pointers + index, this.segment, T.elementSize, defaultSegment, defaultOffset);
328 		else static if(name.startsWith("capnproto.StructList"))
329 			return WireHelpers.getWritableStructListPointer!(T.Builder)(this.pointers + index, this.segment, T.structSize, defaultSegment, defaultOffset);
330 		else
331 			return WireHelpers.getWritableStructPointer!(T.Builder)(this.pointers + index, this.segment, T.structSize, defaultSegment, defaultOffset);
332 	}
333 	
334 	T.Builder _getPointerField(T)(int index, ByteBuffer* defaultBuffer, int defaultOffset, int defaultSize)
335 	{
336 		static if(is(T : Data))
337 			return WireHelpers.getWritableDataPointer(this.pointers + index, this.segment, defaultBuffer, defaultOffset, defaultSize);
338 		else static if(is(T : Text))
339 			return WireHelpers.getWritableTextPointer(this.pointers + index, this.segment, defaultBuffer, defaultOffset, defaultSize);
340 		else
341 			static assert(0);
342 	}
343 	
344 	T.Builder _initPointerField(T)(int index, int elementCount)
345 	{
346 		alias name = fullyQualifiedName!T;
347 		
348 		static if(is(T : AnyPointer))
349 		{
350 			auto result = T.Builder(this.segment, this.pointers + index);
351 			result.clear();
352 			return result;
353 		}
354 		else static if(is(T : Data))
355 			return WireHelpers.initDataPointer(this.pointers + index, this.segment, elementCount);
356 		else static if(is(T : Text))
357 			return WireHelpers.initTextPointer(this.pointers + index, this.segment, elementCount);
358 		else static if(name.startsWith("capnproto.DataList") || name.startsWith("capnproto.EnumList") || name.startsWith("capnproto.PrimitiveList") || name.startsWith("capnproto.ListList") || name.startsWith("capnproto.TextList"))
359 			return WireHelpers.initListPointer!(T.Builder)(this.pointers + index, this.segment, elementCount, T.elementSize);
360 		else static if(name.startsWith("capnproto.StructList"))
361 			return WireHelpers.initStructListPointer!(T.Builder)(this.pointers + index, this.segment, elementCount, T.structSize);
362 		else
363 			return WireHelpers.initStructPointer!(T.Builder)(this.pointers + index, this.segment, T.structSize);
364 	}
365 	
366 	void _setPointerField(T)(int index, T.Reader value)
367 	{
368 		alias name = fullyQualifiedName!T;
369 		
370 		static if(is(T : AnyPointer))
371 			assert(0);
372 		else static if(is(T : Data))
373 			WireHelpers.setDataPointer(this.pointers + index, this.segment, value);
374 		else static if(is(T : Text))
375 			WireHelpers.setTextPointer(this.pointers + index, this.segment, value);
376 		else static if(name.startsWith("capnproto.DataList") || name.startsWith("capnproto.EnumList") || name.startsWith("capnproto.PrimitiveList") || name.startsWith("capnproto.ListList") || name.startsWith("capnproto.StructList") || name.startsWith("capnproto.TextList"))
377 			WireHelpers.setListPointer(this.segment, this.pointers + index, value.b);
378 		else
379 			WireHelpers.setStructPointer(this.segment, this.pointers + index, value.b);
380 	}
381 }