OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_INTERNAL_H_ | 5 #ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_INTERNAL_H_ |
6 #define MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_INTERNAL_H_ | 6 #define MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_INTERNAL_H_ |
7 | 7 |
8 #include <new> | 8 #include <new> |
| 9 #include <vector> |
9 | 10 |
10 #include "mojo/public/cpp/bindings/buffer.h" | |
11 #include "mojo/public/cpp/bindings/lib/bindings_internal.h" | 11 #include "mojo/public/cpp/bindings/lib/bindings_internal.h" |
12 #include "mojo/public/cpp/bindings/lib/bindings_serialization.h" | 12 #include "mojo/public/cpp/bindings/lib/bindings_serialization.h" |
13 #include "mojo/public/cpp/bindings/passable.h" | 13 #include "mojo/public/cpp/bindings/lib/buffer.h" |
14 | 14 |
15 namespace mojo { | 15 namespace mojo { |
16 template <typename T> class Array; | 16 template <typename T> class Array; |
| 17 class String; |
17 | 18 |
18 namespace internal { | 19 namespace internal { |
19 | 20 |
20 template <typename T> | 21 template <typename T> |
21 struct ArrayDataTraits { | 22 struct ArrayDataTraits { |
22 typedef T StorageType; | 23 typedef T StorageType; |
23 typedef Array<T> Wrapper; | |
24 typedef T& Ref; | 24 typedef T& Ref; |
25 typedef T const& ConstRef; | 25 typedef T const& ConstRef; |
26 | 26 |
27 static size_t GetStorageSize(size_t num_elements) { | 27 static size_t GetStorageSize(size_t num_elements) { |
28 return sizeof(StorageType) * num_elements; | 28 return sizeof(StorageType) * num_elements; |
29 } | 29 } |
30 static Ref ToRef(StorageType* storage, size_t offset) { | 30 static Ref ToRef(StorageType* storage, size_t offset) { |
31 return storage[offset]; | 31 return storage[offset]; |
32 } | 32 } |
33 static ConstRef ToConstRef(const StorageType* storage, size_t offset) { | 33 static ConstRef ToConstRef(const StorageType* storage, size_t offset) { |
34 return storage[offset]; | 34 return storage[offset]; |
35 } | 35 } |
36 }; | 36 }; |
37 | 37 |
38 template <typename P> | 38 template <typename P> |
39 struct ArrayDataTraits<P*> { | 39 struct ArrayDataTraits<P*> { |
40 typedef StructPointer<P> StorageType; | 40 typedef StructPointer<P> StorageType; |
41 typedef Array<typename P::Wrapper> Wrapper; | |
42 typedef P*& Ref; | 41 typedef P*& Ref; |
43 typedef P* const& ConstRef; | 42 typedef P* const& ConstRef; |
44 | 43 |
45 static size_t GetStorageSize(size_t num_elements) { | 44 static size_t GetStorageSize(size_t num_elements) { |
46 return sizeof(StorageType) * num_elements; | 45 return sizeof(StorageType) * num_elements; |
47 } | 46 } |
48 static Ref ToRef(StorageType* storage, size_t offset) { | 47 static Ref ToRef(StorageType* storage, size_t offset) { |
49 return storage[offset].ptr; | 48 return storage[offset].ptr; |
50 } | 49 } |
51 static ConstRef ToConstRef(const StorageType* storage, size_t offset) { | 50 static ConstRef ToConstRef(const StorageType* storage, size_t offset) { |
(...skipping 18 matching lines...) Expand all Loading... |
70 operator bool() const; | 69 operator bool() const; |
71 private: | 70 private: |
72 friend struct ArrayDataTraits<bool>; | 71 friend struct ArrayDataTraits<bool>; |
73 BitRef(uint8_t* storage, uint8_t mask); | 72 BitRef(uint8_t* storage, uint8_t mask); |
74 BitRef(); | 73 BitRef(); |
75 uint8_t* storage_; | 74 uint8_t* storage_; |
76 uint8_t mask_; | 75 uint8_t mask_; |
77 }; | 76 }; |
78 | 77 |
79 typedef uint8_t StorageType; | 78 typedef uint8_t StorageType; |
80 typedef Array<bool> Wrapper; | |
81 typedef BitRef Ref; | 79 typedef BitRef Ref; |
82 typedef bool ConstRef; | 80 typedef bool ConstRef; |
83 | 81 |
84 static size_t GetStorageSize(size_t num_elements) { | 82 static size_t GetStorageSize(size_t num_elements) { |
85 return ((num_elements + 7) / 8); | 83 return ((num_elements + 7) / 8); |
86 } | 84 } |
87 static BitRef ToRef(StorageType* storage, size_t offset) { | 85 static BitRef ToRef(StorageType* storage, size_t offset) { |
88 return BitRef(&storage[offset / 8], 1 << (offset % 8)); | 86 return BitRef(&storage[offset / 8], 1 << (offset % 8)); |
89 } | 87 } |
90 static bool ToConstRef(const StorageType* storage, size_t offset) { | 88 static bool ToConstRef(const StorageType* storage, size_t offset) { |
91 return (storage[offset / 8] & (1 << (offset % 8))) != 0; | 89 return (storage[offset / 8] & (1 << (offset % 8))) != 0; |
92 } | 90 } |
93 }; | 91 }; |
94 | 92 |
95 // What follows is code to support the serialization of Array_Data<T>. There | 93 // What follows is code to support the serialization of Array_Data<T>. There |
96 // are two interesting cases: arrays of primitives and arrays of objects. | 94 // are two interesting cases: arrays of primitives and arrays of objects. |
97 // Arrays of objects are represented as arrays of pointers to objects. | 95 // Arrays of objects are represented as arrays of pointers to objects. |
98 | 96 |
99 template <typename T, bool kIsHandle> struct ArraySerializationHelper; | 97 template <typename T, bool kIsHandle> struct ArraySerializationHelper; |
100 | 98 |
101 template <typename T> | 99 template <typename T> |
102 struct ArraySerializationHelper<T, false> { | 100 struct ArraySerializationHelper<T, false> { |
103 typedef typename ArrayDataTraits<T>::StorageType ElementType; | 101 typedef typename ArrayDataTraits<T>::StorageType ElementType; |
104 | 102 |
105 static size_t ComputeSizeOfElements(const ArrayHeader* header, | |
106 const ElementType* elements) { | |
107 return 0; | |
108 } | |
109 | |
110 static void CloneElements(const ArrayHeader* header, | |
111 ElementType* elements, | |
112 Buffer* buf) { | |
113 } | |
114 | |
115 static void ClearHandles(const ArrayHeader* header, ElementType* elements) { | |
116 } | |
117 | |
118 static void CloseHandles(const ArrayHeader* header, | |
119 ElementType* elements) { | |
120 } | |
121 | |
122 static void EncodePointersAndHandles(const ArrayHeader* header, | 103 static void EncodePointersAndHandles(const ArrayHeader* header, |
123 ElementType* elements, | 104 ElementType* elements, |
124 std::vector<Handle>* handles) { | 105 std::vector<Handle>* handles) { |
125 } | 106 } |
126 | 107 |
127 static bool DecodePointersAndHandles(const ArrayHeader* header, | 108 static bool DecodePointersAndHandles(const ArrayHeader* header, |
128 ElementType* elements, | 109 ElementType* elements, |
129 Message* message) { | 110 Message* message) { |
130 return true; | 111 return true; |
131 } | 112 } |
132 }; | 113 }; |
133 | 114 |
134 template <> | 115 template <> |
135 struct ArraySerializationHelper<Handle, true> { | 116 struct ArraySerializationHelper<Handle, true> { |
136 typedef ArrayDataTraits<Handle>::StorageType ElementType; | 117 typedef ArrayDataTraits<Handle>::StorageType ElementType; |
137 | 118 |
138 static size_t ComputeSizeOfElements(const ArrayHeader* header, | |
139 const ElementType* elements) { | |
140 return 0; | |
141 } | |
142 | |
143 static void CloneElements(const ArrayHeader* header, | |
144 ElementType* elements, | |
145 Buffer* buf) { | |
146 } | |
147 | |
148 static void ClearHandles(const ArrayHeader* header, ElementType* elements); | |
149 | |
150 static void CloseHandles(const ArrayHeader* header, ElementType* elements); | |
151 | |
152 static void EncodePointersAndHandles(const ArrayHeader* header, | 119 static void EncodePointersAndHandles(const ArrayHeader* header, |
153 ElementType* elements, | 120 ElementType* elements, |
154 std::vector<Handle>* handles); | 121 std::vector<Handle>* handles); |
155 | 122 |
156 static bool DecodePointersAndHandles(const ArrayHeader* header, | 123 static bool DecodePointersAndHandles(const ArrayHeader* header, |
157 ElementType* elements, | 124 ElementType* elements, |
158 Message* message); | 125 Message* message); |
159 }; | 126 }; |
160 | 127 |
161 template <typename H> | 128 template <typename H> |
162 struct ArraySerializationHelper<H, true> { | 129 struct ArraySerializationHelper<H, true> { |
163 typedef typename ArrayDataTraits<H>::StorageType ElementType; | 130 typedef typename ArrayDataTraits<H>::StorageType ElementType; |
164 | 131 |
165 static size_t ComputeSizeOfElements(const ArrayHeader* header, | |
166 const ElementType* elements) { | |
167 return 0; | |
168 } | |
169 | |
170 static void CloneElements(const ArrayHeader* header, | |
171 ElementType* elements, | |
172 Buffer* buf) { | |
173 } | |
174 | |
175 static void ClearHandles(const ArrayHeader* header, ElementType* elements) { | |
176 ArraySerializationHelper<Handle, true>::ClearHandles(header, elements); | |
177 } | |
178 | |
179 static void CloseHandles(const ArrayHeader* header, ElementType* elements) { | |
180 ArraySerializationHelper<Handle, true>::CloseHandles(header, elements); | |
181 } | |
182 | |
183 static void EncodePointersAndHandles(const ArrayHeader* header, | 132 static void EncodePointersAndHandles(const ArrayHeader* header, |
184 ElementType* elements, | 133 ElementType* elements, |
185 std::vector<Handle>* handles) { | 134 std::vector<Handle>* handles) { |
186 ArraySerializationHelper<Handle, true>::EncodePointersAndHandles( | 135 ArraySerializationHelper<Handle, true>::EncodePointersAndHandles( |
187 header, elements, handles); | 136 header, elements, handles); |
188 } | 137 } |
189 | 138 |
190 static bool DecodePointersAndHandles(const ArrayHeader* header, | 139 static bool DecodePointersAndHandles(const ArrayHeader* header, |
191 ElementType* elements, | 140 ElementType* elements, |
192 Message* message) { | 141 Message* message) { |
193 return ArraySerializationHelper<Handle, true>::DecodePointersAndHandles( | 142 return ArraySerializationHelper<Handle, true>::DecodePointersAndHandles( |
194 header, elements, message); | 143 header, elements, message); |
195 } | 144 } |
196 }; | 145 }; |
197 | 146 |
198 template <typename P> | 147 template <typename P> |
199 struct ArraySerializationHelper<P*, false> { | 148 struct ArraySerializationHelper<P*, false> { |
200 typedef typename ArrayDataTraits<P*>::StorageType ElementType; | 149 typedef typename ArrayDataTraits<P*>::StorageType ElementType; |
201 | 150 |
202 static size_t ComputeSizeOfElements(const ArrayHeader* header, | |
203 const ElementType* elements) { | |
204 size_t result = 0; | |
205 for (uint32_t i = 0; i < header->num_elements; ++i) { | |
206 if (elements[i].ptr) | |
207 result += elements[i].ptr->ComputeSize(); | |
208 } | |
209 return result; | |
210 } | |
211 | |
212 static void CloneElements(const ArrayHeader* header, | |
213 ElementType* elements, | |
214 Buffer* buf) { | |
215 for (uint32_t i = 0; i < header->num_elements; ++i) { | |
216 if (elements[i].ptr) | |
217 elements[i].ptr = elements[i].ptr->Clone(buf); | |
218 } | |
219 } | |
220 | |
221 static void ClearHandles(const ArrayHeader* header, ElementType* elements) { | |
222 } | |
223 | |
224 static void CloseHandles(const ArrayHeader* header, | |
225 ElementType* elements) { | |
226 for (uint32_t i = 0; i < header->num_elements; ++i) { | |
227 if (elements[i].ptr) | |
228 elements[i].ptr->CloseHandles(); | |
229 } | |
230 } | |
231 | |
232 static void EncodePointersAndHandles(const ArrayHeader* header, | 151 static void EncodePointersAndHandles(const ArrayHeader* header, |
233 ElementType* elements, | 152 ElementType* elements, |
234 std::vector<Handle>* handles) { | 153 std::vector<Handle>* handles) { |
235 for (uint32_t i = 0; i < header->num_elements; ++i) | 154 for (uint32_t i = 0; i < header->num_elements; ++i) |
236 Encode(&elements[i], handles); | 155 Encode(&elements[i], handles); |
237 } | 156 } |
238 | 157 |
239 static bool DecodePointersAndHandles(const ArrayHeader* header, | 158 static bool DecodePointersAndHandles(const ArrayHeader* header, |
240 ElementType* elements, | 159 ElementType* elements, |
241 Message* message) { | 160 Message* message) { |
242 for (uint32_t i = 0; i < header->num_elements; ++i) { | 161 for (uint32_t i = 0; i < header->num_elements; ++i) { |
243 if (!Decode(&elements[i], message)) | 162 if (!Decode(&elements[i], message)) |
244 return false; | 163 return false; |
245 } | 164 } |
246 return true; | 165 return true; |
247 } | 166 } |
248 }; | 167 }; |
249 | 168 |
250 template <typename T> | 169 template <typename T> |
251 class Array_Data { | 170 class Array_Data { |
252 public: | 171 public: |
253 typedef ArrayDataTraits<T> Traits; | 172 typedef ArrayDataTraits<T> Traits; |
254 typedef typename Traits::StorageType StorageType; | 173 typedef typename Traits::StorageType StorageType; |
255 typedef typename Traits::Wrapper Wrapper; | |
256 typedef typename Traits::Ref Ref; | 174 typedef typename Traits::Ref Ref; |
257 typedef typename Traits::ConstRef ConstRef; | 175 typedef typename Traits::ConstRef ConstRef; |
258 typedef ArraySerializationHelper<T, TypeTraits<T>::kIsHandle> Helper; | 176 typedef ArraySerializationHelper<T, IsHandle<T>::value> Helper; |
259 | 177 |
260 static Array_Data<T>* New(size_t num_elements, Buffer* buf, | 178 static Array_Data<T>* New(size_t num_elements, Buffer* buf) { |
261 Buffer::Destructor dtor = NULL) { | |
262 size_t num_bytes = sizeof(Array_Data<T>) + | 179 size_t num_bytes = sizeof(Array_Data<T>) + |
263 Traits::GetStorageSize(num_elements); | 180 Traits::GetStorageSize(num_elements); |
264 return new (buf->Allocate(num_bytes, dtor)) Array_Data<T>(num_bytes, | 181 return new (buf->Allocate(num_bytes)) Array_Data<T>(num_bytes, |
265 num_elements); | 182 num_elements); |
266 } | |
267 | |
268 static void Destructor(void* address) { | |
269 static_cast<Array_Data*>(address)->CloseHandles(); | |
270 } | 183 } |
271 | 184 |
272 size_t size() const { return header_.num_elements; } | 185 size_t size() const { return header_.num_elements; } |
273 | 186 |
274 Ref at(size_t offset) { | 187 Ref at(size_t offset) { |
275 assert(offset < static_cast<size_t>(header_.num_elements)); | 188 assert(offset < static_cast<size_t>(header_.num_elements)); |
276 return Traits::ToRef(storage(), offset); | 189 return Traits::ToRef(storage(), offset); |
277 } | 190 } |
278 | 191 |
279 ConstRef at(size_t offset) const { | 192 ConstRef at(size_t offset) const { |
280 assert(offset < static_cast<size_t>(header_.num_elements)); | 193 assert(offset < static_cast<size_t>(header_.num_elements)); |
281 return Traits::ToConstRef(storage(), offset); | 194 return Traits::ToConstRef(storage(), offset); |
282 } | 195 } |
283 | 196 |
284 StorageType* storage() { | 197 StorageType* storage() { |
285 return reinterpret_cast<StorageType*>( | 198 return reinterpret_cast<StorageType*>( |
286 reinterpret_cast<char*>(this) + sizeof(*this)); | 199 reinterpret_cast<char*>(this) + sizeof(*this)); |
287 } | 200 } |
288 | 201 |
289 const StorageType* storage() const { | 202 const StorageType* storage() const { |
290 return reinterpret_cast<const StorageType*>( | 203 return reinterpret_cast<const StorageType*>( |
291 reinterpret_cast<const char*>(this) + sizeof(*this)); | 204 reinterpret_cast<const char*>(this) + sizeof(*this)); |
292 } | 205 } |
293 | 206 |
294 size_t ComputeSize() const { | |
295 return Align(header_.num_bytes) + | |
296 Helper::ComputeSizeOfElements(&header_, storage()); | |
297 } | |
298 | |
299 Array_Data<T>* Clone(Buffer* buf) { | |
300 Array_Data<T>* clone = New(header_.num_elements, buf); | |
301 memcpy(clone->storage(), | |
302 storage(), | |
303 header_.num_bytes - sizeof(Array_Data<T>)); | |
304 Helper::CloneElements(&clone->header_, clone->storage(), buf); | |
305 | |
306 // Zero-out handles in the original storage as they have been transferred | |
307 // to the clone. | |
308 Helper::ClearHandles(&header_, storage()); | |
309 return clone; | |
310 } | |
311 | |
312 void CloseHandles() { | |
313 Helper::CloseHandles(&header_, storage()); | |
314 } | |
315 | |
316 void EncodePointersAndHandles(std::vector<Handle>* handles) { | 207 void EncodePointersAndHandles(std::vector<Handle>* handles) { |
317 Helper::EncodePointersAndHandles(&header_, storage(), handles); | 208 Helper::EncodePointersAndHandles(&header_, storage(), handles); |
318 } | 209 } |
319 | 210 |
320 bool DecodePointersAndHandles(Message* message) { | 211 bool DecodePointersAndHandles(Message* message) { |
321 return Helper::DecodePointersAndHandles(&header_, storage(), message); | 212 return Helper::DecodePointersAndHandles(&header_, storage(), message); |
322 } | 213 } |
323 | 214 |
324 private: | 215 private: |
325 Array_Data(size_t num_bytes, size_t num_elements) { | 216 Array_Data(size_t num_bytes, size_t num_elements) { |
326 header_.num_bytes = static_cast<uint32_t>(num_bytes); | 217 header_.num_bytes = static_cast<uint32_t>(num_bytes); |
327 header_.num_elements = static_cast<uint32_t>(num_elements); | 218 header_.num_elements = static_cast<uint32_t>(num_elements); |
328 } | 219 } |
329 ~Array_Data() {} | 220 ~Array_Data() {} |
330 | 221 |
331 internal::ArrayHeader header_; | 222 internal::ArrayHeader header_; |
332 | 223 |
333 // Elements of type internal::ArrayDataTraits<T>::StorageType follow. | 224 // Elements of type internal::ArrayDataTraits<T>::StorageType follow. |
334 }; | 225 }; |
335 MOJO_COMPILE_ASSERT(sizeof(Array_Data<char>) == 8, bad_sizeof_Array_Data); | 226 MOJO_COMPILE_ASSERT(sizeof(Array_Data<char>) == 8, bad_sizeof_Array_Data); |
336 | 227 |
337 // UTF-8 encoded | 228 // UTF-8 encoded |
338 typedef Array_Data<char> String_Data; | 229 typedef Array_Data<char> String_Data; |
339 | 230 |
340 template <typename T, bool kIsObject, bool kIsHandle> struct ArrayTraits {}; | 231 template <typename T, bool kIsMoveOnlyType> struct ArrayTraits {}; |
341 | 232 |
342 // When T is an object type: | 233 template <typename T> struct ArrayTraits<T, false> { |
343 template <typename T> struct ArrayTraits<T, true, false> { | 234 typedef T StorageType; |
344 typedef Array_Data<typename T::Data*> DataType; | 235 typedef typename std::vector<T>::reference RefType; |
345 typedef const T& ConstRef; | 236 typedef typename std::vector<T>::const_reference ConstRefType; |
346 typedef T& Ref; | 237 static inline void Initialize(std::vector<T>* vec) { |
347 static Buffer::Destructor GetDestructor() { | |
348 return NULL; | |
349 } | 238 } |
350 static typename T::Data* ToArrayElement(const T& value) { | 239 static inline void Finalize(std::vector<T>* vec) { |
351 return Unwrap(value); | |
352 } | 240 } |
353 // Something sketchy is indeed happening here... | 241 static inline ConstRefType at(const std::vector<T>* vec, size_t offset) { |
354 static Ref ToRef(typename T::Data*& data) { | 242 return vec->at(offset); |
355 return *reinterpret_cast<T*>(&data); | |
356 } | 243 } |
357 static ConstRef ToConstRef(typename T::Data* const& data) { | 244 static inline RefType at(std::vector<T>* vec, size_t offset) { |
358 return *reinterpret_cast<const T*>(&data); | 245 return vec->at(offset); |
359 } | 246 } |
360 }; | 247 }; |
361 | 248 |
362 // When T is a primitive (non-bool) type: | 249 template <typename T> struct ArrayTraits<T, true> { |
363 template <typename T> struct ArrayTraits<T, false, false> { | 250 struct StorageType { |
364 typedef Array_Data<T> DataType; | 251 char buf[sizeof(T) + (8 - (sizeof(T) % 8)) % 8]; // Make 8-byte aligned. |
365 typedef const T& ConstRef; | 252 }; |
366 typedef T& Ref; | 253 typedef T& RefType; |
367 static Buffer::Destructor GetDestructor() { | 254 typedef const T& ConstRefType; |
368 return NULL; | 255 static inline void Initialize(std::vector<StorageType>* vec) { |
| 256 for (size_t i = 0; i < vec->size(); ++i) |
| 257 new (vec->at(i).buf) T(); |
369 } | 258 } |
370 static T ToArrayElement(const T& value) { | 259 static inline void Finalize(std::vector<StorageType>* vec) { |
371 return value; | 260 for (size_t i = 0; i < vec->size(); ++i) |
| 261 reinterpret_cast<T*>(vec->at(i).buf)->~T(); |
372 } | 262 } |
373 static Ref ToRef(T& data) { return data; } | 263 static inline ConstRefType at(const std::vector<StorageType>* vec, |
374 static ConstRef ToConstRef(const T& data) { return data; } | 264 size_t offset) { |
375 }; | 265 return *reinterpret_cast<const T*>(vec->at(offset).buf); |
376 | |
377 // When T is a bool type: | |
378 template <> struct ArrayTraits<bool, false, false> { | |
379 typedef Array_Data<bool> DataType; | |
380 typedef bool ConstRef; | |
381 typedef ArrayDataTraits<bool>::Ref Ref; | |
382 static Buffer::Destructor GetDestructor() { | |
383 return NULL; | |
384 } | 266 } |
385 static bool ToArrayElement(const bool& value) { | 267 static inline RefType at(std::vector<StorageType>* vec, size_t offset) { |
386 return value; | 268 return *reinterpret_cast<T*>(vec->at(offset).buf); |
387 } | |
388 static Ref ToRef(const Ref& data) { return data; } | |
389 static ConstRef ToConstRef(ConstRef data) { return data; } | |
390 }; | |
391 | |
392 // When T is a handle type: | |
393 template <typename H> struct ArrayTraits<H, false, true> { | |
394 typedef Array_Data<H> DataType; | |
395 typedef Passable<H> ConstRef; | |
396 typedef AssignableAndPassable<H> Ref; | |
397 static Buffer::Destructor GetDestructor() { | |
398 return &DataType::Destructor; | |
399 } | |
400 static H ToArrayElement(const H& value) { | |
401 return value; | |
402 } | |
403 static Ref ToRef(H& data) { return Ref(&data); } | |
404 static ConstRef ToConstRef(const H& data) { | |
405 return ConstRef(const_cast<H*>(&data)); | |
406 } | 269 } |
407 }; | 270 }; |
408 | 271 |
| 272 template <> struct WrapperTraits<String, false> { |
| 273 typedef String_Data* DataType; |
| 274 }; |
| 275 |
409 } // namespace internal | 276 } // namespace internal |
410 } // namespace mojo | 277 } // namespace mojo |
411 | 278 |
412 #endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_INTERNAL_H_ | 279 #endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_INTERNAL_H_ |
OLD | NEW |