schema.h 22.4 KB
Newer Older
Kenton Varda's avatar
Kenton Varda committed
1 2
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
3
//
Kenton Varda's avatar
Kenton Varda committed
4 5 6 7 8 9
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
10
//
Kenton Varda's avatar
Kenton Varda committed
11 12
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
13
//
Kenton Varda's avatar
Kenton Varda committed
14 15 16 17 18 19 20
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
21

Kenton Varda's avatar
Kenton Varda committed
22 23
#ifndef CAPNP_SCHEMA_H_
#define CAPNP_SCHEMA_H_
24

Kenton Varda's avatar
Kenton Varda committed
25
#include <capnp/schema.capnp.h>
26

27
namespace capnp {
28 29 30 31 32

class Schema;
class StructSchema;
class EnumSchema;
class InterfaceSchema;
33
class ConstSchema;
34 35 36
class ListSchema;

template <typename T, Kind k = kind<T>()> struct SchemaType_ { typedef Schema Type; };
Kenton Varda's avatar
Kenton Varda committed
37 38
template <typename T> struct SchemaType_<T, Kind::PRIMITIVE> { typedef schema::Type::Which Type; };
template <typename T> struct SchemaType_<T, Kind::BLOB> { typedef schema::Type::Which Type; };
39 40 41 42 43 44 45 46 47
template <typename T> struct SchemaType_<T, Kind::ENUM> { typedef EnumSchema Type; };
template <typename T> struct SchemaType_<T, Kind::STRUCT> { typedef StructSchema Type; };
template <typename T> struct SchemaType_<T, Kind::INTERFACE> { typedef InterfaceSchema Type; };
template <typename T> struct SchemaType_<T, Kind::LIST> { typedef ListSchema Type; };

template <typename T>
using SchemaType = typename SchemaType_<T>::Type;
// SchemaType<T> is the type of T's schema, e.g. StructSchema if T is a struct.

48 49 50 51 52 53 54 55 56 57
namespace _ {  // private
extern const RawSchema NULL_SCHEMA;
extern const RawSchema NULL_STRUCT_SCHEMA;
extern const RawSchema NULL_ENUM_SCHEMA;
extern const RawSchema NULL_INTERFACE_SCHEMA;
extern const RawSchema NULL_CONST_SCHEMA;
// The schema types default to these null (empty) schemas in case of error, especially when
// exceptions are disabled.
}  // namespace _ (private)

58
class Schema {
59
  // Convenience wrapper around capnp::schema::Node.
60 61

public:
62
  inline Schema(): raw(&_::NULL_SCHEMA) {}
63 64 65 66 67

  template <typename T>
  static inline SchemaType<T> from() { return SchemaType<T>::template fromImpl<T>(); }
  // Get the Schema for a particular compiled-in type.

Kenton Varda's avatar
Kenton Varda committed
68
  schema::Node::Reader getProto() const;
69 70
  // Get the underlying Cap'n Proto representation of the schema node.  (Note that this accessor
  // has performance comparable to accessors of struct-typed fields on Reader classes.)
71

Kenton Varda's avatar
Kenton Varda committed
72 73 74 75
  kj::ArrayPtr<const word> asUncheckedMessage() const;
  // Get the encoded schema node content as a single message segment.  It is safe to read as an
  // unchecked message.

76 77 78
  Schema getDependency(uint64_t id) const;
  // Gets the Schema for one of this Schema's dependencies.  For example, if this Schema is for a
  // struct, you could look up the schema for one of its fields' types.  Throws an exception if this
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
  // schema doesn't actually depend on the given id.
  //
  // Note that not all type IDs found in the schema node are considered "dependencies" -- only the
  // ones that are needed to implement the dynamic API are.  That includes:
  // - Field types.
  // - Group types.
  // - scopeId for group nodes, but NOT otherwise.
  // - Method parameter and return types.
  //
  // The following are NOT considered dependencies:
  // - Nested nodes.
  // - scopeId for a non-group node.
  // - Annotations.
  //
  // To obtain schemas for those, you would need a SchemaLoader.
94 95 96 97

