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.ListBuilder;
23 
24 import std.string : startsWith;
25 import std.traits;
26 
27 import capnproto.Constants;
28 import capnproto.Data;
29 import capnproto.SegmentBuilder;
30 import capnproto.StructBuilder;
31 import capnproto.Text;
32 import capnproto.WireHelpers;
33 
34 struct ListBuilder
35 {
36 public: //Methods.
37 	this(SegmentBuilder* segment, int ptr, int elementCount, int step, int structDataSize, short structPointerCount)
38 	{
39 		this.segment = segment;
40 		this.ptr = ptr;
41 		this.elementCount = elementCount;
42 		this.step = step;
43 		this.structDataSize = structDataSize;
44 		this.structPointerCount = structPointerCount;
45 	}
46 	
47 	size_t length() const
48 	{
49 		return this.elementCount;
50 	}
51 	
52 	bool _getBooleanElement(size_t index)
53 	{
54 		long bindex = cast(long)index * this.step;
55 		byte b = this.segment.buffer.get!byte(this.ptr + cast(int)(bindex / Constants.BITS_PER_BYTE));
56 		return (b & (1 << (bindex % 8))) != 0;
57 	}
58 	
59 	byte _getByteElement(size_t index)
60 	{
61 		return this.segment.buffer.get!byte(this.ptr + cast(int)(cast(long)index * this.step / Constants.BITS_PER_BYTE));
62 	}
63 	
64 	short _getShortElement(size_t index)
65 	{
66 		return this.segment.buffer.get!short(this.ptr + cast(int)(cast(long)index * this.step / Constants.BITS_PER_BYTE));
67 	}
68 	
69 	int _getIntElement(size_t index)
70 	{
71 		return this.segment.buffer.get!int(this.ptr + cast(int)(cast(long)index * this.step / Constants.BITS_PER_BYTE));
72 	}
73 	
74 	long _getLongElement(size_t index)
75 	{
76 		return this.segment.buffer.get!long(this.ptr + cast(int)(cast(long)index * this.step / Constants.BITS_PER_BYTE));
77 	}
78 	
79 	float _getFloatElement(size_t index)
80 	{
81 		return this.segment.buffer.get!float(this.ptr + cast(int)(cast(long)index * this.step / Constants.BITS_PER_BYTE));
82 	}
83 	
84 	double _getDoubleElement(size_t index)
85 	{
86 		return this.segment.buffer.get!double(this.ptr + cast(int)(cast(long)index * this.step / Constants.BITS_PER_BYTE));
87 	}
88 	
89 	void _setBooleanElement(size_t index, bool value)
90 	{
91 		long bitOffset = index * this.step;
92 		byte bitnum = cast(byte)(bitOffset % 8);
93 		int position = cast(int)(this.ptr + (bitOffset / 8));
94 		byte oldValue = this.segment.buffer.get!byte(position);
95 		this.segment.buffer.put!ubyte(position, cast(byte)((oldValue & (~(1 << bitnum))) | ((value? 1 : 0) << bitnum)));
96 	}
97 	
98 	void _setByteElement(size_t index, byte value)
99 	{
100 		this.segment.buffer.put!ubyte(this.ptr + cast(int)(cast(long)index * this.step / Constants.BITS_PER_BYTE), value);
101 	}
102 	
103 	void _setShortElement(size_t index, short value)
104 	{
105 		this.segment.buffer.put!short(this.ptr + cast(int)(cast(long)index * this.step / Constants.BITS_PER_BYTE), value);
106 	}
107 	
108 	void _setIntElement(size_t index, int value)
109 	{
110 		this.segment.buffer.put!int(this.ptr + cast(int)(cast(long)index * this.step / Constants.BITS_PER_BYTE), value);
111 	}
112 	
113 	void _setLongElement(size_t index, long value)
114 	{
115 		this.segment.buffer.put!long(this.ptr + cast(int)(cast(long)index * this.step / Constants.BITS_PER_BYTE), value);
116 	}
117 	
118 	void _setFloatElement(size_t index, float value)
119 	{
120 		this.segment.buffer.put!float(this.ptr + cast(int)(cast(long)index * this.step / Constants.BITS_PER_BYTE), value);
121 	}
122 	
123 	void _setDoubleElement(size_t index, double value)
124 	{
125 		this.segment.buffer.put!double(this.ptr + cast(int)(cast(long)index * this.step / Constants.BITS_PER_BYTE), value);
126 	}
127 	
128 	T.Builder _getStructElement(T)(size_t index)
129 	{
130 		long indexBit = cast(long)index * this.step;
131 		int structData = this.ptr + cast(int)(indexBit / Constants.BITS_PER_BYTE);
132 		int structPointers = (structData + (this.structDataSize / 8)) / 8;
133 		return T.Builder(this.segment, structData, structPointers, this.structDataSize, this.structPointerCount);
134 	}
135 	
136 	T.Builder _getPointerElement(T)(size_t index)
137 	{
138 		alias name = fullyQualifiedName!T;
139 		
140 		int pointer = (this.ptr + cast(int)(cast(long)index * this.step / Constants.BITS_PER_BYTE)) / Constants.BYTES_PER_WORD;
141 		static if(is(T : Data))
142 			return WireHelpers.getWritableDataPointer(pointer, segment, null, 0, 0);
143 		else static if(is(T : Text))
144 			return WireHelpers.getWritableTextPointer(pointer, segment, null, 0, 0);
145 		else static if(name.startsWith("capnproto.DataList") || name.startsWith("capnproto.EnumList") || name.startsWith("capnproto.PrimitiveList") || name.startsWith("capnproto.ListList") || name.startsWith("capnproto.TextList"))
146 			return WireHelpers.getWritableListPointer!(T.Builder)(pointer, this.segment, T.elementSize, null, 0);
147 		else static if(name.startsWith("capnproto.StructList"))
148 			return WireHelpers.getWritableStructListPointer!(T.Builder)(pointer, this.segment, T.structSize, null, 0);
149 		else
150 			return WireHelpers.getWritableStructPointer!(T.Builder)(pointer, this.segment, T.structSize, null, 0);
151 	}
152 	
153 	T.Builder _initPointerElement(T)(size_t index, int elementCount)
154 	{
155 		alias name = fullyQualifiedName!T;
156 		static if(name.startsWith("capnproto.StructList"))
157 			return WireHelpers.initStructListPointer!(T.Builder)((this.ptr + cast(int)(cast(long)index * this.step / Constants.BITS_PER_BYTE)) / Constants.BYTES_PER_WORD, this.segment, elementCount, T.structSize);
158 		else
159 			return WireHelpers.initListPointer!(T.Builder)((this.ptr + cast(int)(cast(long)index * this.step / Constants.BITS_PER_BYTE)) / Constants.BYTES_PER_WORD, this.segment, elementCount, T.elementSize);
160 	}
161 	
162 	void _setPointerElement(Reader)(size_t index, Reader value)
163 	{
164 		import capnproto.StructReader;
165 		int pointer = (this.ptr + cast(int)(cast(long)index * this.step / Constants.BITS_PER_BYTE)) / Constants.BYTES_PER_WORD;
166 		static if(is(Reader : Data.Reader))
167 			WireHelpers.setDataPointer(pointer, this.segment, value);
168 		else static if(is(Reader : Text.Reader))
169 			WireHelpers.setTextPointer(pointer, this.segment, value);
170 		else
171 			WireHelpers.setStructPointer(this.segment, pointer, cast(StructReader)value);
172 	}
173 
174 package: //Variables.
175 	SegmentBuilder* segment;
176 	int ptr; //Byte offset to front of list.
177 	int elementCount;
178 	int step; //In bits.
179 	int structDataSize; //In bits.
180 	short structPointerCount;
181 }