schema.c++ 10.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "schema.h"
#include "message.h"
Kenton Varda's avatar
Kenton Varda committed
26
#include <kj/debug.h>
27

28
namespace capnp {
29

Kenton Varda's avatar
Kenton Varda committed
30 31
schema2::Node::Reader Schema::getProto() const {
  return readMessageUnchecked<schema2::Node>(raw->encodedNode);
32 33
}

Kenton Varda's avatar
Kenton Varda committed
34 35 36 37
kj::ArrayPtr<const word> Schema::asUncheckedMessage() const {
  return kj::arrayPtr(raw->encodedNode, raw->encodedSize);
}

38 39 40 41 42 43 44
Schema Schema::getDependency(uint64_t id) const {
  uint lower = 0;
  uint upper = raw->dependencyCount;

  while (lower < upper) {
    uint mid = (lower + upper) / 2;

45
    const _::RawSchema* candidate = raw->dependencies[mid];
46

47
    uint64_t candidateId = candidate->id;
48
    if (candidateId == id) {
49 50
      candidate->ensureInitialized();
      return Schema(candidate);
51 52 53 54 55 56 57
    } else if (candidateId < id) {
      lower = mid + 1;
    } else {
      upper = mid;
    }
  }

58
  KJ_FAIL_REQUIRE("Requested ID not found in dependency table.", kj::hex(id));
59 60 61 62
  return Schema();
}

StructSchema Schema::asStruct() const {
Kenton Varda's avatar
Kenton Varda committed
63
  KJ_REQUIRE(getProto().which() == schema2::Node::STRUCT,
64 65 66 67 68 69
          "Tried to use non-struct schema as a struct.",
          getProto().getDisplayName());
  return StructSchema(raw);
}

EnumSchema Schema::asEnum() const {
Kenton Varda's avatar
Kenton Varda committed
70
  KJ_REQUIRE(getProto().which() == schema2::Node::ENUM,
71 72 73 74 75 76
          "Tried to use non-enum schema as an enum.",
          getProto().getDisplayName());
  return EnumSchema(raw);
}

InterfaceSchema Schema::asInterface() const {
Kenton Varda's avatar
Kenton Varda committed
77
  KJ_REQUIRE(getProto().which() == schema2::Node::INTERFACE,
78 79 80 81 82
          "Tried to use non-interface schema as an interface.",
          getProto().getDisplayName());
  return InterfaceSchema(raw);
}

83
void Schema::requireUsableAs(const _::RawSchema* expected) const {
84
  KJ_REQUIRE(raw == expected ||
85 86 87 88
          (raw != nullptr && expected != nullptr && raw->canCastTo == expected),
          "This schema is not compatible with the requested native type.");
}

89 90 91 92 93
// =======================================================================================

namespace {

template <typename List>
Kenton Varda's avatar
Kenton Varda committed
94
auto findSchemaMemberByName(const _::RawSchema* raw, kj::StringPtr name, List&& list)
95
    -> kj::Maybe<decltype(list[0])> {
96 97
  uint lower = 0;
  uint upper = raw->memberCount;
98
  List unnamedUnionMembers;
99 100 101 102

  while (lower < upper) {
    uint mid = (lower + upper) / 2;

Kenton Varda's avatar
Kenton Varda committed
103 104 105 106 107 108 109
    uint16_t memberIndex = raw->membersByName[mid];

    auto candidate = list[memberIndex];
    kj::StringPtr candidateName = candidate.getProto().getName();
    if (candidateName == name) {
      return candidate;
    } else if (candidateName < name) {
110 111 112 113 114 115 116 117 118 119 120
      lower = mid + 1;
    } else {
      upper = mid;
    }
  }

  return nullptr;
}

}  // namespace

Kenton Varda's avatar
Kenton Varda committed
121 122
StructSchema::FieldList StructSchema::getFields() const {
  return FieldList(*this, getProto().getStruct().getFields());
123 124
}

Kenton Varda's avatar
Kenton Varda committed
125 126 127 128 129
StructSchema::FieldSubset StructSchema::getUnionFields() const {
  auto proto = getProto().getStruct();
  return FieldSubset(*this, proto.getFields(),
                     raw->membersByDiscriminant, proto.getDiscriminantCount());
}
130

Kenton Varda's avatar
Kenton Varda committed
131 132 133 134 135 136
StructSchema::FieldSubset StructSchema::getNonUnionFields() const {
  auto proto = getProto().getStruct();
  auto fields = proto.getFields();
  auto offset = proto.getDiscriminantCount();
  auto size = fields.size() - offset;
  return FieldSubset(*this, fields, raw->membersByDiscriminant + offset, size);
137 138
}

Kenton Varda's avatar
Kenton Varda committed
139 140 141 142 143 144
kj::Maybe<StructSchema::Field> StructSchema::findFieldByName(kj::StringPtr name) const {
  return findSchemaMemberByName(raw, name, getFields());
}

StructSchema::Field StructSchema::getFieldByName(kj::StringPtr name) const {
  KJ_IF_MAYBE(member, findFieldByName(name)) {
145 146
    return *member;
  } else {
147
    KJ_FAIL_REQUIRE("struct has no such member", name);
148
  }
Kenton Varda's avatar
Kenton Varda committed
149 150
}

Kenton Varda's avatar
Kenton Varda committed
151 152
kj::Maybe<StructSchema::Field> StructSchema::getFieldByDiscriminant(uint16_t discriminant) const {
  auto unionFields = getUnionFields();
153

Kenton Varda's avatar
Kenton Varda committed
154 155 156 157 158
  if (discriminant >= unionFields.size()) {
    return nullptr;
  } else {
    return unionFields[discriminant];
  }
Kenton Varda's avatar
Kenton Varda committed
159 160 161
}

uint32_t StructSchema::Field::getDefaultValueSchemaOffset() const {
Kenton Varda's avatar
Kenton Varda committed
162
  auto defaultValue = proto.getRegular().getDefaultValue();
Kenton Varda's avatar
Kenton Varda committed
163 164 165
  const word* ptr;

  switch (defaultValue.which()) {
Kenton Varda's avatar
Kenton Varda committed
166 167
    case schema2::Value::TEXT:
      ptr = reinterpret_cast<const word*>(defaultValue.getText().begin());
Kenton Varda's avatar
Kenton Varda committed
168
      break;
Kenton Varda's avatar
Kenton Varda committed
169 170
    case schema2::Value::DATA:
      ptr = reinterpret_cast<const word*>(defaultValue.getData().begin());
Kenton Varda's avatar
Kenton Varda committed
171
      break;
Kenton Varda's avatar
Kenton Varda committed
172 173
    case schema2::Value::STRUCT:
      ptr = defaultValue.getStruct<_::UncheckedMessage>();
Kenton Varda's avatar
Kenton Varda committed
174
      break;
Kenton Varda's avatar
Kenton Varda committed
175 176
    case schema2::Value::LIST:
      ptr = defaultValue.getList<_::UncheckedMessage>();
Kenton Varda's avatar
Kenton Varda committed
177
      break;
Kenton Varda's avatar
Kenton Varda committed
178 179
    case schema2::Value::OBJECT:
      ptr = defaultValue.getObject<_::UncheckedMessage>();
Kenton Varda's avatar
Kenton Varda committed
180 181 182 183 184 185 186 187 188
      break;
    default:
      KJ_FAIL_ASSERT("getDefaultValueSchemaOffset() can only be called on struct, list, "
                     "and object fields.");
  }

  return ptr - parent.raw->encodedNode;
}

189 190
// -------------------------------------------------------------------

191
EnumSchema::EnumerantList EnumSchema::getEnumerants() const {
Kenton Varda's avatar
Kenton Varda committed
192
  return EnumerantList(*this, getProto().getEnum());
193 194
}

195
kj::Maybe<EnumSchema::Enumerant> EnumSchema::findEnumerantByName(kj::StringPtr name) const {
Kenton Varda's avatar
Kenton Varda committed
196
  return findSchemaMemberByName(raw, name, getEnumerants());
197 198
}

199
EnumSchema::Enumerant EnumSchema::getEnumerantByName(kj::StringPtr name) const {
200 201 202
  KJ_IF_MAYBE(enumerant, findEnumerantByName(name)) {
    return *enumerant;
  } else {
203
    KJ_FAIL_REQUIRE("enum has no such enumerant", name);
204
  }
Kenton Varda's avatar
Kenton Varda committed
205 206
}

207 208
// -------------------------------------------------------------------

209
InterfaceSchema::MethodList InterfaceSchema::getMethods() const {
Kenton Varda's avatar
Kenton Varda committed
210
  return MethodList(*this, getProto().getInterface());
211 212
}

213
kj::Maybe<InterfaceSchema::Method> InterfaceSchema::findMethodByName(kj::StringPtr name) const {
Kenton Varda's avatar
Kenton Varda committed
214
  return findSchemaMemberByName(raw, name, getMethods());
215 216
}

217
InterfaceSchema::Method InterfaceSchema::getMethodByName(kj::StringPtr name) const {
218 219 220
  KJ_IF_MAYBE(method, findMethodByName(name)) {
    return *method;
  } else {
221
    KJ_FAIL_REQUIRE("interface has no such method", name);
222
  }
Kenton Varda's avatar
Kenton Varda committed
223 224
}

225 226
// =======================================================================================

Kenton Varda's avatar
Kenton Varda committed
227
ListSchema ListSchema::of(schema2::Type::Which primitiveType) {
228
  switch (primitiveType) {
Kenton Varda's avatar
Kenton Varda committed
229 230 231 232 233 234 235 236 237 238 239 240 241 242
    case schema2::Type::VOID:
    case schema2::Type::BOOL:
    case schema2::Type::INT8:
    case schema2::Type::INT16:
    case schema2::Type::INT32:
    case schema2::Type::INT64:
    case schema2::Type::UINT8:
    case schema2::Type::UINT16:
    case schema2::Type::UINT32:
    case schema2::Type::UINT64:
    case schema2::Type::FLOAT32:
    case schema2::Type::FLOAT64:
    case schema2::Type::TEXT:
    case schema2::Type::DATA:
243 244
      break;

Kenton Varda's avatar
Kenton Varda committed
245 246 247 248
    case schema2::Type::STRUCT:
    case schema2::Type::ENUM:
    case schema2::Type::INTERFACE:
    case schema2::Type::LIST:
249
      KJ_FAIL_REQUIRE("Must use one of the other ListSchema::of() overloads for complex types.");
250 251
      break;

Kenton Varda's avatar
Kenton Varda committed
252
    case schema2::Type::OBJECT:
253
      KJ_FAIL_REQUIRE("List(Object) not supported.");
254 255 256 257 258 259
      break;
  }

  return ListSchema(primitiveType);
}

Kenton Varda's avatar
Kenton Varda committed
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
ListSchema ListSchema::of(schema2::Type::Reader elementType, Schema context) {
  switch (elementType.which()) {
    case schema2::Type::VOID:
    case schema2::Type::BOOL:
    case schema2::Type::INT8:
    case schema2::Type::INT16:
    case schema2::Type::INT32:
    case schema2::Type::INT64:
    case schema2::Type::UINT8:
    case schema2::Type::UINT16:
    case schema2::Type::UINT32:
    case schema2::Type::UINT64:
    case schema2::Type::FLOAT32:
    case schema2::Type::FLOAT64:
    case schema2::Type::TEXT:
    case schema2::Type::DATA:
      return of(elementType.which());

    case schema2::Type::STRUCT:
      return of(context.getDependency(elementType.getStruct()).asStruct());

    case schema2::Type::ENUM:
      return of(context.getDependency(elementType.getEnum()).asEnum());

    case schema2::Type::INTERFACE:
      return of(context.getDependency(elementType.getInterface()).asInterface());

    case schema2::Type::LIST:
      return of(of(elementType.getList(), context));

    case schema2::Type::OBJECT:
291
      KJ_FAIL_REQUIRE("List(Object) not supported.");
292 293 294
      return ListSchema();
  }

295
  // Unknown type is acceptable.
Kenton Varda's avatar
Kenton Varda committed
296
  return ListSchema(elementType.which());
297 298 299
}

StructSchema ListSchema::getStructElementType() const {
Kenton Varda's avatar
Kenton Varda committed
300
  KJ_REQUIRE(nestingDepth == 0 && elementType == schema2::Type::STRUCT,
301 302 303 304 305
          "ListSchema::getStructElementType(): The elements are not structs.");
  return elementSchema.asStruct();
}

EnumSchema ListSchema::getEnumElementType() const {
Kenton Varda's avatar
Kenton Varda committed
306
  KJ_REQUIRE(nestingDepth == 0 && elementType == schema2::Type::ENUM,
307 308 309 310 311
          "ListSchema::getEnumElementType(): The elements are not enums.");
  return elementSchema.asEnum();
}

InterfaceSchema ListSchema::getInterfaceElementType() const {
Kenton Varda's avatar
Kenton Varda committed
312
  KJ_REQUIRE(nestingDepth == 0 && elementType == schema2::Type::INTERFACE,
313 314 315 316 317
          "ListSchema::getInterfaceElementType(): The elements are not interfaces.");
  return elementSchema.asInterface();
}

ListSchema ListSchema::getListElementType() const {
318
  KJ_REQUIRE(nestingDepth > 0,
319 320 321 322
          "ListSchema::getListElementType(): The elements are not lists.");
  return ListSchema(elementType, nestingDepth - 1, elementSchema);
}

323
void ListSchema::requireUsableAs(ListSchema expected) const {
324
  KJ_REQUIRE(elementType == expected.elementType && nestingDepth == expected.nestingDepth,
325 326 327 328
          "This schema is not compatible with the requested native type.");
  elementSchema.requireUsableAs(expected.elementSchema.raw);
}

329
}  // namespace capnp