  StructSchema asStruct() const;
  EnumSchema asEnum() const;
  InterfaceSchema asInterface() const;
98 99 100
  ConstSchema asConst() const;
  // Cast the Schema to a specific type.  Throws an exception if the type doesn't match.  Use
  // getProto() to determine type, e.g. getProto().isStruct().
101 102 103 104 105 106 107

  inline bool operator==(const Schema& other) const { return raw == other.raw; }
  inline bool operator!=(const Schema& other) const { return raw != other.raw; }
  // Determine whether two Schemas are wrapping the exact same underlying data, by identity.  If
  // you want to check if two Schemas represent the same type (but possibly different versions of
  // it), compare their IDs instead.

108
  template <typename T>
109
  void requireUsableAs() const;
110 111 112 113 114 115
  // Throws an exception if a value with this Schema cannot safely be cast to a native value of
  // the given type.  This passes if either:
  // - *this == from<T>()
  // - This schema was loaded with SchemaLoader, the type ID matches typeId<T>(), and
  //   loadCompiledTypeAndDependencies<T>() was called on the SchemaLoader.

116 117 118
  kj::StringPtr getShortDisplayName() const;
  // Get the short version of the node's display name.

119
private:
120
  const _::RawSchema* raw;
121

122 123 124 125
  inline explicit Schema(const _::RawSchema* raw): raw(raw) {
    KJ_IREQUIRE(raw->lazyInitializer == nullptr,
        "Must call ensureInitialized() on RawSchema before constructing Schema.");
  }
126 127

  template <typename T> static inline Schema fromImpl() {
128
    return Schema(&_::rawSchema<T>());
129 130
  }

131
  void requireUsableAs(const _::RawSchema* expected) const;
132

133 134
  uint32_t getSchemaOffset(const schema::Value::Reader& value) const;

135 136 137
  friend class StructSchema;
  friend class EnumSchema;
  friend class InterfaceSchema;
138
  friend class ConstSchema;
139 140
  friend class ListSchema;
  friend class SchemaLoader;
141 142 143 144 145 146
};

// -------------------------------------------------------------------

class StructSchema: public Schema {
public:
147
  inline StructSchema(): Schema(&_::NULL_STRUCT_SCHEMA) {}
148

Kenton Varda's avatar
Kenton Varda committed
149
  class Field;
Kenton Varda's avatar
Kenton Varda committed
150 151
  class FieldList;
  class FieldSubset;
152

Kenton Varda's avatar
Kenton Varda committed
153 154 155 156
  FieldList getFields() const;
  // List top-level fields of this struct.  This list will contain top-level groups (including
  // named unions) but not the members of those groups.  The list does, however, contain the
  // members of the unnamed union, if there is one.
Kenton Varda's avatar
Kenton Varda committed
157

Kenton Varda's avatar
Kenton Varda committed
158 159 160 161
  FieldSubset getUnionFields() const;
  // If the field contains an unnamed union, get a list of fields in the union, ordered by
  // ordinal.  Since discriminant values are assigned sequentially by ordinal, you may index this
  // list by discriminant value.
162

Kenton Varda's avatar
Kenton Varda committed
163 164
  FieldSubset getNonUnionFields() const;
  // Get the fields of this struct which are not in an unnamed union, ordered by ordinal.
Kenton Varda's avatar
Kenton Varda committed
165

Kenton Varda's avatar
Kenton Varda committed
166 167 168 169 170 171 172 173 174 175 176 177 178 179
  kj::Maybe<Field> findFieldByName(kj::StringPtr name) const;
  // Find the field with the given name, or return null if there is no such field.  If the struct
  // contains an unnamed union, then this will find fields of that union in addition to fields
  // of the outer struct, since they exist in the same namespace.  It will not, however, find
  // members of groups (including named unions) -- you must first look up the group itself,
  // then dig into its type.

  Field getFieldByName(kj::StringPtr name) const;
  // Like findFieldByName() but throws an exception on failure.

