OLD | NEW |
| (Empty) |
1 // Copyright (c) 2015, the Dartino project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE.md file. | |
4 | |
5 library service.struct; | |
6 | |
7 import "dart:dartino.ffi"; | |
8 import "dart:collection"; | |
9 | |
10 const int HEADER_SIZE = 56; | |
11 | |
12 Reader getRoot(Reader reader, ForeignMemory request) { | |
13 int segments = request.getInt32(HEADER_SIZE - 8); | |
14 if (segments == 0) { | |
15 MessageReader messageReader = new MessageReader(); | |
16 Segment segment = new Segment(messageReader, request); | |
17 messageReader.segments.add(segment); | |
18 reader.$segment = segment; | |
19 reader.$offset = HEADER_SIZE; | |
20 return reader; | |
21 } else { | |
22 return getSegmentedRoot(reader, request, segments); | |
23 } | |
24 } | |
25 | |
26 Reader getSegmentedRoot(Reader reader, ForeignMemory request, int segments) { | |
27 MessageReader messageReader = new MessageReader(); | |
28 int offset = HEADER_SIZE + 8; | |
29 for (int i = 0; i < segments; i++) { | |
30 int address = (Foreign.bitsPerMachineWord == 32) | |
31 ? request.getUint32(offset) | |
32 : request.getUint64(offset); | |
33 int size = request.getInt32(offset + 8); | |
34 ForeignMemory memory = new ForeignMemory.fromAddress(address, size); | |
35 Segment segment = new Segment(messageReader, memory); | |
36 messageReader.segments.add(segment); | |
37 offset += 16; | |
38 } | |
39 reader.$segment = messageReader.segments.first; | |
40 reader.$offset = HEADER_SIZE; | |
41 return reader; | |
42 } | |
43 | |
44 int getResultMessage(Builder builder) { | |
45 BuilderSegment segment = builder.$segment; | |
46 if (segment._next == null) { | |
47 // Mark result as being non-segmented. | |
48 ForeignMemory memory = segment.memory; | |
49 memory.setInt32(0, 0); | |
50 memory.setInt32(4, memory.length); | |
51 return memory.address; | |
52 } | |
53 | |
54 // The result is a segmented message. Build a memory block that | |
55 // contains the addresses and sizes of all of them. | |
56 int segments = segment._builder.$segments; | |
57 int size = 8 + (segments * 16); | |
58 ForeignMemory buffer = new ForeignMemory.allocated(size); | |
59 // Mark the result as being segmented. | |
60 buffer.setInt32(0, segments); | |
61 int offset = 8; | |
62 do { | |
63 buffer.setInt64(offset, segment.memory.address); | |
64 buffer.setInt32(offset + 8, segment._used); | |
65 segment = segment._next; | |
66 offset += 16; | |
67 } while (segment != null); | |
68 return buffer.address; | |
69 } | |
70 | |
71 class MessageReader { | |
72 final List<Segment> segments = []; | |
73 MessageReader(); | |
74 | |
75 Segment getSegment(int id) => segments[id]; | |
76 } | |
77 | |
78 class Segment { | |
79 final MessageReader reader; | |
80 final ForeignMemory memory; | |
81 Segment(this.reader, this.memory); | |
82 } | |
83 | |
84 class Reader { | |
85 Segment $segment; | |
86 int $offset; | |
87 | |
88 readStruct(Reader reader, int offset) { | |
89 Segment segment = $segment; | |
90 offset += $offset; | |
91 while (true) { | |
92 ForeignMemory memory = segment.memory; | |
93 int lo = memory.getInt32($offset + 0); | |
94 int hi = memory.getInt32($offset + 4); | |
95 int tag = lo & 3; | |
96 if (tag == 0) { | |
97 throw new UnimplementedError("Cannot read uninitialized structs"); | |
98 } else if (tag == 1) { | |
99 reader.$segment = segment; | |
100 reader.$offset = lo >> 2; | |
101 return reader; | |
102 } else { | |
103 segment = segment.reader.getSegment(hi); | |
104 $offset = lo >> 2; | |
105 } | |
106 } | |
107 } | |
108 | |
109 readList(ListReader reader, int offset) { | |
110 Segment segment = $segment; | |
111 offset += $offset; | |
112 while (true) { | |
113 ForeignMemory memory = segment.memory; | |
114 int lo = memory.getInt32(offset + 0); | |
115 int hi = memory.getInt32(offset + 4); | |
116 int tag = lo & 3; | |
117 if (tag == 0) { | |
118 // If the list hasn't been initialized, then | |
119 // we return an empty list. | |
120 reader.$length = 0; | |
121 return reader; | |
122 } else if (tag == 2) { | |
123 reader.$segment = segment; | |
124 reader.$offset = lo >> 2; | |
125 reader.$length = hi; | |
126 return reader; | |
127 } else { | |
128 segment = segment.reader.getSegment(hi); | |
129 offset = lo >> 2; | |
130 } | |
131 } | |
132 } | |
133 | |
134 String readString(ListReader reader, int offset) { | |
135 List<int> charCodes = readList(reader, offset); | |
136 return new String.fromCharCodes(charCodes); | |
137 } | |
138 } | |
139 | |
140 abstract class ListReader<T> extends Reader with ListMixin<T> { | |
141 int $length; | |
142 int get length => $length; // Required by List<T>. | |
143 | |
144 readListElement(Reader reader, int index, int size) { | |
145 reader.$segment = $segment; | |
146 reader.$offset = $offset + index * size; | |
147 return reader; | |
148 } | |
149 | |
150 void operator []=(int index, value) { | |
151 throw new UnsupportedError("ListReader::operator []="); | |
152 } | |
153 | |
154 void set length(int newLength) { | |
155 throw new UnsupportedError("ListReader::set length"); | |
156 } | |
157 } | |
158 | |
159 class BuilderSegment { | |
160 final MessageBuilder _builder; | |
161 final ForeignMemory memory; | |
162 int _id; | |
163 int _used = 0; | |
164 BuilderSegment _next; | |
165 | |
166 BuilderSegment(this._builder, this._id, int space) | |
167 : memory = new ForeignMemory.allocated(space); | |
168 | |
169 bool HasSpaceForBytes(int bytes) => _used + bytes <= memory.length; | |
170 | |
171 int Allocate(int bytes) { | |
172 if (!HasSpaceForBytes(bytes)) return -1; | |
173 var result = _used; | |
174 _used += bytes; | |
175 return result; | |
176 } | |
177 } | |
178 | |
179 class MessageBuilder { | |
180 BuilderSegment _first; | |
181 BuilderSegment _last; | |
182 int $segments = 1; | |
183 | |
184 MessageBuilder(int space) { | |
185 _first = new BuilderSegment(this, 0, space); | |
186 _last = _first; | |
187 } | |
188 | |
189 Builder initRoot(Builder builder, int size) { | |
190 int offset = _first.Allocate(8 + size); | |
191 builder.$segment = _first; | |
192 builder.$offset = offset + 8; | |
193 return builder; | |
194 } | |
195 | |
196 BuilderSegment FindSegmentForBytes(int bytes) { | |
197 if (_last.HasSpaceForBytes(bytes)) return _last; | |
198 int capacity = (bytes > 8192) ? bytes : 8192; | |
199 BuilderSegment segment = new BuilderSegment(this, $segments++, capacity); | |
200 _last._next = segment; | |
201 _last = segment; | |
202 return segment; | |
203 } | |
204 } | |
205 | |
206 class Builder { | |
207 BuilderSegment $segment; | |
208 int $offset; | |
209 | |
210 Builder NewStruct(Builder builder, int offset, int size) { | |
211 offset += $offset; | |
212 BuilderSegment segment = $segment; | |
213 while (true) { | |
214 int result = segment.Allocate(size); | |
215 ForeignMemory memory = segment.memory; | |
216 if (result >= 0) { | |
217 memory.setInt32(offset + 0, (result << 2) | 1); | |
218 memory.setInt32(offset + 4, 0); | |
219 builder.$segment = segment; | |
220 builder.$offset = result; | |
221 return builder; | |
222 } | |
223 | |
224 BuilderSegment other = segment._builder.FindSegmentForBytes(size + 8); | |
225 int target = other.Allocate(8); | |
226 memory.setInt32(offset + 0, (target << 2) | 3); | |
227 memory.setInt32(offset + 4, other._id); | |
228 | |
229 segment = other; | |
230 offset = target; | |
231 } | |
232 } | |
233 | |
234 ListBuilder NewList(ListBuilder list, | |
235 int offset, | |
236 int length, | |
237 int size) { | |
238 list.$length = length; | |
239 offset += $offset; | |
240 size *= length; | |
241 BuilderSegment segment = $segment; | |
242 while (true) { | |
243 int result = segment.Allocate(size); | |
244 ForeignMemory memory = segment.memory; | |
245 if (result >= 0) { | |
246 memory.setInt32(offset + 0, (result << 2) | 1); | |
247 memory.setInt32(offset + 4, length); | |
248 list.$segment = segment; | |
249 list.$offset = result; | |
250 return list; | |
251 } | |
252 | |
253 BuilderSegment other = segment._builder.FindSegmentForBytes(size + 8); | |
254 int target = other.Allocate(8); | |
255 memory.setInt32(offset + 0, (target << 2) | 3); | |
256 memory.setInt32(offset + 4, other._id); | |
257 | |
258 segment = other; | |
259 offset = target; | |
260 } | |
261 } | |
262 | |
263 void NewString(ListBuilder list, int offset, String value) { | |
264 NewList(list, offset, value.length, 2); | |
265 for (int i = 0; i < value.length; i++) { | |
266 list[i] = value.codeUnitAt(i); | |
267 } | |
268 } | |
269 } | |
270 | |
271 abstract class ListBuilder<T> extends Builder with ListMixin<T> { | |
272 int $length; | |
273 int get length => $length; | |
274 | |
275 readListElement(Builder builder, int index, int size) { | |
276 builder.$segment = $segment; | |
277 builder.$offset = $offset + index * size; | |
278 return builder; | |
279 } | |
280 | |
281 void operator []=(int index, value) { | |
282 throw new UnsupportedError("ListBuilder::operator []="); | |
283 } | |
284 | |
285 void set length(int newLength) { | |
286 throw new UnsupportedError("ListBuilder::set length"); | |
287 } | |
288 } | |
OLD | NEW |