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.StructReader;
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.ElementSize;
33 import capnproto.SegmentReader;
34 import capnproto.StructList;
35 import capnproto.Text;
36 import capnproto.WireHelpers;
37 
38 struct StructReader
39 {
40 public: //Variables.
41 	SegmentReader* segment;
42 	int data; //Byte offset to data section.
43 	int pointers; //Word offset of pointer section.
44 	int dataSize; //In bits.
45 	short pointerCount;
46 	int nestingLimit;
47 
48 public: //Methods.
49 	this(SegmentReader* segment, int data, int pointers, int dataSize, short pointerCount, int nestingLimit)
50 	{
51 		this.segment = segment;
52 		this.data = data;
53 		this.pointers = pointers;
54 		this.dataSize = dataSize;
55 		this.pointerCount = pointerCount;
56 		this.nestingLimit = nestingLimit;
57 	}
58 	
59 	bool _getBoolField(int offset)
60 	{
61 		if(offset < this.dataSize)
62 		{
63 			ubyte b = this.segment.buffer.get!ubyte(this.data + offset / 8);
64 			return (b & (1 << (offset % 8))) != 0;
65 		}
66 		return false;
67 	}
68 	
69 	bool _getBoolField(int offset, bool mask)
70 	{
71 		return this._getBoolField(offset) ^ mask;
72 	}
73 	
74 	byte _getByteField(int offset)
75 	{
76 		if((offset + 1) * 8 <= this.dataSize)
77 			return this.segment.buffer.get!byte(this.data + offset);
78 		return 0;
79 	}
80 	
81 	byte _getByteField(int offset, byte mask)
82 	{
83 		return cast(byte)(this._getByteField(offset) ^ mask);
84 	}
85 	
86 	ubyte _getUbyteField(int offset)
87 	{
88 		if((offset + 1) * 8 <= this.dataSize)
89 			return this.segment.buffer.get!ubyte(this.data + offset);
90 		return 0;
91 	}
92 	
93 	ubyte _getUbyteField(int offset, ubyte mask)
94 	{
95 		return cast(ubyte)(this._getByteField(offset) ^ mask);
96 	}
97 	
98 	short _getShortField(int offset)
99 	{
100 		if((offset + 1) * 16 <= this.dataSize)
101 			return this.segment.buffer.get!short(this.data + offset * 2);
102 		return 0;
103 	}
104 	
105 	short _getShortField(int offset, short mask)
106 	{
107 		return cast(short)(this._getShortField(offset) ^ mask);
108 	}
109 	
110 	ushort _getUshortField(int offset)
111 	{
112 		if((offset + 1) * 16 <= this.dataSize)
113 			return cast(ushort)this.segment.buffer.get!short(this.data + offset * 2);
114 		return 0;
115 	}
116 	
117 	ushort _getUshortField(int offset, ushort mask)
118 	{
119 		return cast(ushort)(this._getUshortField(offset) ^ mask);
120 	}
121 	
122 	int _getIntField(int offset)
123 	{
124 		if((offset + 1) * 32 <= this.dataSize)
125 			return this.segment.buffer.get!int(this.data + offset * 4);
126 		return 0;
127 	}
128 	
129 	int _getIntField(int offset, int mask)
130 	{
131 		return this._getIntField(offset) ^ mask;
132 	}
133 	
134 	uint _getUintField(int offset)
135 	{
136 		if((offset + 1) * 32 <= this.dataSize)
137 			return cast(uint)this.segment.buffer.get!int(this.data + offset * 4);
138 		return 0;
139 	}
140 	
141 	uint _getUintField(int offset, uint mask)
142 	{
143 		return this._getUintField(offset) ^ mask;
144 	}
145 	
146 	long _getLongField(int offset)
147 	{
148 		if((offset + 1) * 64 <= this.dataSize)
149 			return this.segment.buffer.get!long(this.data + offset * 8);
150 		return 0;
151 	}
152 	
153 	long _getLongField(int offset, long mask)
154 	{
155 		return this._getLongField(offset) ^ mask;
156 	}
157 	
158 	ulong _getUlongField(int offset)
159 	{
160 		if((offset + 1) * 64 <= this.dataSize)
161 			return cast(ulong)this.segment.buffer.get!long(this.data + offset * 8);
162 		return 0;
163 	}
164 	
165 	ulong _getUlongField(int offset, ulong mask)
166 	{
167 		return this._getUlongField(offset) ^ mask;
168 	}
169 	
170 	float _getFloatField(int offset)
171 	{
172 		if((offset + 1) * 32 <= this.dataSize)
173 			return this.segment.buffer.get!float(this.data + offset * 4);
174 		return 0;
175 	}
176 	
177 	float _getFloatField(int offset, int mask)
178 	{
179 		if((offset + 1) * 32 <= this.dataSize)
180 			return intBitsToFloat(this.segment.buffer.get!int(this.data + offset * 4) ^ mask);
181 		return intBitsToFloat(mask);
182 	}
183 	
184 	double _getDoubleField(int offset)
185 	{
186 		if((offset + 1) * 64 <= this.dataSize)
187 			return this.segment.buffer.get!double(this.data + offset * 8);
188 		return 0;
189 	}
190 	
191 	double _getDoubleField(int offset, long mask)
192 	{
193 		if((offset + 1) * 64 <= this.dataSize)
194 			return longBitsToDouble(this.segment.buffer.get!long(this.data + offset * 8) ^ mask);
195 		return longBitsToDouble(mask);
196 	}
197 	
198 	bool _pointerFieldIsNull(int ptrIndex)
199 	{
200 		return this.segment.buffer.get!long((this.pointers + ptrIndex) * Constants.BYTES_PER_WORD) == 0;
201 	}
202 	
203 	T.Reader _getPointerField(T)(int ptrIndex)
204 	{
205 		alias name = fullyQualifiedName!T;
206 		auto segment = cast(SegmentReader*)&SegmentReader.EMPTY;
207 		int pointer = 0;
208 		if(ptrIndex < this.pointerCount)
209 		{
210 			segment = this.segment;
211 			pointer = this.pointers + ptrIndex;
212 		}
213 		static if(is(T : AnyPointer))
214 			return T.Reader(segment, pointer, nestingLimit);
215 		else static if(is(T : Data))
216 			return WireHelpers.readDataPointer(segment, pointer, null, 0, 0);
217 		else static if(is(T : Text))
218 			return WireHelpers.readTextPointer(segment, pointer, null, 0, 0);
219 		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"))
220 			return WireHelpers.readListPointer!(T.Reader)(segment, pointer, null, 0, T.elementSize, this.nestingLimit);
221 		else
222 			return WireHelpers.readStructPointer!(T.Reader)(segment, pointer, null, 0, nestingLimit);
223 	}
224 	
225 	T.Reader _getPointerField(T)(int ptrIndex, SegmentReader* defaultSegment, int defaultOffset)
226 	{
227 		alias name = fullyQualifiedName!T;
228 		
229 		auto segment = cast(SegmentReader*)&SegmentReader.EMPTY;
230 		int pointer = 0;
231 		if(ptrIndex < this.pointerCount)
232 		{
233 			segment = this.segment;
234 			pointer = this.pointers + ptrIndex;
235 		}
236 		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"))
237 			return WireHelpers.readListPointer!(T.Reader)(segment, pointer, defaultSegment, defaultOffset, T.elementSize, this.nestingLimit);
238 		else
239 			return WireHelpers.readStructPointer!(T.Reader)(segment, pointer, defaultSegment, defaultOffset, this.nestingLimit);
240 	}
241 	
242 	StructList.Reader!(T.Reader) _getPointerListField(T)(int ptrIndex, SegmentReader* defaultSegment, int defaultOffset)
243 	{
244 		if(ptrIndex < this.pointerCount)
245 			return WireHelpers.readListPointer!(StructList.Reader!(T.Reader))(this.segment, this.pointers + ptrIndex, defaultSegment, defaultOffset, ElementSize.INLINE_COMPOSITE, this.nestingLimit);
246 		return WireHelpers.readListPointer!(StructList.Reader!(T.Reader))(cast(SegmentReader*)&SegmentReader.EMPTY, 0, defaultSegment, defaultOffset, ElementSize.INLINE_COMPOSITE, this.nestingLimit);
247 	}
248 	
249 	T.Reader _getPointerField(T)(int ptrIndex, ByteBuffer* defaultBuffer, int defaultOffset, int defaultSize)
250 	{
251 		auto segment = cast(SegmentReader*)&SegmentReader.EMPTY;
252 		int pointer = 0;
253 		if(ptrIndex < this.pointerCount)
254 		{
255 			segment = this.segment;
256 			pointer = this.pointers + ptrIndex;
257 		}
258 		
259 		static if(is(T : Data))
260 			return WireHelpers.readDataPointer(segment, pointer, defaultBuffer, defaultOffset, defaultSize);
261 		else static if(is(T : Text))
262 			return WireHelpers.readTextPointer(segment, pointer, defaultBuffer, defaultOffset, defaultSize);
263 		else
264 			static assert(0);
265 	}
266 }