  kj::Maybe<Field> getFieldByDiscriminant(uint16_t discriminant) const;
  // Finds the field whose `discriminantValue` is equal to the given value, or returns null if
  // there is no such field.  (If the schema does not represent a union or a struct containing
  // an unnamed union, then this always returns null.)
180

181
private:
182
  StructSchema(const _::RawSchema* raw): Schema(raw) {}
183
  template <typename T> static inline StructSchema fromImpl() {
184
    return StructSchema(&_::rawSchema<T>());
185 186
  }
  friend class Schema;
187
  friend kj::StringTree _::structString(
188
      _::StructReader reader, const _::RawSchema& schema);
189 190
};

Kenton Varda's avatar
Kenton Varda committed
191
class StructSchema::Field {
192
public:
Kenton Varda's avatar
Kenton Varda committed
193
  Field() = default;
194

Kenton Varda's avatar
Kenton Varda committed
195
  inline schema::Field::Reader getProto() const { return proto; }
196 197 198
  inline StructSchema getContainingStruct() const { return parent; }

  inline uint getIndex() const { return index; }
Kenton Varda's avatar
Kenton Varda committed
199
  // Get the index of this field within the containing struct or union.
Kenton Varda's avatar
Kenton Varda committed
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220

  uint32_t getDefaultValueSchemaOffset() const;
  // For struct, list, and object fields, returns the offset, in words, within the first segment of
  // the struct's schema, where this field's default value pointer is located.  The schema is
  // always stored as a single-segment unchecked message, which in turn means that the default
  // value pointer itself can be treated as the root of an unchecked message -- if you know where
  // to find it, which is what this method helps you with.
  //
  // For blobs, returns the offset of the begging of the blob's content within the first segment of
  // the struct's schema.
  //
  // This is primarily useful for code generators.  The C++ code generator, for example, embeds
  // the entire schema as a raw word array within the generated code.  Of course, to implement
  // field accessors, it needs access to those fields' default values.  Embedding separate copies
  // of those default values would be redundant since they are already included in the schema, but
  // seeking through the schema at runtime to find the default values would be ugly.  Instead,
  // the code generator can use getDefaultValueSchemaOffset() to find the offset of the default
  // value within the schema, and can simply apply that offset at runtime.
  //
  // If the above does not make sense, you probably don't need this method.

Kenton Varda's avatar
Kenton Varda committed
221 222
  inline bool operator==(const Field& other) const;
  inline bool operator!=(const Field& other) const { return !(*this == other); }
Kenton Varda's avatar
Kenton Varda committed
223

224
private:
Kenton Varda's avatar
Kenton Varda committed
225 226
  StructSchema parent;
  uint index;
Kenton Varda's avatar
Kenton Varda committed
227
  schema::Field::Reader proto;
Kenton Varda's avatar
Kenton Varda committed
228

Kenton Varda's avatar
Kenton Varda committed
229
  inline Field(StructSchema parent, uint index, schema::Field::Reader proto)
Kenton Varda's avatar
Kenton Varda committed
230
      : parent(parent), index(index), proto(proto) {}
231 232 233 234

  friend class StructSchema;
};

Kenton Varda's avatar
Kenton Varda committed
235
class StructSchema::FieldList {
Kenton Varda's avatar
Kenton Varda committed
236
public:
Kenton Varda's avatar
Kenton Varda committed
237
  FieldList() = default;  // empty list
Kenton Varda's avatar
Kenton Varda committed
238

Kenton Varda's avatar
Kenton Varda committed
239 240
  inline uint size() const { return list.size(); }
  inline Field operator[](uint index) const { return Field(parent, index, list[index]); }
Kenton Varda's avatar
Kenton Varda committed
241

Kenton Varda's avatar
Kenton Varda committed
242 243 244
  typedef _::IndexingIterator<const FieldList, Field> Iterator;
  inline Iterator begin() const { return Iterator(this, 0); }
  inline Iterator end() const { return Iterator(this, size()); }
Kenton Varda's avatar
Kenton Varda committed
245 246

private:
Kenton Varda's avatar
Kenton Varda committed
247
  StructSchema parent;
Kenton Varda's avatar
Kenton Varda committed
248
  List<schema::Field>::Reader list;
Kenton Varda's avatar
Kenton Varda committed
249

Kenton Varda's avatar
Kenton Varda committed
250
  inline FieldList(StructSchema parent, List<schema::Field>::Reader list)
Kenton Varda's avatar
Kenton Varda committed
251
      : parent(parent), list(list) {}
Kenton Varda's avatar
Kenton Varda committed
252 253 254 255

