capnproto

FORK: Cap'n Proto serialization/RPC system - core tools and C++ library
git clone https://git.neptards.moe/neptards/capnproto.git
Log | Files | Refs | README | LICENSE

schema.c++ (33122B)


      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 #include "schema.h"
     23 #include "message.h"
     24 #include <kj/debug.h>
     25 #include <capnp/stream.capnp.h>
     26 
     27 namespace capnp {
     28 
     29 namespace schema {
     30   uint KJ_HASHCODE(Type::Which w) { return kj::hashCode(static_cast<uint16_t>(w)); }
     31   // TODO(cleanup): Cap'n Proto does not declare stringifiers nor hashers for `Which` enums, unlike
     32   //   all other enums. Fix that and remove this.
     33 }
     34 
     35 namespace _ {  // private
     36 
     37 // Null schemas generated using the below schema file with:
     38 //
     39 //     capnp eval -Isrc null-schemas.capnp node --flat |
     40 //         hexdump -v -e '8/1 "0x%02x, "' -e '1/8 "\n"'; echo
     41 //
     42 // I totally don't understand hexdump format strings and came up with this command based on trial
     43 // and error.
     44 //
     45 //     @0x879863d4b2cc4a1e;
     46 //
     47 //     using Node = import "/capnp/schema.capnp".Node;
     48 //
     49 //     const node :Node = (
     50 //         id = 0x0000000000000000,
     51 //         displayName = "(null schema)");
     52 //
     53 //     const struct :Node = (
     54 //         id = 0x0000000000000001,
     55 //         displayName = "(null struct schema)",
     56 //         struct = (
     57 //             dataWordCount = 0,
     58 //             pointerCount = 0,
     59 //             preferredListEncoding = empty));
     60 //
     61 //     const enum :Node = (
     62 //         id = 0x0000000000000002,
     63 //         displayName = "(null enum schema)",
     64 //         enum = ());
     65 //
     66 //     const interface :Node = (
     67 //         id = 0x0000000000000003,
     68 //         displayName = "(null interface schema)",
     69 //         interface = ());
     70 //
     71 //     const const :Node = (
     72 //         id = 0x0000000000000004,
     73 //         displayName = "(null const schema)",
     74 //         const = (type = (void = void), value = (void = void)));
     75 
     76 static const AlignedData<13> NULL_SCHEMA_BYTES = {{
     77   0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
     78   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     79   0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00,   // union discriminant intentionally mangled
     80   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     81   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     82   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     83   0x11, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00,
     84   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     85   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     86   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     87   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     88   0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x73, 0x63,
     89   0x68, 0x65, 0x6d, 0x61, 0x29, 0x00, 0x00, 0x00,
     90 }};
     91 const RawSchema NULL_SCHEMA = {
     92   0x0000000000000000, NULL_SCHEMA_BYTES.words, 13,
     93   nullptr, nullptr, 0, 0, nullptr, nullptr, nullptr,
     94   { &NULL_SCHEMA, nullptr, nullptr, 0, 0, nullptr }
     95 };
     96 
     97 static const AlignedData<14> NULL_STRUCT_SCHEMA_BYTES = {{
     98   0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
     99   0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    100   0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
    101   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    102   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    103   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    104   0x11, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00,
    105   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    106   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    107   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    108   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    109   0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x73, 0x74,
    110   0x72, 0x75, 0x63, 0x74, 0x20, 0x73, 0x63, 0x68,
    111   0x65, 0x6d, 0x61, 0x29, 0x00, 0x00, 0x00, 0x00,
    112 }};
    113 const RawSchema NULL_STRUCT_SCHEMA = {
    114   0x0000000000000001, NULL_STRUCT_SCHEMA_BYTES.words, 14,
    115   nullptr, nullptr, 0, 0, nullptr, nullptr, nullptr,
    116   { &NULL_STRUCT_SCHEMA, nullptr, nullptr, 0, 0, nullptr }
    117 };
    118 
    119 static const AlignedData<14> NULL_ENUM_SCHEMA_BYTES = {{
    120   0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
    121   0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    122   0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
    123   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    124   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    125   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    126   0x11, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00,
    127   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    128   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    129   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    130   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    131   0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x65, 0x6e,
    132   0x75, 0x6d, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d,
    133   0x61, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    134 }};
    135 const RawSchema NULL_ENUM_SCHEMA = {
    136   0x0000000000000002, NULL_ENUM_SCHEMA_BYTES.words, 14,
    137   nullptr, nullptr, 0, 0, nullptr, nullptr, nullptr,
    138   { &NULL_ENUM_SCHEMA, nullptr, nullptr, 0, 0, nullptr }
    139 };
    140 
    141 static const AlignedData<14> NULL_INTERFACE_SCHEMA_BYTES = {{
    142   0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
    143   0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    144   0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
    145   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    146   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    147   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    148   0x11, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00,
    149   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    150   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    151   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    152   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    153   0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x69, 0x6e,
    154   0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x20,
    155   0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x29, 0x00,
    156 }};
    157 const RawSchema NULL_INTERFACE_SCHEMA = {
    158   0x0000000000000003, NULL_INTERFACE_SCHEMA_BYTES.words, 14,
    159   nullptr, nullptr, 0, 0, nullptr, nullptr, nullptr,
    160   { &NULL_INTERFACE_SCHEMA, nullptr, nullptr, 0, 0, nullptr }
    161 };
    162 
    163 static const AlignedData<20> NULL_CONST_SCHEMA_BYTES = {{
    164   0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
    165   0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    166   0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
    167   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    168   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    169   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    170   0x11, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00,
    171   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    172   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    173   0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
    174   0x18, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
    175   0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x63, 0x6f,
    176   0x6e, 0x73, 0x74, 0x20, 0x73, 0x63, 0x68, 0x65,
    177   0x6d, 0x61, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00,
    178   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    179   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    180   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    181   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    182   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    183   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    184 }};
    185 const RawSchema NULL_CONST_SCHEMA = {
    186   0x0000000000000004, NULL_CONST_SCHEMA_BYTES.words, 20,
    187   nullptr, nullptr, 0, 0, nullptr, nullptr, nullptr,
    188   { &NULL_CONST_SCHEMA, nullptr, nullptr, 0, 0, nullptr }
    189 };
    190 
    191 }  // namespace _ (private)
    192 
    193 // =======================================================================================
    194 
    195 schema::Node::Reader Schema::getProto() const {
    196   return readMessageUnchecked<schema::Node>(raw->generic->encodedNode);
    197 }
    198 
    199 kj::ArrayPtr<const word> Schema::asUncheckedMessage() const {
    200   return kj::arrayPtr(raw->generic->encodedNode, raw->generic->encodedSize);
    201 }
    202 
    203 Schema Schema::getDependency(uint64_t id, uint location) const {
    204   {
    205     // Binary search dependency list.
    206     uint lower = 0;
    207     uint upper = raw->dependencyCount;
    208 
    209     while (lower < upper) {
    210       uint mid = (lower + upper) / 2;
    211 
    212       auto candidate = raw->dependencies[mid];
    213       if (candidate.location == location) {
    214         candidate.schema->ensureInitialized();
    215         return Schema(candidate.schema);
    216       } else if (candidate.location < location) {
    217         lower = mid + 1;
    218       } else {
    219         upper = mid;
    220       }
    221     }
    222   }
    223 
    224   {
    225     uint lower = 0;
    226     uint upper = raw->generic->dependencyCount;
    227 
    228     while (lower < upper) {
    229       uint mid = (lower + upper) / 2;
    230 
    231       const _::RawSchema* candidate = raw->generic->dependencies[mid];
    232 
    233       uint64_t candidateId = candidate->id;
    234       if (candidateId == id) {
    235         candidate->ensureInitialized();
    236         return Schema(&candidate->defaultBrand);
    237       } else if (candidateId < id) {
    238         lower = mid + 1;
    239       } else {
    240         upper = mid;
    241       }
    242     }
    243   }
    244 
    245   KJ_FAIL_REQUIRE("Requested ID not found in dependency table.", kj::hex(id)) {
    246     return Schema();
    247   }
    248 }
    249 
    250 Schema::BrandArgumentList Schema::getBrandArgumentsAtScope(uint64_t scopeId) const {
    251   KJ_REQUIRE(getProto().getIsGeneric(), "Not a generic type.", getProto().getDisplayName());
    252 
    253   for (auto scope: kj::range(raw->scopes, raw->scopes + raw->scopeCount)) {
    254     if (scope->typeId == scopeId) {
    255       // OK, this scope matches the scope we're looking for.
    256       if (scope->isUnbound) {
    257         return BrandArgumentList(scopeId, true);
    258       } else {
    259         return BrandArgumentList(scopeId, scope->bindingCount, scope->bindings);
    260       }
    261     }
    262   }
    263 
    264   // This scope is not listed in the scopes list.
    265   return BrandArgumentList(scopeId, raw->isUnbound());
    266 }
    267 
    268 StructSchema Schema::asStruct() const {
    269   KJ_REQUIRE(getProto().isStruct(), "Tried to use non-struct schema as a struct.",
    270              getProto().getDisplayName()) {
    271     return StructSchema();
    272   }
    273   return StructSchema(*this);
    274 }
    275 
    276 EnumSchema Schema::asEnum() const {
    277   KJ_REQUIRE(getProto().isEnum(), "Tried to use non-enum schema as an enum.",
    278              getProto().getDisplayName()) {
    279     return EnumSchema();
    280   }
    281   return EnumSchema(*this);
    282 }
    283 
    284 InterfaceSchema Schema::asInterface() const {
    285   KJ_REQUIRE(getProto().isInterface(), "Tried to use non-interface schema as an interface.",
    286              getProto().getDisplayName()) {
    287     return InterfaceSchema();
    288   }
    289   return InterfaceSchema(*this);
    290 }
    291 
    292 ConstSchema Schema::asConst() const {
    293   KJ_REQUIRE(getProto().isConst(), "Tried to use non-constant schema as a constant.",
    294              getProto().getDisplayName()) {
    295     return ConstSchema();
    296   }
    297   return ConstSchema(*this);
    298 }
    299 
    300 kj::StringPtr Schema::getShortDisplayName() const {
    301   auto proto = getProto();
    302   return proto.getDisplayName().slice(proto.getDisplayNamePrefixLength());
    303 }
    304 
    305 const kj::StringPtr Schema::getUnqualifiedName() const {
    306   auto proto = getProto();
    307   return proto.getDisplayName().slice(proto.getDisplayNamePrefixLength());
    308 }
    309 
    310 void Schema::requireUsableAs(const _::RawSchema* expected) const {
    311   KJ_REQUIRE(raw->generic == expected ||
    312              (expected != nullptr && raw->generic->canCastTo == expected),
    313              "This schema is not compatible with the requested native type.");
    314 }
    315 
    316 uint32_t Schema::getSchemaOffset(const schema::Value::Reader& value) const {
    317   const word* ptr;
    318 
    319   switch (value.which()) {
    320     case schema::Value::TEXT:
    321       ptr = reinterpret_cast<const word*>(value.getText().begin());
    322       break;
    323     case schema::Value::DATA:
    324       ptr = reinterpret_cast<const word*>(value.getData().begin());
    325       break;
    326     case schema::Value::STRUCT:
    327       ptr = value.getStruct().getAs<_::UncheckedMessage>();
    328       break;
    329     case schema::Value::LIST:
    330       ptr = value.getList().getAs<_::UncheckedMessage>();
    331       break;
    332     case schema::Value::ANY_POINTER:
    333       ptr = value.getAnyPointer().getAs<_::UncheckedMessage>();
    334       break;
    335     default:
    336       KJ_FAIL_ASSERT("getDefaultValueSchemaOffset() can only be called on struct, list, "
    337                      "and any-pointer fields.");
    338   }
    339 
    340   return ptr - raw->generic->encodedNode;
    341 }
    342 
    343 Type Schema::getBrandBinding(uint64_t scopeId, uint index) const {
    344   return getBrandArgumentsAtScope(scopeId)[index];
    345 }
    346 
    347 Type Schema::interpretType(schema::Type::Reader proto, uint location) const {
    348   switch (proto.which()) {
    349     case schema::Type::VOID:
    350     case schema::Type::BOOL:
    351     case schema::Type::INT8:
    352     case schema::Type::INT16:
    353     case schema::Type::INT32:
    354     case schema::Type::INT64:
    355     case schema::Type::UINT8:
    356     case schema::Type::UINT16:
    357     case schema::Type::UINT32:
    358     case schema::Type::UINT64:
    359     case schema::Type::FLOAT32:
    360     case schema::Type::FLOAT64:
    361     case schema::Type::TEXT:
    362     case schema::Type::DATA:
    363       return proto.which();
    364 
    365     case schema::Type::STRUCT: {
    366       auto structType = proto.getStruct();
    367       return getDependency(structType.getTypeId(), location).asStruct();
    368     }
    369 
    370     case schema::Type::ENUM: {
    371       auto enumType = proto.getEnum();
    372       return getDependency(enumType.getTypeId(), location).asEnum();
    373     }
    374 
    375     case schema::Type::INTERFACE: {
    376       auto interfaceType = proto.getInterface();
    377       return getDependency(interfaceType.getTypeId(), location).asInterface();
    378     }
    379 
    380     case schema::Type::LIST:
    381       return ListSchema::of(interpretType(proto.getList().getElementType(), location));
    382 
    383     case schema::Type::ANY_POINTER: {
    384       auto anyPointer = proto.getAnyPointer();
    385       switch (anyPointer.which()) {
    386         case schema::Type::AnyPointer::UNCONSTRAINED:
    387           return anyPointer.getUnconstrained().which();
    388         case schema::Type::AnyPointer::PARAMETER: {
    389           auto param = anyPointer.getParameter();
    390           return getBrandBinding(param.getScopeId(), param.getParameterIndex());
    391         }
    392         case schema::Type::AnyPointer::IMPLICIT_METHOD_PARAMETER:
    393           return Type(Type::ImplicitParameter {
    394               anyPointer.getImplicitMethodParameter().getParameterIndex() });
    395       }
    396 
    397       KJ_UNREACHABLE;
    398     }
    399   }
    400 
    401   KJ_UNREACHABLE;
    402 }
    403 
    404 Type Schema::BrandArgumentList::operator[](uint index) const {
    405   if (isUnbound) {
    406     return Type::BrandParameter { scopeId, index };
    407   }
    408 
    409   if (index >= size_) {
    410     // Binding index out-of-range. Treat as AnyPointer. This is important to allow new
    411     // type parameters to be added to existing types without breaking dependent
    412     // schemas.
    413     return schema::Type::ANY_POINTER;
    414   }
    415 
    416   auto& binding = bindings[index];
    417   Type result;
    418   if (binding.which == (uint)schema::Type::ANY_POINTER) {
    419     if (binding.scopeId != 0) {
    420       result = Type::BrandParameter { binding.scopeId, binding.paramIndex };
    421     } else if (binding.isImplicitParameter) {
    422       result = Type::ImplicitParameter { binding.paramIndex };
    423     } else {
    424       result = static_cast<schema::Type::AnyPointer::Unconstrained::Which>(binding.paramIndex);
    425     }
    426   } else if (binding.schema == nullptr) {
    427     // Builtin / primitive type.
    428     result = static_cast<schema::Type::Which>(binding.which);
    429   } else {
    430     binding.schema->ensureInitialized();
    431     result = Type(static_cast<schema::Type::Which>(binding.which), binding.schema);
    432   }
    433 
    434   return result.wrapInList(binding.listDepth);
    435 }
    436 
    437 kj::StringPtr KJ_STRINGIFY(const Schema& schema) {
    438   return schema.getProto().getDisplayName();
    439 }
    440 
    441 // =======================================================================================
    442 
    443 namespace {
    444 
    445 template <typename List>
    446 auto findSchemaMemberByName(const _::RawSchema* raw, kj::StringPtr name, List&& list)
    447     -> kj::Maybe<decltype(list[0])> {
    448   uint lower = 0;
    449   uint upper = raw->memberCount;
    450 
    451   while (lower < upper) {
    452     uint mid = (lower + upper) / 2;
    453 
    454     uint16_t memberIndex = raw->membersByName[mid];
    455 
    456     auto candidate = list[memberIndex];
    457     kj::StringPtr candidateName = candidate.getProto().getName();
    458     if (candidateName == name) {
    459       return candidate;
    460     } else if (candidateName < name) {
    461       lower = mid + 1;
    462     } else {
    463       upper = mid;
    464     }
    465   }
    466 
    467   return nullptr;
    468 }
    469 
    470 }  // namespace
    471 
    472 StructSchema::FieldList StructSchema::getFields() const {
    473   return FieldList(*this, getProto().getStruct().getFields());
    474 }
    475 
    476 StructSchema::FieldSubset StructSchema::getUnionFields() const {
    477   auto proto = getProto().getStruct();
    478   return FieldSubset(*this, proto.getFields(),
    479                      raw->generic->membersByDiscriminant, proto.getDiscriminantCount());
    480 }
    481 
    482 StructSchema::FieldSubset StructSchema::getNonUnionFields() const {
    483   auto proto = getProto().getStruct();
    484   auto fields = proto.getFields();
    485   auto offset = proto.getDiscriminantCount();
    486   auto size = fields.size() - offset;
    487   return FieldSubset(*this, fields, raw->generic->membersByDiscriminant + offset, size);
    488 }
    489 
    490 kj::Maybe<StructSchema::Field> StructSchema::findFieldByName(kj::StringPtr name) const {
    491   return findSchemaMemberByName(raw->generic, name, getFields());
    492 }
    493 
    494 StructSchema::Field StructSchema::getFieldByName(kj::StringPtr name) const {
    495   KJ_IF_MAYBE(member, findFieldByName(name)) {
    496     return *member;
    497   } else {
    498     KJ_FAIL_REQUIRE("struct has no such member", name);
    499   }
    500 }
    501 
    502 kj::Maybe<StructSchema::Field> StructSchema::getFieldByDiscriminant(uint16_t discriminant) const {
    503   auto unionFields = getUnionFields();
    504 
    505   if (discriminant >= unionFields.size()) {
    506     return nullptr;
    507   } else {
    508     return unionFields[discriminant];
    509   }
    510 }
    511 
    512 bool StructSchema::isStreamResult() const {
    513   auto& streamRaw = _::rawSchema<StreamResult>();
    514   return raw->generic == &streamRaw || raw->generic->canCastTo == &streamRaw;
    515 }
    516 
    517 Type StructSchema::Field::getType() const {
    518   auto proto = getProto();
    519   uint location = _::RawBrandedSchema::makeDepLocation(_::RawBrandedSchema::DepKind::FIELD, index);
    520 
    521   switch (proto.which()) {
    522     case schema::Field::SLOT:
    523       return parent.interpretType(proto.getSlot().getType(), location);
    524 
    525     case schema::Field::GROUP:
    526       return parent.getDependency(proto.getGroup().getTypeId(), location).asStruct();
    527   }
    528   KJ_UNREACHABLE;
    529 }
    530 
    531 uint32_t StructSchema::Field::getDefaultValueSchemaOffset() const {
    532   return parent.getSchemaOffset(proto.getSlot().getDefaultValue());
    533 }
    534 
    535 kj::StringPtr KJ_STRINGIFY(const StructSchema::Field& field) {
    536   return field.getProto().getName();
    537 }
    538 
    539 // -------------------------------------------------------------------
    540 
    541 EnumSchema::EnumerantList EnumSchema::getEnumerants() const {
    542   return EnumerantList(*this, getProto().getEnum().getEnumerants());
    543 }
    544 
    545 kj::Maybe<EnumSchema::Enumerant> EnumSchema::findEnumerantByName(kj::StringPtr name) const {
    546   return findSchemaMemberByName(raw->generic, name, getEnumerants());
    547 }
    548 
    549 EnumSchema::Enumerant EnumSchema::getEnumerantByName(kj::StringPtr name) const {
    550   KJ_IF_MAYBE(enumerant, findEnumerantByName(name)) {
    551     return *enumerant;
    552   } else {
    553     KJ_FAIL_REQUIRE("enum has no such enumerant", name);
    554   }
    555 }
    556 
    557 // -------------------------------------------------------------------
    558 
    559 InterfaceSchema::MethodList InterfaceSchema::getMethods() const {
    560   return MethodList(*this, getProto().getInterface().getMethods());
    561 }
    562 
    563 kj::Maybe<InterfaceSchema::Method> InterfaceSchema::findMethodByName(kj::StringPtr name) const {
    564   uint counter = 0;
    565   return findMethodByName(name, counter);
    566 }
    567 
    568 static constexpr uint MAX_SUPERCLASSES = 64;
    569 
    570 kj::Maybe<InterfaceSchema::Method> InterfaceSchema::findMethodByName(
    571     kj::StringPtr name, uint& counter) const {
    572   // Security:  Don't let someone DOS us with a dynamic schema containing cyclic inheritance.
    573   KJ_REQUIRE(counter++ < MAX_SUPERCLASSES, "Cyclic or absurdly-large inheritance graph detected.") {
    574     return nullptr;
    575   }
    576 
    577   auto result = findSchemaMemberByName(raw->generic, name, getMethods());
    578 
    579   if (result == nullptr) {
    580     // Search superclasses.
    581     // TODO(perf):  This may be somewhat slow, and in the case of lots of diamond dependencies it
    582     //   could get pathological.  Arguably we should generate a flat list of transitive
    583     //   superclasses to search and store it in the RawSchema.  It's problematic, though, because
    584     //   this means that a dynamically-loaded RawSchema cannot be correctly constructed until all
    585     //   superclasses have been loaded, which imposes an ordering requirement on SchemaLoader or
    586     //   requires updating subclasses whenever a new superclass is loaded.
    587     auto superclasses = getProto().getInterface().getSuperclasses();
    588     for (auto i: kj::indices(superclasses)) {
    589       auto superclass = superclasses[i];
    590       uint location = _::RawBrandedSchema::makeDepLocation(
    591           _::RawBrandedSchema::DepKind::SUPERCLASS, i);
    592       result = getDependency(superclass.getId(), location)
    593           .asInterface().findMethodByName(name, counter);
    594       if (result != nullptr) {
    595         break;
    596       }
    597     }
    598   }
    599 
    600   return result;
    601 }
    602 
    603 InterfaceSchema::Method InterfaceSchema::getMethodByName(kj::StringPtr name) const {
    604   KJ_IF_MAYBE(method, findMethodByName(name)) {
    605     return *method;
    606   } else {
    607     KJ_FAIL_REQUIRE("interface has no such method", name);
    608   }
    609 }
    610 
    611 InterfaceSchema::SuperclassList InterfaceSchema::getSuperclasses() const {
    612   return SuperclassList(*this, getProto().getInterface().getSuperclasses());
    613 }
    614 
    615 bool InterfaceSchema::extends(InterfaceSchema other) const {
    616   if (other.raw->generic == &_::NULL_INTERFACE_SCHEMA) {
    617     // We consider all interfaces to extend the null schema.
    618     return true;
    619   }
    620   uint counter = 0;
    621   return extends(other, counter);
    622 }
    623 
    624 bool InterfaceSchema::extends(InterfaceSchema other, uint& counter) const {
    625   // Security:  Don't let someone DOS us with a dynamic schema containing cyclic inheritance.
    626   KJ_REQUIRE(counter++ < MAX_SUPERCLASSES, "Cyclic or absurdly-large inheritance graph detected.") {
    627     return false;
    628   }
    629 
    630   if (other == *this) {
    631     return true;
    632   }
    633 
    634   // TODO(perf):  This may be somewhat slow.  See findMethodByName() for discussion.
    635   auto superclasses = getProto().getInterface().getSuperclasses();
    636   for (auto i: kj::indices(superclasses)) {
    637     auto superclass = superclasses[i];
    638     uint location = _::RawBrandedSchema::makeDepLocation(
    639         _::RawBrandedSchema::DepKind::SUPERCLASS, i);
    640     if (getDependency(superclass.getId(), location).asInterface().extends(other, counter)) {
    641       return true;
    642     }
    643   }
    644 
    645   return false;
    646 }
    647 
    648 kj::Maybe<InterfaceSchema> InterfaceSchema::findSuperclass(uint64_t typeId) const {
    649   if (typeId == _::NULL_INTERFACE_SCHEMA.id) {
    650     // We consider all interfaces to extend the null schema.
    651     return InterfaceSchema();
    652   }
    653   uint counter = 0;
    654   return findSuperclass(typeId, counter);
    655 }
    656 
    657 kj::Maybe<InterfaceSchema> InterfaceSchema::findSuperclass(uint64_t typeId, uint& counter) const {
    658   // Security:  Don't let someone DOS us with a dynamic schema containing cyclic inheritance.
    659   KJ_REQUIRE(counter++ < MAX_SUPERCLASSES, "Cyclic or absurdly-large inheritance graph detected.") {
    660     return nullptr;
    661   }
    662 
    663   if (typeId == raw->generic->id) {
    664     return *this;
    665   }
    666 
    667   // TODO(perf):  This may be somewhat slow.  See findMethodByName() for discussion.
    668   auto superclasses = getProto().getInterface().getSuperclasses();
    669   for (auto i: kj::indices(superclasses)) {
    670     auto superclass = superclasses[i];
    671     uint location = _::RawBrandedSchema::makeDepLocation(
    672         _::RawBrandedSchema::DepKind::SUPERCLASS, i);
    673     KJ_IF_MAYBE(result, getDependency(superclass.getId(), location).asInterface()
    674                             .findSuperclass(typeId, counter)) {
    675       return *result;
    676     }
    677   }
    678 
    679   return nullptr;
    680 }
    681 
    682 StructSchema InterfaceSchema::Method::getParamType() const {
    683   auto proto = getProto();
    684   uint location = _::RawBrandedSchema::makeDepLocation(
    685       _::RawBrandedSchema::DepKind::METHOD_PARAMS, ordinal);
    686   return parent.getDependency(proto.getParamStructType(), location).asStruct();
    687 }
    688 
    689 StructSchema InterfaceSchema::Method::getResultType() const {
    690   auto proto = getProto();
    691   uint location = _::RawBrandedSchema::makeDepLocation(
    692       _::RawBrandedSchema::DepKind::METHOD_RESULTS, ordinal);
    693   return parent.getDependency(proto.getResultStructType(), location).asStruct();
    694 }
    695 
    696 InterfaceSchema InterfaceSchema::SuperclassList::operator[](uint index) const {
    697   auto superclass = list[index];
    698   uint location = _::RawBrandedSchema::makeDepLocation(
    699       _::RawBrandedSchema::DepKind::SUPERCLASS, index);
    700   return parent.getDependency(superclass.getId(), location).asInterface();
    701 }
    702 
    703 // -------------------------------------------------------------------
    704 
    705 uint32_t ConstSchema::getValueSchemaOffset() const {
    706   return getSchemaOffset(getProto().getConst().getValue());
    707 }
    708 
    709 Type ConstSchema::getType() const {
    710   return interpretType(getProto().getConst().getType(),
    711       _::RawBrandedSchema::makeDepLocation(_::RawBrandedSchema::DepKind::CONST_TYPE, 0));
    712 }
    713 
    714 // =======================================================================================
    715 
    716 ListSchema ListSchema::of(schema::Type::Which primitiveType) {
    717   switch (primitiveType) {
    718     case schema::Type::VOID:
    719     case schema::Type::BOOL:
    720     case schema::Type::INT8:
    721     case schema::Type::INT16:
    722     case schema::Type::INT32:
    723     case schema::Type::INT64:
    724     case schema::Type::UINT8:
    725     case schema::Type::UINT16:
    726     case schema::Type::UINT32:
    727     case schema::Type::UINT64:
    728     case schema::Type::FLOAT32:
    729     case schema::Type::FLOAT64:
    730     case schema::Type::TEXT:
    731     case schema::Type::DATA:
    732       break;
    733 
    734     case schema::Type::STRUCT:
    735     case schema::Type::ENUM:
    736     case schema::Type::INTERFACE:
    737     case schema::Type::LIST:
    738       KJ_FAIL_REQUIRE("Must use one of the other ListSchema::of() overloads for complex types.");
    739       break;
    740 
    741     case schema::Type::ANY_POINTER:
    742       KJ_FAIL_REQUIRE("List(AnyPointer) not supported.");
    743       break;
    744   }
    745 
    746   return ListSchema(primitiveType);
    747 }
    748 
    749 ListSchema ListSchema::of(schema::Type::Reader elementType, Schema context) {
    750   // This method is deprecated because it can only be implemented in terms of other deprecated
    751   // methods. Temporarily disable warnings for those other deprecated methods.
    752 #pragma GCC diagnostic push
    753 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
    754 
    755   switch (elementType.which()) {
    756     case schema::Type::VOID:
    757     case schema::Type::BOOL:
    758     case schema::Type::INT8:
    759     case schema::Type::INT16:
    760     case schema::Type::INT32:
    761     case schema::Type::INT64:
    762     case schema::Type::UINT8:
    763     case schema::Type::UINT16:
    764     case schema::Type::UINT32:
    765     case schema::Type::UINT64:
    766     case schema::Type::FLOAT32:
    767     case schema::Type::FLOAT64:
    768     case schema::Type::TEXT:
    769     case schema::Type::DATA:
    770       return of(elementType.which());
    771 
    772     case schema::Type::STRUCT:
    773       return of(context.getDependency(elementType.getStruct().getTypeId()).asStruct());
    774 
    775     case schema::Type::ENUM:
    776       return of(context.getDependency(elementType.getEnum().getTypeId()).asEnum());
    777 
    778     case schema::Type::INTERFACE:
    779       return of(context.getDependency(elementType.getInterface().getTypeId()).asInterface());
    780 
    781     case schema::Type::LIST:
    782       return of(of(elementType.getList().getElementType(), context));
    783 
    784     case schema::Type::ANY_POINTER:
    785       KJ_FAIL_REQUIRE("List(AnyPointer) not supported.");
    786       return ListSchema();
    787   }
    788 
    789   // Unknown type is acceptable.
    790   return ListSchema(elementType.which());
    791 #pragma GCC diagnostic pop
    792 }
    793 
    794 // =======================================================================================
    795 
    796 StructSchema Type::asStruct() const {
    797   KJ_REQUIRE(isStruct(), "Tried to interpret a non-struct type as a struct.") {
    798     return StructSchema();
    799   }
    800   KJ_ASSERT(schema != nullptr);
    801   return StructSchema(Schema(schema));
    802 }
    803 EnumSchema Type::asEnum() const {
    804   KJ_REQUIRE(isEnum(), "Tried to interpret a non-enum type as an enum.") {
    805     return EnumSchema();
    806   }
    807   KJ_ASSERT(schema != nullptr);
    808   return EnumSchema(Schema(schema));
    809 }
    810 InterfaceSchema Type::asInterface() const {
    811   KJ_REQUIRE(isInterface(), "Tried to interpret a non-interface type as an interface.") {
    812     return InterfaceSchema();
    813   }
    814   KJ_ASSERT(schema != nullptr);
    815   return InterfaceSchema(Schema(schema));
    816 }
    817 ListSchema Type::asList() const {
    818   KJ_REQUIRE(isList(), "Type::asList(): Not a list.") {
    819     return ListSchema::of(schema::Type::VOID);
    820   }
    821   Type elementType = *this;
    822   --elementType.listDepth;
    823   return ListSchema::of(elementType);
    824 }
    825 
    826 kj::Maybe<Type::BrandParameter> Type::getBrandParameter() const {
    827   KJ_REQUIRE(isAnyPointer(), "Type::getBrandParameter() can only be called on AnyPointer types.");
    828 
    829   if (scopeId == 0) {
    830     return nullptr;
    831   } else {
    832     return BrandParameter { scopeId, paramIndex };
    833   }
    834 }
    835 
    836 kj::Maybe<Type::ImplicitParameter> Type::getImplicitParameter() const {
    837   KJ_REQUIRE(isAnyPointer(),
    838       "Type::getImplicitParameter() can only be called on AnyPointer types.");
    839 
    840   if (isImplicitParam) {
    841     return ImplicitParameter { paramIndex };
    842   } else {
    843     return nullptr;
    844   }
    845 }
    846 
    847 bool Type::operator==(const Type& other) const {
    848   if (baseType != other.baseType || listDepth != other.listDepth) {
    849     return false;
    850   }
    851 
    852   switch (baseType) {
    853     case schema::Type::VOID:
    854     case schema::Type::BOOL:
    855     case schema::Type::INT8:
    856     case schema::Type::INT16:
    857     case schema::Type::INT32:
    858     case schema::Type::INT64:
    859     case schema::Type::UINT8:
    860     case schema::Type::UINT16:
    861     case schema::Type::UINT32:
    862     case schema::Type::UINT64:
    863     case schema::Type::FLOAT32:
    864     case schema::Type::FLOAT64:
    865     case schema::Type::TEXT:
    866     case schema::Type::DATA:
    867       return true;
    868 
    869     case schema::Type::STRUCT:
    870     case schema::Type::ENUM:
    871     case schema::Type::INTERFACE:
    872       return schema == other.schema;
    873 
    874     case schema::Type::LIST:
    875       KJ_UNREACHABLE;
    876 
    877     case schema::Type::ANY_POINTER:
    878       return scopeId == other.scopeId && isImplicitParam == other.isImplicitParam &&
    879           // Trying to comply with strict aliasing rules. Hopefully the compiler realizes that
    880           // both branches compile to the same instructions and can optimize it away.
    881           (scopeId != 0 || isImplicitParam ? paramIndex == other.paramIndex
    882                                            : anyPointerKind == other.anyPointerKind);
    883   }
    884 
    885   KJ_UNREACHABLE;
    886 }
    887 
    888 uint Type::hashCode() const {
    889   switch (baseType) {
    890     case schema::Type::VOID:
    891     case schema::Type::BOOL:
    892     case schema::Type::INT8:
    893     case schema::Type::INT16:
    894     case schema::Type::INT32:
    895     case schema::Type::INT64:
    896     case schema::Type::UINT8:
    897     case schema::Type::UINT16:
    898     case schema::Type::UINT32:
    899     case schema::Type::UINT64:
    900     case schema::Type::FLOAT32:
    901     case schema::Type::FLOAT64:
    902     case schema::Type::TEXT:
    903     case schema::Type::DATA:
    904       if (listDepth == 0) {
    905         // Make sure that hashCode(Type(baseType)) == hashCode(baseType), otherwise HashMap lookups
    906         // keyed by `Type` won't work when the caller passes `baseType` as the key.
    907         return kj::hashCode(baseType);
    908       } else {
    909         return kj::hashCode(baseType, listDepth);
    910       }
    911 
    912     case schema::Type::STRUCT:
    913     case schema::Type::ENUM:
    914     case schema::Type::INTERFACE:
    915       if (listDepth == 0) {
    916         // Make sure that hashCode(Type(schema)) == hashCode(schema), otherwise HashMap lookups
    917         // keyed by `Type` won't work when the caller passes `schema` as the key.
    918         return kj::hashCode(schema);
    919       } else {
    920         return kj::hashCode(schema, listDepth);
    921       }
    922 
    923     case schema::Type::LIST:
    924       KJ_UNREACHABLE;
    925 
    926     case schema::Type::ANY_POINTER: {
    927       // Trying to comply with strict aliasing rules. Hopefully the compiler realizes that
    928       // both branches compile to the same instructions and can optimize it away.
    929       uint16_t val = scopeId != 0 || isImplicitParam ?
    930           paramIndex : static_cast<uint16_t>(anyPointerKind);
    931       return kj::hashCode(val, isImplicitParam, scopeId, listDepth);
    932     }
    933   }
    934 
    935   KJ_UNREACHABLE;
    936 }
    937 
    938 void Type::requireUsableAs(Type expected) const {
    939   KJ_REQUIRE(baseType == expected.baseType && listDepth == expected.listDepth,
    940              "This type is not compatible with the requested native type.");
    941 
    942   switch (baseType) {
    943     case schema::Type::VOID:
    944     case schema::Type::BOOL:
    945     case schema::Type::INT8:
    946     case schema::Type::INT16:
    947     case schema::Type::INT32:
    948     case schema::Type::INT64:
    949     case schema::Type::UINT8:
    950     case schema::Type::UINT16:
    951     case schema::Type::UINT32:
    952     case schema::Type::UINT64:
    953     case schema::Type::FLOAT32:
    954     case schema::Type::FLOAT64:
    955     case schema::Type::TEXT:
    956     case schema::Type::DATA:
    957     case schema::Type::ANY_POINTER:
    958       break;
    959 
    960     case schema::Type::STRUCT:
    961     case schema::Type::ENUM:
    962     case schema::Type::INTERFACE:
    963       Schema(schema).requireUsableAs(expected.schema->generic);
    964       break;
    965 
    966     case schema::Type::LIST:
    967       KJ_UNREACHABLE;
    968   }
    969 }
    970 
    971 }  // namespace capnp