  friend class StructSchema;
};

Kenton Varda's avatar
Kenton Varda committed
256
class StructSchema::FieldSubset {
257
public:
Kenton Varda's avatar
Kenton Varda committed
258
  FieldSubset() = default;  // empty list
259

Kenton Varda's avatar
Kenton Varda committed
260 261 262 263
  inline uint size() const { return size_; }
  inline Field operator[](uint index) const {
    return Field(parent, indices[index], list[indices[index]]);
  }
264

Kenton Varda's avatar
Kenton Varda committed
265
  typedef _::IndexingIterator<const FieldSubset, Field> Iterator;
266 267
  inline Iterator begin() const { return Iterator(this, 0); }
  inline Iterator end() const { return Iterator(this, size()); }
268 269 270

private:
  StructSchema parent;
Kenton Varda's avatar
Kenton Varda committed
271
  List<schema::Field>::Reader list;
Kenton Varda's avatar
Kenton Varda committed
272 273
  const uint16_t* indices;
  uint size_;
274

Kenton Varda's avatar
Kenton Varda committed
275
  inline FieldSubset(StructSchema parent, List<schema::Field>::Reader list,
Kenton Varda's avatar
Kenton Varda committed
276 277
                     const uint16_t* indices, uint size)
      : parent(parent), list(list), indices(indices), size_(size) {}
278 279 280 281 282 283 284 285

  friend class StructSchema;
};

// -------------------------------------------------------------------

class EnumSchema: public Schema {
public:
286
  inline EnumSchema(): Schema(&_::NULL_ENUM_SCHEMA) {}
287

288 289 290
  class Enumerant;
  class EnumerantList;

291
  EnumerantList getEnumerants() const;
Kenton Varda's avatar
Kenton Varda committed
292

293
  kj::Maybe<Enumerant> findEnumerantByName(kj::StringPtr name) const;
294

295
  Enumerant getEnumerantByName(kj::StringPtr name) const;
Kenton Varda's avatar
Kenton Varda committed
296 297
  // Like findEnumerantByName() but throws an exception on failure.

298
private:
299
  EnumSchema(const _::RawSchema* raw): Schema(raw) {}
300
  template <typename T> static inline EnumSchema fromImpl() {
301
    return EnumSchema(&_::rawSchema<T>());
302 303 304 305 306 307 308 309
  }
  friend class Schema;
};

class EnumSchema::Enumerant {
public:
  Enumerant() = default;

Kenton Varda's avatar
Kenton Varda committed
310
  inline schema::Enumerant::Reader getProto() const { return proto; }
Kenton Varda's avatar
Kenton Varda committed
311
  inline EnumSchema getContainingEnum() const { return parent; }
312

Kenton Varda's avatar
Kenton Varda committed
313 314
  inline uint16_t getOrdinal() const { return ordinal; }
  inline uint getIndex() const { return ordinal; }
315 316 317 318 319 320 321

  inline bool operator==(const Enumerant& other) const;
  inline bool operator!=(const Enumerant& other) const { return !(*this == other); }

private:
  EnumSchema parent;
  uint16_t ordinal;
Kenton Varda's avatar
Kenton Varda committed
322
  schema::Enumerant::Reader proto;
323

Kenton Varda's avatar
Kenton Varda committed
324
  inline Enumerant(EnumSchema parent, uint16_t ordinal, schema::Enumerant::Reader proto)
325 326 327 328 329 330 331
      : parent(parent), ordinal(ordinal), proto(proto) {}

  friend class EnumSchema;
};

class EnumSchema::EnumerantList {
public:
332 333
  EnumerantList() = default;  // empty list

334 335 336
  inline uint size() const { return list.size(); }
  inline Enumerant operator[](uint index) const { return Enumerant(parent, index, list[index]); }

337
  typedef _::IndexingIterator<const EnumerantList, Enumerant> Iterator;
338 339
  inline Iterator begin() const { return Iterator(this, 0); }
  inline Iterator end() const { return Iterator(this, size()); }
340 341 342

private:
  EnumSchema parent;
Kenton Varda's avatar
Kenton Varda committed
343
  List<schema::Enumerant>::Reader list;
344

Kenton Varda's avatar
Kenton Varda committed
345
  inline EnumerantList(EnumSchema parent, List<schema::Enumerant>::Reader list)
346 347 348 349 350 351 352 353 354
      : parent(parent), list(list) {}

  friend class EnumSchema;
};

// -------------------------------------------------------------------

class InterfaceSchema: public Schema {
public:
355
  inline InterfaceSchema(): Schema(&_::NULL_INTERFACE_SCHEMA) {}
356

357 358 359
  class Method;
  class MethodList;

360
  MethodList getMethods() const;
Kenton Varda's avatar
Kenton Varda committed
361

362
  kj::Maybe<Method> findMethodByName(kj::StringPtr name) const;
363

364
  Method getMethodByName(kj::StringPtr name) const;
Kenton Varda's avatar
Kenton Varda committed
365 366
  // Like findMethodByName() but throws an exception on failure.

367 368 369 370 371 372 373
  bool extends(InterfaceSchema other) const;
  // Returns true if `other` is a superclass of this interface (including if `other == *this`).

  kj::Maybe<InterfaceSchema> findSuperclass(uint64_t typeId) const;
  // Find the superclass of this interface with the given type ID.  Returns null if the interface
  // extends no such type.

374
private:
375
  InterfaceSchema(const _::RawSchema* raw): Schema(raw) {}
376
  template <typename T> static inline InterfaceSchema fromImpl() {
377
    return InterfaceSchema(&_::rawSchema<T>());
378 379
  }
  friend class Schema;
380 381 382 383 384 385

  kj::Maybe<Method> findMethodByName(kj::StringPtr name, uint& counter) const;
  bool extends(InterfaceSchema other, uint& counter) const;
  kj::Maybe<InterfaceSchema> findSuperclass(uint64_t typeId, uint& counter) const;
  // We protect against malicious schemas with large or cyclic hierarchies by cutting off the
  // search when the counter reaches a threshold.
386 387 388 389 390 391
};

class InterfaceSchema::Method {
public:
  Method() = default;

Kenton Varda's avatar
Kenton Varda committed
392
  inline schema::Method::Reader getProto() const { return proto; }
Kenton Varda's avatar
Kenton Varda committed
393
  inline InterfaceSchema getContainingInterface() const { return parent; }
394

Kenton Varda's avatar
Kenton Varda committed
395 396
  inline uint16_t getOrdinal() const { return ordinal; }
  inline uint getIndex() const { return ordinal; }
397 398 399 400 401 402 403

  inline bool operator==(const Method& other) const;
  inline bool operator!=(const Method& other) const { return !(*this == other); }

private:
  InterfaceSchema parent;
  uint16_t ordinal;
Kenton Varda's avatar
Kenton Varda committed
404
  schema::Method::Reader proto;
405 406

  inline Method(InterfaceSchema parent, uint16_t ordinal,
Kenton Varda's avatar
Kenton Varda committed
407
                schema::Method::Reader proto)
408 409 410 411 412 413 414
      : parent(parent), ordinal(ordinal), proto(proto) {}

  friend class InterfaceSchema;
};

class InterfaceSchema::MethodList {
public:
415 416
  MethodList() = default;  // empty list

417 418 419
  inline uint size() const { return list.size(); }
  inline Method operator[](uint index) const { return Method(parent, index, list[index]); }

420
  typedef _::IndexingIterator<const MethodList, Method> Iterator;
421 422
  inline Iterator begin() const { return Iterator(this, 0); }
  inline Iterator end() const { return Iterator(this, size()); }
423 424 425

private:
  InterfaceSchema parent;
Kenton Varda's avatar
Kenton Varda committed
426
  List<schema::Method>::Reader list;
427

Kenton Varda's avatar
Kenton Varda committed
428
  inline MethodList(InterfaceSchema parent, List<schema::Method>::Reader list)
429 430 431 432 433 434 435
      : parent(parent), list(list) {}

  friend class InterfaceSchema;
};

// -------------------------------------------------------------------

436 437 438 439 440 441
class ConstSchema: public Schema {
  // Represents a constant declaration.
  //
  // `ConstSchema` can be implicitly cast to DynamicValue to read its value.

public:
442
  inline ConstSchema(): Schema(&_::NULL_CONST_SCHEMA) {}
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461

  template <typename T>
  ReaderFor<T> as() const;
  // Read the constant's value.  This is a convenience method equivalent to casting the ConstSchema
  // to a DynamicValue and then calling its `as<T>()` method.  For dependency reasons, this method
  // is defined in <capnp/dynamic.h>, which you must #include explicitly.

  uint32_t getValueSchemaOffset() const;
  // Much like StructSchema::Field::getDefaultValueSchemaOffset(), if the constant has pointer
  // type, this gets the offset from the beginning of the constant's schema node to a pointer
  // representing the constant value.

private:
  ConstSchema(const _::RawSchema* raw): Schema(raw) {}
  friend class Schema;
};

// -------------------------------------------------------------------

462 463 464 465 466 467 468
class ListSchema {
  // ListSchema is a little different because list types are not described by schema nodes.  So,
  // ListSchema doesn't subclass Schema.

public:
  ListSchema() = default;

Kenton Varda's avatar
Kenton Varda committed
469
  static ListSchema of(schema::Type::Which primitiveType);
470 471 472 473 474 475
  static ListSchema of(StructSchema elementType);
  static ListSchema of(EnumSchema elementType);
  static ListSchema of(InterfaceSchema elementType);
  static ListSchema of(ListSchema elementType);
  // Construct the schema for a list of the given type.

Kenton Varda's avatar
Kenton Varda committed
476
  static ListSchema of(schema::Type::Reader elementType, Schema context);
477 478 479
  // Construct from an element type schema.  Requires a context which can handle getDependency()
  // requests for any type ID found in the schema.

Kenton Varda's avatar
Kenton Varda committed
480
  inline schema::Type::Which whichElementType() const;
481 482 483 484 485 486 487 488 489 490 491
  // Get the element type's "which()".  ListSchema does not actually store a schema::Type::Reader
  // describing the element type, but if it did, this would be equivalent to calling
  // .getBody().which() on that type.

  StructSchema getStructElementType() const;
  EnumSchema getEnumElementType() const;
  InterfaceSchema getInterfaceElementType() const;
  ListSchema getListElementType() const;
  // Get the schema for complex element types.  Each of these throws an exception if the element
  // type is not of the requested kind.

492 493 494
  inline bool operator==(const ListSchema& other) const;
  inline bool operator!=(const ListSchema& other) const { return !(*this == other); }

495
  template <typename T>
496
  void requireUsableAs() const;
497

498
private:
Kenton Varda's avatar
Kenton Varda committed
499
  schema::Type::Which elementType;
500 501 502
  uint8_t nestingDepth;  // 0 for T, 1 for List(T), 2 for List(List(T)), ...
  Schema elementSchema;  // if elementType is struct, enum, interface...

Kenton Varda's avatar
Kenton Varda committed
503
  inline ListSchema(schema::Type::Which elementType)
504
      : elementType(elementType), nestingDepth(0) {}
Kenton Varda's avatar
Kenton Varda committed
505
  inline ListSchema(schema::Type::Which elementType, Schema elementSchema)
506
      : elementType(elementType), nestingDepth(0), elementSchema(elementSchema) {}
Kenton Varda's avatar
Kenton Varda committed
507
  inline ListSchema(schema::Type::Which elementType, uint8_t nestingDepth,
508 509 510 511 512 513 514 515 516
                    Schema elementSchema)
      : elementType(elementType), nestingDepth(nestingDepth), elementSchema(elementSchema) {}

  template <typename T>
  struct FromImpl;
  template <typename T> static inline ListSchema fromImpl() {
    return FromImpl<T>::get();
  }

517
  void requireUsableAs(ListSchema expected) const;
518

519 520 521 522 523 524
  friend class Schema;
};

// =======================================================================================
// inline implementation

Kenton Varda's avatar
Kenton Varda committed
525 526 527 528 529 530 531 532 533 534 535 536 537 538
template <> inline schema::Type::Which Schema::from<Void>() { return schema::Type::VOID; }
template <> inline schema::Type::Which Schema::from<bool>() { return schema::Type::BOOL; }
template <> inline schema::Type::Which Schema::from<int8_t>() { return schema::Type::INT8; }
template <> inline schema::Type::Which Schema::from<int16_t>() { return schema::Type::INT16; }
template <> inline schema::Type::Which Schema::from<int32_t>() { return schema::Type::INT32; }
template <> inline schema::Type::Which Schema::from<int64_t>() { return schema::Type::INT64; }
template <> inline schema::Type::Which Schema::from<uint8_t>() { return schema::Type::UINT8; }
template <> inline schema::Type::Which Schema::from<uint16_t>() { return schema::Type::UINT16; }
template <> inline schema::Type::Which Schema::from<uint32_t>() { return schema::Type::UINT32; }
template <> inline schema::Type::Which Schema::from<uint64_t>() { return schema::Type::UINT64; }
template <> inline schema::Type::Which Schema::from<float>() { return schema::Type::FLOAT32; }
template <> inline schema::Type::Which Schema::from<double>() { return schema::Type::FLOAT64; }
template <> inline schema::Type::Which Schema::from<Text>() { return schema::Type::TEXT; }
template <> inline schema::Type::Which Schema::from<Data>() { return schema::Type::DATA; }
539

540
template <typename T>
541
inline void Schema::requireUsableAs() const {
542
  requireUsableAs(&_::rawSchema<T>());
543 544
}

Kenton Varda's avatar
Kenton Varda committed
545 546
inline bool StructSchema::Field::operator==(const Field& other) const {
  return parent == other.parent && index == other.index;
547 548 549 550 551 552 553 554 555
}
inline bool EnumSchema::Enumerant::operator==(const Enumerant& other) const {
  return parent == other.parent && ordinal == other.ordinal;
}
inline bool InterfaceSchema::Method::operator==(const Method& other) const {
  return parent == other.parent && ordinal == other.ordinal;
}

inline ListSchema ListSchema::of(StructSchema elementType) {
Kenton Varda's avatar
Kenton Varda committed
556
  return ListSchema(schema::Type::STRUCT, 0, elementType);
557 558
}
inline ListSchema ListSchema::of(EnumSchema elementType) {
Kenton Varda's avatar
Kenton Varda committed
559
  return ListSchema(schema::Type::ENUM, 0, elementType);
560 561
}
inline ListSchema ListSchema::of(InterfaceSchema elementType) {
Kenton Varda's avatar
Kenton Varda committed
562
  return ListSchema(schema::Type::INTERFACE, 0, elementType);
563 564 565 566 567 568
}
inline ListSchema ListSchema::of(ListSchema elementType) {
  return ListSchema(elementType.elementType, elementType.nestingDepth + 1,
                    elementType.elementSchema);
}

Kenton Varda's avatar
Kenton Varda committed
569 570
inline schema::Type::Which ListSchema::whichElementType() const {
  return nestingDepth == 0 ? elementType : schema::Type::LIST;
571 572
}

573 574 575 576 577
inline bool ListSchema::operator==(const ListSchema& other) const {
  return elementType == other.elementType && nestingDepth == other.nestingDepth &&
      elementSchema == other.elementSchema;
}

578
template <typename T>
579
inline void ListSchema::requireUsableAs() const {
580 581 582 583 584
  static_assert(kind<T>() == Kind::LIST,
                "ListSchema::requireUsableAs<T>() requires T is a list type.");
  requireUsableAs(Schema::from<T>());
}

585 586 587 588 589
template <typename T>
struct ListSchema::FromImpl<List<T>> {
  static inline ListSchema get() { return of(Schema::from<T>()); }
};

590
}  // namespace capnp
591

Kenton Varda's avatar
Kenton Varda committed
592
#endif  // CAPNP_SCHEMA_H_