dynamic.c++ 74.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:
Kenton Varda's avatar
Kenton Varda committed
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:
Kenton Varda's avatar
Kenton Varda committed
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.
Kenton Varda's avatar
Kenton Varda committed
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.
Kenton Varda's avatar
Kenton Varda committed
21 22

#include "dynamic.h"
Kenton Varda's avatar
Kenton Varda committed
23
#include <kj/debug.h>
Kenton Varda's avatar
Kenton Varda committed
24

25
namespace capnp {
Kenton Varda's avatar
Kenton Varda committed
26 27 28

namespace {

29 30 31 32
bool hasDiscriminantValue(const schema::Field::Reader& reader) {
  return reader.getDiscriminantValue() != schema::Field::NO_DISCRIMINANT;
}

Kenton Varda's avatar
Kenton Varda committed
33
template <typename T, typename U>
34
KJ_ALWAYS_INLINE(T bitCast(U value));
Kenton Varda's avatar
Kenton Varda committed
35 36 37 38 39 40

template <typename T, typename U>
inline T bitCast(U value) {
  static_assert(sizeof(T) == sizeof(U), "Size must match.");
  return value;
}
Kenton Varda's avatar
Kenton Varda committed
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
template <>
inline float bitCast<float, uint32_t>(uint32_t value) KJ_UNUSED;
template <>
inline float bitCast<float, uint32_t>(uint32_t value) {
  float result;
  memcpy(&result, &value, sizeof(value));
  return result;
}
template <>
inline double bitCast<double, uint64_t>(uint64_t value) KJ_UNUSED;
template <>
inline double bitCast<double, uint64_t>(uint64_t value) {
  double result;
  memcpy(&result, &value, sizeof(value));
  return result;
}
Kenton Varda's avatar
Kenton Varda committed
57 58 59 60 61 62 63 64 65 66 67 68 69
template <>
inline uint32_t bitCast<uint32_t, float>(float value) {
  uint32_t result;
  memcpy(&result, &value, sizeof(value));
  return result;
}
template <>
inline uint64_t bitCast<uint64_t, double>(double value) {
  uint64_t result;
  memcpy(&result, &value, sizeof(value));
  return result;
}

70
ElementSize elementSizeFor(schema::Type::Which elementType) {
Kenton Varda's avatar
Kenton Varda committed
71
  switch (elementType) {
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
    case schema::Type::VOID: return ElementSize::VOID;
    case schema::Type::BOOL: return ElementSize::BIT;
    case schema::Type::INT8: return ElementSize::BYTE;
    case schema::Type::INT16: return ElementSize::TWO_BYTES;
    case schema::Type::INT32: return ElementSize::FOUR_BYTES;
    case schema::Type::INT64: return ElementSize::EIGHT_BYTES;
    case schema::Type::UINT8: return ElementSize::BYTE;
    case schema::Type::UINT16: return ElementSize::TWO_BYTES;
    case schema::Type::UINT32: return ElementSize::FOUR_BYTES;
    case schema::Type::UINT64: return ElementSize::EIGHT_BYTES;
    case schema::Type::FLOAT32: return ElementSize::FOUR_BYTES;
    case schema::Type::FLOAT64: return ElementSize::EIGHT_BYTES;

    case schema::Type::TEXT: return ElementSize::POINTER;
    case schema::Type::DATA: return ElementSize::POINTER;
    case schema::Type::LIST: return ElementSize::POINTER;
    case schema::Type::ENUM: return ElementSize::TWO_BYTES;
    case schema::Type::STRUCT: return ElementSize::INLINE_COMPOSITE;
    case schema::Type::INTERFACE: return ElementSize::POINTER;
91
    case schema::Type::ANY_POINTER: KJ_FAIL_ASSERT("List(AnyPointer) not supported."); break;
Kenton Varda's avatar
Kenton Varda committed
92
  }
93 94

  // Unknown type.  Treat it as zero-size.
95
  return ElementSize::VOID;
Kenton Varda's avatar
Kenton Varda committed
96 97
}

98
inline _::StructSize structSizeFromSchema(StructSchema schema) {
Kenton Varda's avatar
Kenton Varda committed
99
  auto node = schema.getProto().getStruct();
100
  return _::StructSize(
101 102
      bounded(node.getDataWordCount()) * WORDS,
      bounded(node.getPointerCount()) * POINTERS);
Kenton Varda's avatar
Kenton Varda committed
103 104
}

Kenton Varda's avatar
Kenton Varda committed
105 106 107 108
}  // namespace

// =======================================================================================

109
kj::Maybe<EnumSchema::Enumerant> DynamicEnum::getEnumerant() const {
110
  auto enumerants = schema.getEnumerants();
Kenton Varda's avatar
Kenton Varda committed
111 112 113 114 115 116 117
  if (value < enumerants.size()) {
    return enumerants[value];
  } else {
    return nullptr;
  }
}

118
uint16_t DynamicEnum::asImpl(uint64_t requestedTypeId) const {
119 120
  KJ_REQUIRE(requestedTypeId == schema.getProto().getId(),
             "Type mismatch in DynamicEnum.as().") {
121
    // use it anyway
122
    break;
Kenton Varda's avatar
Kenton Varda committed
123 124 125 126 127 128
  }
  return value;
}

// =======================================================================================

Kenton Varda's avatar
Kenton Varda committed
129 130
bool DynamicStruct::Reader::isSetInUnion(StructSchema::Field field) const {
  auto proto = field.getProto();
131
  if (hasDiscriminantValue(proto)) {
Kenton Varda's avatar
Kenton Varda committed
132
    uint16_t discrim = reader.getDataField<uint16_t>(
133
        assumeDataOffset(schema.getProto().getStruct().getDiscriminantOffset()));
Kenton Varda's avatar
Kenton Varda committed
134
    return discrim == proto.getDiscriminantValue();
135
  } else {
Kenton Varda's avatar
Kenton Varda committed
136
    return true;
137
  }
Kenton Varda's avatar
Kenton Varda committed
138 139
}

Kenton Varda's avatar
Kenton Varda committed
140 141 142 143
void DynamicStruct::Reader::verifySetInUnion(StructSchema::Field field) const {
  KJ_REQUIRE(isSetInUnion(field),
      "Tried to get() a union member which is not currently initialized.",
      field.getProto().getName(), schema.getProto().getDisplayName());
Kenton Varda's avatar
Kenton Varda committed
144 145
}

Kenton Varda's avatar
Kenton Varda committed
146 147
bool DynamicStruct::Builder::isSetInUnion(StructSchema::Field field) {
  auto proto = field.getProto();
148
  if (hasDiscriminantValue(proto)) {
Kenton Varda's avatar
Kenton Varda committed
149
    uint16_t discrim = builder.getDataField<uint16_t>(
150
        assumeDataOffset(schema.getProto().getStruct().getDiscriminantOffset()));
Kenton Varda's avatar
Kenton Varda committed
151
    return discrim == proto.getDiscriminantValue();
152
  } else {
Kenton Varda's avatar
Kenton Varda committed
153
    return true;
154
  }
Kenton Varda's avatar
Kenton Varda committed
155 156
}

Kenton Varda's avatar
Kenton Varda committed
157 158 159 160
void DynamicStruct::Builder::verifySetInUnion(StructSchema::Field field) {
  KJ_REQUIRE(isSetInUnion(field),
      "Tried to get() a union member which is not currently initialized.",
      field.getProto().getName(), schema.getProto().getDisplayName());
161 162
}

Kenton Varda's avatar
Kenton Varda committed
163
void DynamicStruct::Builder::setInUnion(StructSchema::Field field) {
164
  // If a union member, set the discriminant to match.
Kenton Varda's avatar
Kenton Varda committed
165
  auto proto = field.getProto();
166
  if (hasDiscriminantValue(proto)) {
167
    builder.setDataField<uint16_t>(
168
        assumeDataOffset(schema.getProto().getStruct().getDiscriminantOffset()),
Kenton Varda's avatar
Kenton Varda committed
169
        proto.getDiscriminantValue());
Kenton Varda's avatar
Kenton Varda committed
170
  }
171
}
Kenton Varda's avatar
Kenton Varda committed
172

Kenton Varda's avatar
Kenton Varda committed
173 174 175
DynamicValue::Reader DynamicStruct::Reader::get(StructSchema::Field field) const {
  KJ_REQUIRE(field.getContainingStruct() == schema, "`field` is not a field of this struct.");
  verifySetInUnion(field);
Kenton Varda's avatar
Kenton Varda committed
176

177
  auto type = field.getType();
Kenton Varda's avatar
Kenton Varda committed
178 179
  auto proto = field.getProto();
  switch (proto.which()) {
180 181
    case schema::Field::SLOT: {
      auto slot = proto.getSlot();
182 183 184 185 186

      // Note that the default value might be "anyPointer" even if the type is some poniter type
      // *other than* anyPointer. This happens with generics -- the field is actually a generic
      // parameter that has been bound, but the default value was of course compiled without any
      // binding available.
187
      auto dval = slot.getDefaultValue();
188 189

      switch (type.which()) {
Kenton Varda's avatar
Kenton Varda committed
190
        case schema::Type::VOID:
191
          return reader.getDataField<Void>(assumeDataOffset(slot.getOffset()));
Kenton Varda's avatar
Kenton Varda committed
192 193

#define HANDLE_TYPE(discrim, titleCase, type) \
Kenton Varda's avatar
Kenton Varda committed
194
        case schema::Type::discrim: \
195
          return reader.getDataField<type>( \
196
              assumeDataOffset(slot.getOffset()), \
197
              bitCast<_::Mask<type>>(dval.get##titleCase()));
198 199 200 201 202 203 204 205 206 207 208 209

        HANDLE_TYPE(BOOL, Bool, bool)
        HANDLE_TYPE(INT8, Int8, int8_t)
        HANDLE_TYPE(INT16, Int16, int16_t)
        HANDLE_TYPE(INT32, Int32, int32_t)
        HANDLE_TYPE(INT64, Int64, int64_t)
        HANDLE_TYPE(UINT8, Uint8, uint8_t)
        HANDLE_TYPE(UINT16, Uint16, uint16_t)
        HANDLE_TYPE(UINT32, Uint32, uint32_t)
        HANDLE_TYPE(UINT64, Uint64, uint64_t)
        HANDLE_TYPE(FLOAT32, Float32, float)
        HANDLE_TYPE(FLOAT64, Float64, double)
Kenton Varda's avatar
Kenton Varda committed
210 211 212

#undef HANDLE_TYPE

Kenton Varda's avatar
Kenton Varda committed
213
        case schema::Type::ENUM: {
214 215
          uint16_t typedDval = dval.getEnum();
          return DynamicEnum(type.asEnum(),
216
              reader.getDataField<uint16_t>(assumeDataOffset(slot.getOffset()), typedDval));
217 218
        }

Kenton Varda's avatar
Kenton Varda committed
219
        case schema::Type::TEXT: {
220
          Text::Reader typedDval = dval.isAnyPointer() ? Text::Reader() : dval.getText();
221 222 223
          return reader.getPointerField(assumePointerOffset(slot.getOffset()))
                       .getBlob<Text>(typedDval.begin(),
                           assumeMax<MAX_TEXT_SIZE>(typedDval.size()) * BYTES);
224 225
        }

Kenton Varda's avatar
Kenton Varda committed
226
        case schema::Type::DATA: {
227
          Data::Reader typedDval = dval.isAnyPointer() ? Data::Reader() : dval.getData();
228 229 230
          return reader.getPointerField(assumePointerOffset(slot.getOffset()))
                       .getBlob<Data>(typedDval.begin(),
                           assumeBits<BLOB_SIZE_BITS>(typedDval.size()) * BYTES);
231 232
        }

Kenton Varda's avatar
Kenton Varda committed
233
        case schema::Type::LIST: {
234 235
          auto elementType = type.asList().getElementType();
          return DynamicList::Reader(type.asList(),
236
              reader.getPointerField(assumePointerOffset(slot.getOffset()))
237 238
                    .getList(elementSizeFor(elementType.which()), dval.isAnyPointer() ? nullptr :
                        dval.getList().getAs<_::UncheckedMessage>()));
239 240
        }

241
        case schema::Type::STRUCT:
242
          return DynamicStruct::Reader(type.asStruct(),
243
              reader.getPointerField(assumePointerOffset(slot.getOffset()))
244 245
                    .getStruct(dval.isAnyPointer() ? nullptr :
                        dval.getStruct().getAs<_::UncheckedMessage>()));
246

247
        case schema::Type::ANY_POINTER:
248
          return AnyPointer::Reader(reader.getPointerField(assumePointerOffset(slot.getOffset())));
249

Kenton Varda's avatar
Kenton Varda committed
250
        case schema::Type::INTERFACE:
251
          return DynamicCapability::Client(type.asInterface(),
252
              reader.getPointerField(assumePointerOffset(slot.getOffset())).getCapability());
253
      }
Kenton Varda's avatar
Kenton Varda committed
254

Kenton Varda's avatar
Kenton Varda committed
255
      KJ_UNREACHABLE;
Kenton Varda's avatar
Kenton Varda committed
256
    }
Kenton Varda's avatar
Kenton Varda committed
257

Kenton Varda's avatar
Kenton Varda committed
258
    case schema::Field::GROUP:
259
      return DynamicStruct::Reader(type.asStruct(), reader);
Kenton Varda's avatar
Kenton Varda committed
260 261
  }

Kenton Varda's avatar
Kenton Varda committed
262
  KJ_UNREACHABLE;
Kenton Varda's avatar
Kenton Varda committed
263 264
}

Kenton Varda's avatar
Kenton Varda committed
265 266 267
DynamicValue::Builder DynamicStruct::Builder::get(StructSchema::Field field) {
  KJ_REQUIRE(field.getContainingStruct() == schema, "`field` is not a field of this struct.");
  verifySetInUnion(field);
Kenton Varda's avatar
Kenton Varda committed
268

Kenton Varda's avatar
Kenton Varda committed
269
  auto proto = field.getProto();
270
  auto type = field.getType();
Kenton Varda's avatar
Kenton Varda committed
271
  switch (proto.which()) {
272 273
    case schema::Field::SLOT: {
      auto slot = proto.getSlot();
274 275 276 277 278

      // Note that the default value might be "anyPointer" even if the type is some poniter type
      // *other than* anyPointer. This happens with generics -- the field is actually a generic
      // parameter that has been bound, but the default value was of course compiled without any
      // binding available.
279
      auto dval = slot.getDefaultValue();
280 281

      switch (type.which()) {
Kenton Varda's avatar
Kenton Varda committed
282
        case schema::Type::VOID:
283
          return builder.getDataField<Void>(assumeDataOffset(slot.getOffset()));
Kenton Varda's avatar
Kenton Varda committed
284 285

#define HANDLE_TYPE(discrim, titleCase, type) \
Kenton Varda's avatar
Kenton Varda committed
286
        case schema::Type::discrim: \
Kenton Varda's avatar
Kenton Varda committed
287
          return builder.getDataField<type>( \
288
              assumeDataOffset(slot.getOffset()), \
Kenton Varda's avatar
Kenton Varda committed
289
              bitCast<_::Mask<type>>(dval.get##titleCase()));
290 291 292 293 294 295 296 297 298 299 300 301

        HANDLE_TYPE(BOOL, Bool, bool)
        HANDLE_TYPE(INT8, Int8, int8_t)
        HANDLE_TYPE(INT16, Int16, int16_t)
        HANDLE_TYPE(INT32, Int32, int32_t)
        HANDLE_TYPE(INT64, Int64, int64_t)
        HANDLE_TYPE(UINT8, Uint8, uint8_t)
        HANDLE_TYPE(UINT16, Uint16, uint16_t)
        HANDLE_TYPE(UINT32, Uint32, uint32_t)
        HANDLE_TYPE(UINT64, Uint64, uint64_t)
        HANDLE_TYPE(FLOAT32, Float32, float)
        HANDLE_TYPE(FLOAT64, Float64, double)
Kenton Varda's avatar
Kenton Varda committed
302 303 304

#undef HANDLE_TYPE

Kenton Varda's avatar
Kenton Varda committed
305
        case schema::Type::ENUM: {
306 307
          uint16_t typedDval = dval.getEnum();
          return DynamicEnum(type.asEnum(),
308
              builder.getDataField<uint16_t>(assumeDataOffset(slot.getOffset()), typedDval));
309 310
        }

Kenton Varda's avatar
Kenton Varda committed
311
        case schema::Type::TEXT: {
312
          Text::Reader typedDval = dval.isAnyPointer() ? Text::Reader() : dval.getText();
313 314 315
          return builder.getPointerField(assumePointerOffset(slot.getOffset()))
                        .getBlob<Text>(typedDval.begin(),
                            assumeMax<MAX_TEXT_SIZE>(typedDval.size()) * BYTES);
316 317
        }

Kenton Varda's avatar
Kenton Varda committed
318
        case schema::Type::DATA: {
319
          Data::Reader typedDval = dval.isAnyPointer() ? Data::Reader() : dval.getData();
320 321 322
          return builder.getPointerField(assumePointerOffset(slot.getOffset()))
                        .getBlob<Data>(typedDval.begin(),
                            assumeBits<BLOB_SIZE_BITS>(typedDval.size()) * BYTES);
323 324
        }

Kenton Varda's avatar
Kenton Varda committed
325
        case schema::Type::LIST: {
326
          ListSchema listType = type.asList();
Kenton Varda's avatar
Kenton Varda committed
327
          if (listType.whichElementType() == schema::Type::STRUCT) {
Kenton Varda's avatar
Kenton Varda committed
328
            return DynamicList::Builder(listType,
329
                builder.getPointerField(assumePointerOffset(slot.getOffset()))
330
                       .getStructList(structSizeFromSchema(listType.getStructElementType()),
331 332
                                      dval.isAnyPointer() ? nullptr :
                                          dval.getList().getAs<_::UncheckedMessage>()));
333
          } else {
Kenton Varda's avatar
Kenton Varda committed
334
            return DynamicList::Builder(listType,
335
                builder.getPointerField(assumePointerOffset(slot.getOffset()))
336
                       .getList(elementSizeFor(listType.whichElementType()),
337 338
                                dval.isAnyPointer() ? nullptr :
                                    dval.getList().getAs<_::UncheckedMessage>()));
339 340
          }
        }
341

Kenton Varda's avatar
Kenton Varda committed
342
        case schema::Type::STRUCT: {
343
          auto structSchema = type.asStruct();
Kenton Varda's avatar
Kenton Varda committed
344
          return DynamicStruct::Builder(structSchema,
345
              builder.getPointerField(assumePointerOffset(slot.getOffset()))
346
                     .getStruct(structSizeFromSchema(structSchema),
347 348
                                dval.isAnyPointer() ? nullptr :
                                    dval.getStruct().getAs<_::UncheckedMessage>()));
349 350
        }

351
        case schema::Type::ANY_POINTER:
352 353
          return AnyPointer::Builder(
              builder.getPointerField(assumePointerOffset(slot.getOffset())));
354

Kenton Varda's avatar
Kenton Varda committed
355
        case schema::Type::INTERFACE:
356
          return DynamicCapability::Client(type.asInterface(),
357
              builder.getPointerField(assumePointerOffset(slot.getOffset())).getCapability());
358
      }
Kenton Varda's avatar
Kenton Varda committed
359

Kenton Varda's avatar
Kenton Varda committed
360
      KJ_UNREACHABLE;
Kenton Varda's avatar
Kenton Varda committed
361
    }
Kenton Varda's avatar
Kenton Varda committed
362

Kenton Varda's avatar
Kenton Varda committed
363
    case schema::Field::GROUP:
364
      return DynamicStruct::Builder(type.asStruct(), builder);
Kenton Varda's avatar
Kenton Varda committed
365 366
  }

Kenton Varda's avatar
Kenton Varda committed
367
  KJ_UNREACHABLE;
Kenton Varda's avatar
Kenton Varda committed
368
}
Kenton Varda's avatar
Kenton Varda committed
369

370
DynamicValue::Pipeline DynamicStruct::Pipeline::get(StructSchema::Field field) {
371 372 373
  KJ_REQUIRE(field.getContainingStruct() == schema, "`field` is not a field of this struct.");

  auto proto = field.getProto();
374
  KJ_REQUIRE(!hasDiscriminantValue(proto), "Can't pipeline on union members.");
375

376 377
  auto type = field.getType();

378 379 380 381 382 383
  switch (proto.which()) {
    case schema::Field::SLOT: {
      auto slot = proto.getSlot();

      switch (type.which()) {
        case schema::Type::STRUCT:
384
          return DynamicStruct::Pipeline(type.asStruct(),
385 386 387
              typeless.getPointerField(slot.getOffset()));

        case schema::Type::INTERFACE:
388
          return DynamicCapability::Client(type.asInterface(),
389 390
              typeless.getPointerField(slot.getOffset()).asCap());

391 392 393 394 395 396 397 398 399 400 401 402
        case schema::Type::ANY_POINTER:
          switch (type.whichAnyPointerKind()) {
            case schema::Type::AnyPointer::Unconstrained::STRUCT:
              return DynamicStruct::Pipeline(StructSchema(),
                  typeless.getPointerField(slot.getOffset()));
            case schema::Type::AnyPointer::Unconstrained::CAPABILITY:
              return DynamicCapability::Client(Capability::Client(
                  typeless.getPointerField(slot.getOffset()).asCap()));
            default:
              KJ_FAIL_REQUIRE("Can only pipeline on struct and interface fields.");
          }

403 404 405 406 407 408 409 410
        default:
          KJ_FAIL_REQUIRE("Can only pipeline on struct and interface fields.");
      }

      KJ_UNREACHABLE;
    }

    case schema::Field::GROUP:
411
      return DynamicStruct::Pipeline(type.asStruct(), typeless.noop());
412 413 414 415 416
  }

  KJ_UNREACHABLE;
}

Kenton Varda's avatar
Kenton Varda committed
417 418 419 420
bool DynamicStruct::Reader::has(StructSchema::Field field) const {
  KJ_REQUIRE(field.getContainingStruct() == schema, "`field` is not a field of this struct.");

  auto proto = field.getProto();
421
  if (hasDiscriminantValue(proto)) {
Kenton Varda's avatar
Kenton Varda committed
422
    uint16_t discrim = reader.getDataField<uint16_t>(
423
        assumeDataOffset(schema.getProto().getStruct().getDiscriminantOffset()));
Kenton Varda's avatar
Kenton Varda committed
424 425 426 427
    if (discrim != proto.getDiscriminantValue()) {
      // Field is not active in the union.
      return false;
    }
428
  }
Kenton Varda's avatar
Kenton Varda committed
429 430

  switch (proto.which()) {
431
    case schema::Field::SLOT:
Kenton Varda's avatar
Kenton Varda committed
432 433 434
      // Continue to below.
      break;

435 436
    case schema::Field::GROUP:
      return true;
Kenton Varda's avatar
Kenton Varda committed
437 438
  }

439
  auto slot = proto.getSlot();
440
  auto type = field.getType();
Kenton Varda's avatar
Kenton Varda committed
441 442

  switch (type.which()) {
Kenton Varda's avatar
Kenton Varda committed
443
    case schema::Type::VOID:
444 445 446 447 448 449 450 451 452 453 454 455 456 457
    case schema::Type::BOOL:
    case schema::Type::INT8:
    case schema::Type::INT16:
    case schema::Type::INT32:
    case schema::Type::INT64:
    case schema::Type::UINT8:
    case schema::Type::UINT16:
    case schema::Type::UINT32:
    case schema::Type::UINT64:
    case schema::Type::FLOAT32:
    case schema::Type::FLOAT64:
    case schema::Type::ENUM:
      // Primitive types are always present.
      return true;
Kenton Varda's avatar
Kenton Varda committed
458

Kenton Varda's avatar
Kenton Varda committed
459 460 461 462
    case schema::Type::TEXT:
    case schema::Type::DATA:
    case schema::Type::LIST:
    case schema::Type::STRUCT:
463
    case schema::Type::ANY_POINTER:
Kenton Varda's avatar
Kenton Varda committed
464
    case schema::Type::INTERFACE:
465
      return !reader.getPointerField(assumePointerOffset(slot.getOffset())).isNull();
Kenton Varda's avatar
Kenton Varda committed
466 467 468 469 470 471 472 473 474 475 476 477
  }

  // Unknown type.  As far as we know, it isn't set.
  return false;
}

kj::Maybe<StructSchema::Field> DynamicStruct::Reader::which() const {
  auto structProto = schema.getProto().getStruct();
  if (structProto.getDiscriminantCount() == 0) {
    return nullptr;
  }

478 479
  uint16_t discrim = reader.getDataField<uint16_t>(
      assumeDataOffset(structProto.getDiscriminantOffset()));
Kenton Varda's avatar
Kenton Varda committed
480 481 482 483 484 485 486 487 488
  return schema.getFieldByDiscriminant(discrim);
}

kj::Maybe<StructSchema::Field> DynamicStruct::Builder::which() {
  auto structProto = schema.getProto().getStruct();
  if (structProto.getDiscriminantCount() == 0) {
    return nullptr;
  }

489 490
  uint16_t discrim = builder.getDataField<uint16_t>(
      assumeDataOffset(structProto.getDiscriminantOffset()));
Kenton Varda's avatar
Kenton Varda committed
491 492 493 494 495 496 497 498
  return schema.getFieldByDiscriminant(discrim);
}

void DynamicStruct::Builder::set(StructSchema::Field field, const DynamicValue::Reader& value) {
  KJ_REQUIRE(field.getContainingStruct() == schema, "`field` is not a field of this struct.");
  setInUnion(field);

  auto proto = field.getProto();
499
  auto type = field.getType();
Kenton Varda's avatar
Kenton Varda committed
500
  switch (proto.which()) {
501 502 503
    case schema::Field::SLOT: {
      auto slot = proto.getSlot();
      auto dval = slot.getDefaultValue();
Kenton Varda's avatar
Kenton Varda committed
504

505
      switch (type.which()) {
Kenton Varda's avatar
Kenton Varda committed
506
        case schema::Type::VOID:
507
          builder.setDataField<Void>(assumeDataOffset(slot.getOffset()), value.as<Void>());
508
          return;
Kenton Varda's avatar
Kenton Varda committed
509

510
#define HANDLE_TYPE(discrim, titleCase, type) \
Kenton Varda's avatar
Kenton Varda committed
511
        case schema::Type::discrim: \
512
          builder.setDataField<type>( \
513
              assumeDataOffset(slot.getOffset()), value.as<type>(), \
Kenton Varda's avatar
Kenton Varda committed
514
              bitCast<_::Mask<type> >(dval.get##titleCase())); \
515 516 517 518 519 520 521 522 523 524 525 526 527
          return;

        HANDLE_TYPE(BOOL, Bool, bool)
        HANDLE_TYPE(INT8, Int8, int8_t)
        HANDLE_TYPE(INT16, Int16, int16_t)
        HANDLE_TYPE(INT32, Int32, int32_t)
        HANDLE_TYPE(INT64, Int64, int64_t)
        HANDLE_TYPE(UINT8, Uint8, uint8_t)
        HANDLE_TYPE(UINT16, Uint16, uint16_t)
        HANDLE_TYPE(UINT32, Uint32, uint32_t)
        HANDLE_TYPE(UINT64, Uint64, uint64_t)
        HANDLE_TYPE(FLOAT32, Float32, float)
        HANDLE_TYPE(FLOAT64, Float64, double)
Kenton Varda's avatar
Kenton Varda committed
528 529 530

#undef HANDLE_TYPE

Kenton Varda's avatar
Kenton Varda committed
531
        case schema::Type::ENUM: {
532
          uint16_t rawValue;
533
          auto enumSchema = type.asEnum();
534 535 536
          if (value.getType() == DynamicValue::TEXT) {
            // Convert from text.
            rawValue = enumSchema.getEnumerantByName(value.as<Text>()).getOrdinal();
537 538
          } else if (value.getType() == DynamicValue::INT ||
                     value.getType() == DynamicValue::UINT) {
539
            rawValue = value.as<uint16_t>();
Kenton Varda's avatar
Kenton Varda committed
540
          } else {
541
            DynamicEnum enumValue = value.as<DynamicEnum>();
542
            KJ_REQUIRE(enumValue.getSchema() == enumSchema, "Value type mismatch.") {
543 544 545 546
              return;
            }
            rawValue = enumValue.getRaw();
          }
547
          builder.setDataField<uint16_t>(assumeDataOffset(slot.getOffset()), rawValue,
Kenton Varda's avatar
Kenton Varda committed
548
                                         dval.getEnum());
549
          return;
550
        }
551

Kenton Varda's avatar
Kenton Varda committed
552
        case schema::Type::TEXT:
553 554
          builder.getPointerField(assumePointerOffset(slot.getOffset()))
                 .setBlob<Text>(value.as<Text>());
555 556
          return;

Kenton Varda's avatar
Kenton Varda committed
557
        case schema::Type::DATA:
558 559
          builder.getPointerField(assumePointerOffset(slot.getOffset()))
                 .setBlob<Data>(value.as<Data>());
560 561
          return;

562
        case schema::Type::LIST: {
563
          ListSchema listType = type.asList();
564 565 566 567
          auto listValue = value.as<DynamicList>();
          KJ_REQUIRE(listValue.getSchema() == listType, "Value type mismatch.") {
            return;
          }
568 569
          builder.getPointerField(assumePointerOffset(slot.getOffset()))
                 .setList(listValue.reader);
570
          return;
571
        }
572

573
        case schema::Type::STRUCT: {
574
          auto structType = type.asStruct();
575 576 577 578
          auto structValue = value.as<DynamicStruct>();
          KJ_REQUIRE(structValue.getSchema() == structType, "Value type mismatch.") {
            return;
          }
579 580
          builder.getPointerField(assumePointerOffset(slot.getOffset()))
                 .setStruct(structValue.reader);
581
          return;
582
        }
583

584
        case schema::Type::ANY_POINTER: {
585 586
          auto target = AnyPointer::Builder(
              builder.getPointerField(assumePointerOffset(slot.getOffset())));
587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619

          switch (value.getType()) {
            case DynamicValue::Type::TEXT:
              target.setAs<Text>(value.as<Text>());
              return;
            case DynamicValue::Type::DATA:
              target.setAs<Data>(value.as<Data>());
              return;
            case DynamicValue::Type::LIST:
              target.setAs<DynamicList>(value.as<DynamicList>());
              return;
            case DynamicValue::Type::STRUCT:
              target.setAs<DynamicStruct>(value.as<DynamicStruct>());
              return;
            case DynamicValue::Type::CAPABILITY:
              target.setAs<DynamicCapability>(value.as<DynamicCapability>());
              return;
            case DynamicValue::Type::ANY_POINTER:
              target.set(value.as<AnyPointer>());
              return;

            case DynamicValue::Type::UNKNOWN:
            case DynamicValue::Type::VOID:
            case DynamicValue::Type::BOOL:
            case DynamicValue::Type::INT:
            case DynamicValue::Type::UINT:
            case DynamicValue::Type::FLOAT:
            case DynamicValue::Type::ENUM:
              KJ_FAIL_ASSERT("Value type mismatch; expected AnyPointer");
          }

          KJ_UNREACHABLE;
        }
620

621
        case schema::Type::INTERFACE: {
622
          auto interfaceType = type.asInterface();
623 624 625 626
          auto capability = value.as<DynamicCapability>();
          KJ_REQUIRE(capability.getSchema().extends(interfaceType), "Value type mismatch.") {
            return;
          }
627 628
          builder.getPointerField(assumePointerOffset(slot.getOffset()))
                 .setCapability(kj::mv(capability.hook));
629
          return;
630
        }
631
      }
Kenton Varda's avatar
Kenton Varda committed
632

Kenton Varda's avatar
Kenton Varda committed
633 634 635
      KJ_UNREACHABLE;
    }

Kenton Varda's avatar
Kenton Varda committed
636
    case schema::Field::GROUP: {
Kenton Varda's avatar
Kenton Varda committed
637 638 639 640 641 642 643 644 645 646 647
      auto src = value.as<DynamicStruct>();
      auto dst = init(field).as<DynamicStruct>();

      KJ_IF_MAYBE(unionField, src.which()) {
        dst.set(*unionField, src.get(*unionField));
      }

      for (auto field: src.schema.getNonUnionFields()) {
        if (src.has(field)) {
          dst.set(field, src.get(field));
        }
648
      }
Kenton Varda's avatar
Kenton Varda committed
649
    }
650
  }
Kenton Varda's avatar
Kenton Varda committed
651

Kenton Varda's avatar
Kenton Varda committed
652
  KJ_UNREACHABLE;
653
}
Kenton Varda's avatar
Kenton Varda committed
654

Kenton Varda's avatar
Kenton Varda committed
655 656 657 658 659
DynamicValue::Builder DynamicStruct::Builder::init(StructSchema::Field field) {
  KJ_REQUIRE(field.getContainingStruct() == schema, "`field` is not a field of this struct.");
  setInUnion(field);

  auto proto = field.getProto();
660
  auto type = field.getType();
Kenton Varda's avatar
Kenton Varda committed
661 662

  switch (proto.which()) {
663 664
    case schema::Field::SLOT: {
      auto slot = proto.getSlot();
665 666
      switch (type.which()) {
        case schema::Type::STRUCT: {
667
          auto subSchema = type.asStruct();
668
          return DynamicStruct::Builder(subSchema,
669
              builder.getPointerField(assumePointerOffset(slot.getOffset()))
670 671
                     .initStruct(structSizeFromSchema(subSchema)));
        }
672
        case schema::Type::ANY_POINTER: {
673
          auto pointer = builder.getPointerField(assumePointerOffset(slot.getOffset()));
674
          pointer.clear();
675
          return AnyPointer::Builder(pointer);
676 677 678 679
        }
        default:
          KJ_FAIL_REQUIRE("init() without a size is only valid for struct and object fields.");
      }
Kenton Varda's avatar
Kenton Varda committed
680 681
    }

Kenton Varda's avatar
Kenton Varda committed
682
    case schema::Field::GROUP: {
Kenton Varda's avatar
Kenton Varda committed
683
      clear(field);
684
      return DynamicStruct::Builder(type.asStruct(), builder);
Kenton Varda's avatar
Kenton Varda committed
685 686 687 688 689
    }
  }

  KJ_UNREACHABLE;
}
Kenton Varda's avatar
Kenton Varda committed
690

Kenton Varda's avatar
Kenton Varda committed
691 692 693 694 695
DynamicValue::Builder DynamicStruct::Builder::init(StructSchema::Field field, uint size) {
  KJ_REQUIRE(field.getContainingStruct() == schema, "`field` is not a field of this struct.");
  setInUnion(field);

  auto proto = field.getProto();
696
  auto type = field.getType();
Kenton Varda's avatar
Kenton Varda committed
697 698

  switch (proto.which()) {
699 700
    case schema::Field::SLOT: {
      auto slot = proto.getSlot();
701
      switch (type.which()) {
Kenton Varda's avatar
Kenton Varda committed
702
        case schema::Type::LIST: {
703
          auto listType = type.asList();
Kenton Varda's avatar
Kenton Varda committed
704
          if (listType.whichElementType() == schema::Type::STRUCT) {
Kenton Varda's avatar
Kenton Varda committed
705
            return DynamicList::Builder(listType,
706
                builder.getPointerField(assumePointerOffset(slot.getOffset()))
707
                       .initStructList(bounded(size) * ELEMENTS,
708
                                       structSizeFromSchema(listType.getStructElementType())));
Kenton Varda's avatar
Kenton Varda committed
709 710
          } else {
            return DynamicList::Builder(listType,
711 712
                builder.getPointerField(assumePointerOffset(slot.getOffset()))
                       .initList(elementSizeFor(listType.whichElementType()),
713
                                 bounded(size) * ELEMENTS));
Kenton Varda's avatar
Kenton Varda committed
714 715
          }
        }
Kenton Varda's avatar
Kenton Varda committed
716
        case schema::Type::TEXT:
717
          return builder.getPointerField(assumePointerOffset(slot.getOffset()))
718
                        .initBlob<Text>(bounded(size) * BYTES);
Kenton Varda's avatar
Kenton Varda committed
719
        case schema::Type::DATA:
720
          return builder.getPointerField(assumePointerOffset(slot.getOffset()))
721
                        .initBlob<Data>(bounded(size) * BYTES);
722
        default:
723
          KJ_FAIL_REQUIRE(
724 725
              "init() with size is only valid for list, text, or data fields.",
              (uint)type.which());
726 727
          break;
      }
728
    }
Kenton Varda's avatar
Kenton Varda committed
729

Kenton Varda's avatar
Kenton Varda committed
730
    case schema::Field::GROUP:
Kenton Varda's avatar
Kenton Varda committed
731
      KJ_FAIL_REQUIRE("init() with size is only valid for list, text, or data fields.");
Kenton Varda's avatar
Kenton Varda committed
732 733
  }

Kenton Varda's avatar
Kenton Varda committed
734
  KJ_UNREACHABLE;
Kenton Varda's avatar
Kenton Varda committed
735 736
}

737 738 739 740 741 742
void DynamicStruct::Builder::adopt(StructSchema::Field field, Orphan<DynamicValue>&& orphan) {
  KJ_REQUIRE(field.getContainingStruct() == schema, "`field` is not a field of this struct.");
  setInUnion(field);

  auto proto = field.getProto();
  switch (proto.which()) {
743 744
    case schema::Field::SLOT: {
      auto slot = proto.getSlot();
745
      auto type = field.getType();
746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764

      switch (type.which()) {
        case schema::Type::VOID:
        case schema::Type::BOOL:
        case schema::Type::INT8:
        case schema::Type::INT16:
        case schema::Type::INT32:
        case schema::Type::INT64:
        case schema::Type::UINT8:
        case schema::Type::UINT16:
        case schema::Type::UINT32:
        case schema::Type::UINT64:
        case schema::Type::FLOAT32:
        case schema::Type::FLOAT64:
        case schema::Type::ENUM:
          set(field, orphan.getReader());
          return;

        case schema::Type::TEXT:
765
          KJ_REQUIRE(orphan.getType() == DynamicValue::TEXT, "Value type mismatch.");
766 767 768
          break;

        case schema::Type::DATA:
769
          KJ_REQUIRE(orphan.getType() == DynamicValue::DATA, "Value type mismatch.");
770 771 772
          break;

        case schema::Type::LIST: {
773
          ListSchema listType = type.asList();
774
          KJ_REQUIRE(orphan.getType() == DynamicValue::LIST && orphan.listSchema == listType,
775 776 777
                     "Value type mismatch.") {
            return;
          }
778 779 780 781
          break;
        }

        case schema::Type::STRUCT: {
782
          auto structType = type.asStruct();
783
          KJ_REQUIRE(orphan.getType() == DynamicValue::STRUCT && orphan.structSchema == structType,
784 785 786
                     "Value type mismatch.") {
            return;
          }
787 788 789
          break;
        }

790
        case schema::Type::ANY_POINTER:
791 792
          KJ_REQUIRE(orphan.getType() == DynamicValue::STRUCT ||
                     orphan.getType() == DynamicValue::LIST ||
793 794
                     orphan.getType() == DynamicValue::TEXT ||
                     orphan.getType() == DynamicValue::DATA ||
795
                     orphan.getType() == DynamicValue::CAPABILITY ||
796
                     orphan.getType() == DynamicValue::ANY_POINTER,
797 798 799
                     "Value type mismatch.") {
            return;
          }
800 801
          break;

802
        case schema::Type::INTERFACE: {
803
          auto interfaceType = type.asInterface();
804 805 806 807 808
          KJ_REQUIRE(orphan.getType() == DynamicValue::CAPABILITY &&
                     orphan.interfaceSchema.extends(interfaceType),
                     "Value type mismatch.") {
            return;
          }
809
          break;
810
        }
811 812
      }

813
      builder.getPointerField(assumePointerOffset(slot.getOffset())).adopt(kj::mv(orphan.builder));
814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845
      return;
    }

    case schema::Field::GROUP:
      // Have to transfer fields.
      auto src = orphan.get().as<DynamicStruct>();
      auto dst = init(field).as<DynamicStruct>();

      KJ_REQUIRE(orphan.getType() == DynamicValue::STRUCT && orphan.structSchema == dst.getSchema(),
                 "Value type mismatch.");

      KJ_IF_MAYBE(unionField, src.which()) {
        dst.adopt(*unionField, src.disown(*unionField));
      }

      for (auto field: src.schema.getNonUnionFields()) {
        if (src.has(field)) {
          dst.adopt(field, src.disown(field));
        }
      }

      return;
  }

  KJ_UNREACHABLE;
}

Orphan<DynamicValue> DynamicStruct::Builder::disown(StructSchema::Field field) {
  // We end up calling get(field) below, so we don't need to validate `field` here.

  auto proto = field.getProto();
  switch (proto.which()) {
846 847
    case schema::Field::SLOT: {
      auto slot = proto.getSlot();
848

849
      switch (field.getType().which()) {
850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871
        case schema::Type::VOID:
        case schema::Type::BOOL:
        case schema::Type::INT8:
        case schema::Type::INT16:
        case schema::Type::INT32:
        case schema::Type::INT64:
        case schema::Type::UINT8:
        case schema::Type::UINT16:
        case schema::Type::UINT32:
        case schema::Type::UINT64:
        case schema::Type::FLOAT32:
        case schema::Type::FLOAT64:
        case schema::Type::ENUM: {
          auto result = Orphan<DynamicValue>(get(field), _::OrphanBuilder());
          clear(field);
          return kj::mv(result);
        }

        case schema::Type::TEXT:
        case schema::Type::DATA:
        case schema::Type::LIST:
        case schema::Type::STRUCT:
872
        case schema::Type::ANY_POINTER:
873 874
        case schema::Type::INTERFACE: {
          auto value = get(field);
875
          return Orphan<DynamicValue>(
876
              value, builder.getPointerField(assumePointerOffset(slot.getOffset())).disown());
877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910
        }
      }
      KJ_UNREACHABLE;
    }

    case schema::Field::GROUP: {
      // We have to allocate new space for the group, unfortunately.
      auto src = get(field).as<DynamicStruct>();

      Orphan<DynamicStruct> result =
          Orphanage::getForMessageContaining(*this).newOrphan(src.getSchema());
      auto dst = result.get();

      KJ_IF_MAYBE(unionField, src.which()) {
        dst.adopt(*unionField, src.disown(*unionField));
      }

      // We need to explicitly reset the union to its default field.
      KJ_IF_MAYBE(unionField, src.schema.getFieldByDiscriminant(0)) {
        src.clear(*unionField);
      }

      for (auto field: src.schema.getNonUnionFields()) {
        if (src.has(field)) {
          dst.adopt(field, src.disown(field));
        }
      }

      return kj::mv(result);
    }
  }

  KJ_UNREACHABLE;
}
Kenton Varda's avatar
Kenton Varda committed
911 912 913 914 915 916

void DynamicStruct::Builder::clear(StructSchema::Field field) {
  KJ_REQUIRE(field.getContainingStruct() == schema, "`field` is not a field of this struct.");
  setInUnion(field);

  auto proto = field.getProto();
917
  auto type = field.getType();
Kenton Varda's avatar
Kenton Varda committed
918
  switch (proto.which()) {
919 920
    case schema::Field::SLOT: {
      auto slot = proto.getSlot();
Kenton Varda's avatar
Kenton Varda committed
921 922

      switch (type.which()) {
Kenton Varda's avatar
Kenton Varda committed
923
        case schema::Type::VOID:
924
          builder.setDataField<Void>(assumeDataOffset(slot.getOffset()), VOID);
Kenton Varda's avatar
Kenton Varda committed
925 926 927
          return;

#define HANDLE_TYPE(discrim, type) \
Kenton Varda's avatar
Kenton Varda committed
928
        case schema::Type::discrim: \
929
          builder.setDataField<type>(assumeDataOffset(slot.getOffset()), 0); \
Kenton Varda's avatar
Kenton Varda committed
930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946
          return;

        HANDLE_TYPE(BOOL, bool)
        HANDLE_TYPE(INT8, uint8_t)
        HANDLE_TYPE(INT16, uint16_t)
        HANDLE_TYPE(INT32, uint32_t)
        HANDLE_TYPE(INT64, uint64_t)
        HANDLE_TYPE(UINT8, uint8_t)
        HANDLE_TYPE(UINT16, uint16_t)
        HANDLE_TYPE(UINT32, uint32_t)
        HANDLE_TYPE(UINT64, uint64_t)
        HANDLE_TYPE(FLOAT32, uint32_t)
        HANDLE_TYPE(FLOAT64, uint64_t)
        HANDLE_TYPE(ENUM, uint16_t)

#undef HANDLE_TYPE

Kenton Varda's avatar
Kenton Varda committed
947 948 949 950
        case schema::Type::TEXT:
        case schema::Type::DATA:
        case schema::Type::LIST:
        case schema::Type::STRUCT:
951
        case schema::Type::ANY_POINTER:
Kenton Varda's avatar
Kenton Varda committed
952
        case schema::Type::INTERFACE:
953
          builder.getPointerField(assumePointerOffset(slot.getOffset())).clear();
Kenton Varda's avatar
Kenton Varda committed
954 955 956 957 958 959
          return;
      }

      KJ_UNREACHABLE;
    }

Kenton Varda's avatar
Kenton Varda committed
960
    case schema::Field::GROUP: {
961
      DynamicStruct::Builder group(type.asStruct(), builder);
962 963 964

      // We clear the union field with discriminant 0 rather than the one that is set because
      // we want the union to end up with its default field active.
Kenton Varda's avatar
Kenton Varda committed
965 966 967
      KJ_IF_MAYBE(unionField, group.schema.getFieldByDiscriminant(0)) {
        group.clear(*unionField);
      }
968

Kenton Varda's avatar
Kenton Varda committed
969 970 971
      for (auto subField: group.schema.getNonUnionFields()) {
        group.clear(subField);
      }
972
      return;
Kenton Varda's avatar
Kenton Varda committed
973 974
    }
  }
975

Kenton Varda's avatar
Kenton Varda committed
976 977 978 979 980 981 982 983 984
  KJ_UNREACHABLE;
}

DynamicValue::Reader DynamicStruct::Reader::get(kj::StringPtr name) const {
  return get(schema.getFieldByName(name));
}
DynamicValue::Builder DynamicStruct::Builder::get(kj::StringPtr name) {
  return get(schema.getFieldByName(name));
}
985
DynamicValue::Pipeline DynamicStruct::Pipeline::get(kj::StringPtr name) {
986 987
  return get(schema.getFieldByName(name));
}
Kenton Varda's avatar
Kenton Varda committed
988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
bool DynamicStruct::Reader::has(kj::StringPtr name) const {
  return has(schema.getFieldByName(name));
}
bool DynamicStruct::Builder::has(kj::StringPtr name) {
  return has(schema.getFieldByName(name));
}
void DynamicStruct::Builder::set(kj::StringPtr name, const DynamicValue::Reader& value) {
  set(schema.getFieldByName(name), value);
}
void DynamicStruct::Builder::set(kj::StringPtr name,
                                 std::initializer_list<DynamicValue::Reader> value) {
  auto list = init(name, value.size()).as<DynamicList>();
  uint i = 0;
  for (auto element: value) {
    list.set(i++, element);
  }
}
DynamicValue::Builder DynamicStruct::Builder::init(kj::StringPtr name) {
  return init(schema.getFieldByName(name));
}
DynamicValue::Builder DynamicStruct::Builder::init(kj::StringPtr name, uint size) {
  return init(schema.getFieldByName(name), size);
}
1011 1012 1013 1014 1015 1016
void DynamicStruct::Builder::adopt(kj::StringPtr name, Orphan<DynamicValue>&& orphan) {
  adopt(schema.getFieldByName(name), kj::mv(orphan));
}
Orphan<DynamicValue> DynamicStruct::Builder::disown(kj::StringPtr name) {
  return disown(schema.getFieldByName(name));
}
Kenton Varda's avatar
Kenton Varda committed
1017
void DynamicStruct::Builder::clear(kj::StringPtr name) {
1018
  clear(schema.getFieldByName(name));
Kenton Varda's avatar
Kenton Varda committed
1019
}
Kenton Varda's avatar
Kenton Varda committed
1020 1021 1022

// =======================================================================================

1023
DynamicValue::Reader DynamicList::Reader::operator[](uint index) const {
1024
  KJ_REQUIRE(index < size(), "List index out-of-bounds.");
Kenton Varda's avatar
Kenton Varda committed
1025

1026
  switch (schema.whichElementType()) {
Kenton Varda's avatar
Kenton Varda committed
1027
#define HANDLE_TYPE(name, discrim, typeName) \
Kenton Varda's avatar
Kenton Varda committed
1028
    case schema::Type::discrim: \
1029
      return reader.getDataElement<typeName>(bounded(index) * ELEMENTS);
1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042

    HANDLE_TYPE(void, VOID, Void)
    HANDLE_TYPE(bool, BOOL, bool)
    HANDLE_TYPE(int8, INT8, int8_t)
    HANDLE_TYPE(int16, INT16, int16_t)
    HANDLE_TYPE(int32, INT32, int32_t)
    HANDLE_TYPE(int64, INT64, int64_t)
    HANDLE_TYPE(uint8, UINT8, uint8_t)
    HANDLE_TYPE(uint16, UINT16, uint16_t)
    HANDLE_TYPE(uint32, UINT32, uint32_t)
    HANDLE_TYPE(uint64, UINT64, uint64_t)
    HANDLE_TYPE(float32, FLOAT32, float)
    HANDLE_TYPE(float64, FLOAT64, double)
Kenton Varda's avatar
Kenton Varda committed
1043 1044
#undef HANDLE_TYPE

Kenton Varda's avatar
Kenton Varda committed
1045
    case schema::Type::TEXT:
1046
      return reader.getPointerElement(bounded(index) * ELEMENTS)
1047
                   .getBlob<Text>(nullptr, ZERO * BYTES);
Kenton Varda's avatar
Kenton Varda committed
1048
    case schema::Type::DATA:
1049
      return reader.getPointerElement(bounded(index) * ELEMENTS)
1050
                   .getBlob<Data>(nullptr, ZERO * BYTES);
Kenton Varda's avatar
Kenton Varda committed
1051

Kenton Varda's avatar
Kenton Varda committed
1052
    case schema::Type::LIST: {
1053
      auto elementType = schema.getListElementType();
1054
      return DynamicList::Reader(elementType,
1055
          reader.getPointerElement(bounded(index) * ELEMENTS)
1056
                .getList(elementSizeFor(elementType.whichElementType()), nullptr));
1057
    }
Kenton Varda's avatar
Kenton Varda committed
1058

Kenton Varda's avatar
Kenton Varda committed
1059
    case schema::Type::STRUCT:
1060
      return DynamicStruct::Reader(schema.getStructElementType(),
1061
                                   reader.getStructElement(bounded(index) * ELEMENTS));
Kenton Varda's avatar
Kenton Varda committed
1062

Kenton Varda's avatar
Kenton Varda committed
1063
    case schema::Type::ENUM:
1064
      return DynamicEnum(schema.getEnumElementType(),
1065
                         reader.getDataElement<uint16_t>(bounded(index) * ELEMENTS));
Kenton Varda's avatar
Kenton Varda committed
1066

1067
    case schema::Type::ANY_POINTER:
1068
      return AnyPointer::Reader(reader.getPointerElement(bounded(index) * ELEMENTS));
Kenton Varda's avatar
Kenton Varda committed
1069

Kenton Varda's avatar
Kenton Varda committed
1070
    case schema::Type::INTERFACE:
1071
      return DynamicCapability::Client(schema.getInterfaceElementType(),
1072
                                       reader.getPointerElement(bounded(index) * ELEMENTS)
1073
                                             .getCapability());
Kenton Varda's avatar
Kenton Varda committed
1074
  }
1075

1076
  return nullptr;
Kenton Varda's avatar
Kenton Varda committed
1077 1078
}

1079
DynamicValue::Builder DynamicList::Builder::operator[](uint index) {
1080
  KJ_REQUIRE(index < size(), "List index out-of-bounds.");
Kenton Varda's avatar
Kenton Varda committed
1081

1082
  switch (schema.whichElementType()) {
Kenton Varda's avatar
Kenton Varda committed
1083
#define HANDLE_TYPE(name, discrim, typeName) \
Kenton Varda's avatar
Kenton Varda committed
1084
    case schema::Type::discrim: \
1085
      return builder.getDataElement<typeName>(bounded(index) * ELEMENTS);
1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098

    HANDLE_TYPE(void, VOID, Void)
    HANDLE_TYPE(bool, BOOL, bool)
    HANDLE_TYPE(int8, INT8, int8_t)
    HANDLE_TYPE(int16, INT16, int16_t)
    HANDLE_TYPE(int32, INT32, int32_t)
    HANDLE_TYPE(int64, INT64, int64_t)
    HANDLE_TYPE(uint8, UINT8, uint8_t)
    HANDLE_TYPE(uint16, UINT16, uint16_t)
    HANDLE_TYPE(uint32, UINT32, uint32_t)
    HANDLE_TYPE(uint64, UINT64, uint64_t)
    HANDLE_TYPE(float32, FLOAT32, float)
    HANDLE_TYPE(float64, FLOAT64, double)
Kenton Varda's avatar
Kenton Varda committed
1099 1100
#undef HANDLE_TYPE

Kenton Varda's avatar
Kenton Varda committed
1101
    case schema::Type::TEXT:
1102
      return builder.getPointerElement(bounded(index) * ELEMENTS)
1103
                    .getBlob<Text>(nullptr, ZERO * BYTES);
Kenton Varda's avatar
Kenton Varda committed
1104
    case schema::Type::DATA:
1105
      return builder.getPointerElement(bounded(index) * ELEMENTS)
1106
                    .getBlob<Data>(nullptr, ZERO * BYTES);
Kenton Varda's avatar
Kenton Varda committed
1107

Kenton Varda's avatar
Kenton Varda committed
1108
    case schema::Type::LIST: {
1109
      ListSchema elementType = schema.getListElementType();
Kenton Varda's avatar
Kenton Varda committed
1110
      if (elementType.whichElementType() == schema::Type::STRUCT) {
1111
        return DynamicList::Builder(elementType,
1112
            builder.getPointerElement(bounded(index) * ELEMENTS)
1113 1114
                   .getStructList(structSizeFromSchema(elementType.getStructElementType()),
                                  nullptr));
1115
      } else {
1116
        return DynamicList::Builder(elementType,
1117
            builder.getPointerElement(bounded(index) * ELEMENTS)
1118
                   .getList(elementSizeFor(elementType.whichElementType()), nullptr));
1119 1120
      }
    }
Kenton Varda's avatar
Kenton Varda committed
1121

Kenton Varda's avatar
Kenton Varda committed
1122
    case schema::Type::STRUCT:
1123
      return DynamicStruct::Builder(schema.getStructElementType(),
1124
                                    builder.getStructElement(bounded(index) * ELEMENTS));
Kenton Varda's avatar
Kenton Varda committed
1125

Kenton Varda's avatar
Kenton Varda committed
1126
    case schema::Type::ENUM:
1127
      return DynamicEnum(schema.getEnumElementType(),
1128
                         builder.getDataElement<uint16_t>(bounded(index) * ELEMENTS));
Kenton Varda's avatar
Kenton Varda committed
1129

1130 1131
    case schema::Type::ANY_POINTER:
      KJ_FAIL_ASSERT("List(AnyPointer) not supported.");
1132
      return nullptr;
Kenton Varda's avatar
Kenton Varda committed
1133

Kenton Varda's avatar
Kenton Varda committed
1134
    case schema::Type::INTERFACE:
1135
      return DynamicCapability::Client(schema.getInterfaceElementType(),
1136
                                       builder.getPointerElement(bounded(index) * ELEMENTS)
1137
                                              .getCapability());
Kenton Varda's avatar
Kenton Varda committed
1138
  }
1139

1140
  return nullptr;
Kenton Varda's avatar
Kenton Varda committed
1141 1142
}

1143
void DynamicList::Builder::set(uint index, const DynamicValue::Reader& value) {
1144
  KJ_REQUIRE(index < size(), "List index out-of-bounds.") {
1145 1146
    return;
  }
Kenton Varda's avatar
Kenton Varda committed
1147

1148
  switch (schema.whichElementType()) {
Kenton Varda's avatar
Kenton Varda committed
1149
#define HANDLE_TYPE(name, discrim, typeName) \
Kenton Varda's avatar
Kenton Varda committed
1150
    case schema::Type::discrim: \
1151
      builder.setDataElement<typeName>(bounded(index) * ELEMENTS, value.as<typeName>()); \
1152
      return;
Kenton Varda's avatar
Kenton Varda committed
1153

1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165
    HANDLE_TYPE(void, VOID, Void)
    HANDLE_TYPE(bool, BOOL, bool)
    HANDLE_TYPE(int8, INT8, int8_t)
    HANDLE_TYPE(int16, INT16, int16_t)
    HANDLE_TYPE(int32, INT32, int32_t)
    HANDLE_TYPE(int64, INT64, int64_t)
    HANDLE_TYPE(uint8, UINT8, uint8_t)
    HANDLE_TYPE(uint16, UINT16, uint16_t)
    HANDLE_TYPE(uint32, UINT32, uint32_t)
    HANDLE_TYPE(uint64, UINT64, uint64_t)
    HANDLE_TYPE(float32, FLOAT32, float)
    HANDLE_TYPE(float64, FLOAT64, double)
Kenton Varda's avatar
Kenton Varda committed
1166 1167
#undef HANDLE_TYPE

Kenton Varda's avatar
Kenton Varda committed
1168
    case schema::Type::TEXT:
1169
      builder.getPointerElement(bounded(index) * ELEMENTS).setBlob<Text>(value.as<Text>());
1170
      return;
Kenton Varda's avatar
Kenton Varda committed
1171
    case schema::Type::DATA:
1172
      builder.getPointerElement(bounded(index) * ELEMENTS).setBlob<Data>(value.as<Data>());
1173
      return;
Kenton Varda's avatar
Kenton Varda committed
1174

Kenton Varda's avatar
Kenton Varda committed
1175
    case schema::Type::LIST: {
1176 1177 1178 1179
      auto listValue = value.as<DynamicList>();
      KJ_REQUIRE(listValue.getSchema() == schema.getListElementType(), "Value type mismatch.") {
        return;
      }
1180
      builder.getPointerElement(bounded(index) * ELEMENTS).setList(listValue.reader);
1181
      return;
1182
    }
Kenton Varda's avatar
Kenton Varda committed
1183

1184
    case schema::Type::STRUCT: {
1185 1186 1187 1188
      auto structValue = value.as<DynamicStruct>();
      KJ_REQUIRE(structValue.getSchema() == schema.getStructElementType(), "Value type mismatch.") {
        return;
      }
1189
      builder.getStructElement(bounded(index) * ELEMENTS).copyContentFrom(structValue.reader);
1190 1191
      return;
    }
Kenton Varda's avatar
Kenton Varda committed
1192

Kenton Varda's avatar
Kenton Varda committed
1193
    case schema::Type::ENUM: {
1194 1195 1196 1197 1198 1199
      uint16_t rawValue;
      if (value.getType() == DynamicValue::TEXT) {
        // Convert from text.
        rawValue = schema.getEnumElementType().getEnumerantByName(value.as<Text>()).getOrdinal();
      } else {
        DynamicEnum enumValue = value.as<DynamicEnum>();
1200 1201
        KJ_REQUIRE(schema.getEnumElementType() == enumValue.getSchema(),
                   "Type mismatch when using DynamicList::Builder::set().") {
1202 1203 1204
          return;
        }
        rawValue = enumValue.getRaw();
1205
      }
1206
      builder.setDataElement<uint16_t>(bounded(index) * ELEMENTS, rawValue);
1207
      return;
1208
    }
Kenton Varda's avatar
Kenton Varda committed
1209

1210 1211
    case schema::Type::ANY_POINTER:
      KJ_FAIL_ASSERT("List(AnyPointer) not supported.") {
1212 1213
        return;
      }
Kenton Varda's avatar
Kenton Varda committed
1214

1215 1216 1217 1218
    case schema::Type::INTERFACE: {
      auto capValue = value.as<DynamicCapability>();
      KJ_REQUIRE(capValue.getSchema().extends(schema.getInterfaceElementType()),
                 "Value type mismatch.") {
1219 1220
        return;
      }
1221
      builder.getPointerElement(bounded(index) * ELEMENTS).setCapability(kj::mv(capValue.hook));
1222 1223
      return;
    }
Kenton Varda's avatar
Kenton Varda committed
1224
  }
1225

1226 1227 1228
  KJ_FAIL_REQUIRE("can't set element of unknown type", (uint)schema.whichElementType()) {
    return;
  }
Kenton Varda's avatar
Kenton Varda committed
1229 1230 1231
}

DynamicValue::Builder DynamicList::Builder::init(uint index, uint size) {
1232
  KJ_REQUIRE(index < this->size(), "List index out-of-bounds.");
Kenton Varda's avatar
Kenton Varda committed
1233

1234
  switch (schema.whichElementType()) {
Kenton Varda's avatar
Kenton Varda committed
1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249
    case schema::Type::VOID:
    case schema::Type::BOOL:
    case schema::Type::INT8:
    case schema::Type::INT16:
    case schema::Type::INT32:
    case schema::Type::INT64:
    case schema::Type::UINT8:
    case schema::Type::UINT16:
    case schema::Type::UINT32:
    case schema::Type::UINT64:
    case schema::Type::FLOAT32:
    case schema::Type::FLOAT64:
    case schema::Type::ENUM:
    case schema::Type::STRUCT:
    case schema::Type::INTERFACE:
1250
      KJ_FAIL_REQUIRE("Expected a list or blob.");
1251
      return nullptr;
1252

Kenton Varda's avatar
Kenton Varda committed
1253
    case schema::Type::TEXT:
1254 1255
      return builder.getPointerElement(bounded(index) * ELEMENTS)
                    .initBlob<Text>(bounded(size) * BYTES);
1256

Kenton Varda's avatar
Kenton Varda committed
1257
    case schema::Type::DATA:
1258 1259
      return builder.getPointerElement(bounded(index) * ELEMENTS)
                    .initBlob<Data>(bounded(size) * BYTES);
1260

Kenton Varda's avatar
Kenton Varda committed
1261
    case schema::Type::LIST: {
1262 1263
      auto elementType = schema.getListElementType();

Kenton Varda's avatar
Kenton Varda committed
1264
      if (elementType.whichElementType() == schema::Type::STRUCT) {
1265
        return DynamicList::Builder(elementType,
1266 1267
            builder.getPointerElement(bounded(index) * ELEMENTS)
                   .initStructList(bounded(size) * ELEMENTS,
1268
                                   structSizeFromSchema(elementType.getStructElementType())));
1269
      } else {
1270
        return DynamicList::Builder(elementType,
1271
            builder.getPointerElement(bounded(index) * ELEMENTS)
1272
                   .initList(elementSizeFor(elementType.whichElementType()),
1273
                             bounded(size) * ELEMENTS));
Kenton Varda's avatar
Kenton Varda committed
1274 1275 1276
      }
    }

1277 1278
    case schema::Type::ANY_POINTER: {
      KJ_FAIL_ASSERT("List(AnyPointer) not supported.");
1279
      return nullptr;
Kenton Varda's avatar
Kenton Varda committed
1280 1281
    }
  }
1282

1283
  return nullptr;
Kenton Varda's avatar
Kenton Varda committed
1284 1285
}

1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305
void DynamicList::Builder::adopt(uint index, Orphan<DynamicValue>&& orphan) {
  switch (schema.whichElementType()) {
    case schema::Type::VOID:
    case schema::Type::BOOL:
    case schema::Type::INT8:
    case schema::Type::INT16:
    case schema::Type::INT32:
    case schema::Type::INT64:
    case schema::Type::UINT8:
    case schema::Type::UINT16:
    case schema::Type::UINT32:
    case schema::Type::UINT64:
    case schema::Type::FLOAT32:
    case schema::Type::FLOAT64:
    case schema::Type::ENUM:
      set(index, orphan.getReader());
      return;

    case schema::Type::TEXT:
      KJ_REQUIRE(orphan.getType() == DynamicValue::TEXT, "Value type mismatch.");
1306
      builder.getPointerElement(bounded(index) * ELEMENTS).adopt(kj::mv(orphan.builder));
1307 1308 1309 1310
      return;

    case schema::Type::DATA:
      KJ_REQUIRE(orphan.getType() == DynamicValue::DATA, "Value type mismatch.");
1311
      builder.getPointerElement(bounded(index) * ELEMENTS).adopt(kj::mv(orphan.builder));
1312 1313 1314 1315 1316 1317
      return;

    case schema::Type::LIST: {
      ListSchema elementType = schema.getListElementType();
      KJ_REQUIRE(orphan.getType() == DynamicValue::LIST && orphan.listSchema == elementType,
                 "Value type mismatch.");
1318
      builder.getPointerElement(bounded(index) * ELEMENTS).adopt(kj::mv(orphan.builder));
1319 1320 1321 1322 1323 1324 1325
      return;
    }

    case schema::Type::STRUCT: {
      auto elementType = schema.getStructElementType();
      KJ_REQUIRE(orphan.getType() == DynamicValue::STRUCT && orphan.structSchema == elementType,
                 "Value type mismatch.");
1326
      builder.getStructElement(bounded(index) * ELEMENTS).transferContentFrom(
1327 1328 1329 1330
          orphan.builder.asStruct(structSizeFromSchema(elementType)));
      return;
    }

1331 1332
    case schema::Type::ANY_POINTER:
      KJ_FAIL_ASSERT("List(AnyPointer) not supported.");
1333

1334 1335 1336 1337 1338
    case schema::Type::INTERFACE: {
      auto elementType = schema.getInterfaceElementType();
      KJ_REQUIRE(orphan.getType() == DynamicValue::CAPABILITY &&
                 orphan.interfaceSchema.extends(elementType),
                 "Value type mismatch.");
1339
      builder.getPointerElement(bounded(index) * ELEMENTS).adopt(kj::mv(orphan.builder));
1340 1341
      return;
    }
1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363
  }

  KJ_UNREACHABLE;
}

Orphan<DynamicValue> DynamicList::Builder::disown(uint index) {
  switch (schema.whichElementType()) {
    case schema::Type::VOID:
    case schema::Type::BOOL:
    case schema::Type::INT8:
    case schema::Type::INT16:
    case schema::Type::INT32:
    case schema::Type::INT64:
    case schema::Type::UINT8:
    case schema::Type::UINT16:
    case schema::Type::UINT32:
    case schema::Type::UINT64:
    case schema::Type::FLOAT32:
    case schema::Type::FLOAT64:
    case schema::Type::ENUM: {
      auto result = Orphan<DynamicValue>(operator[](index), _::OrphanBuilder());
      switch (elementSizeFor(schema.whichElementType())) {
1364
        case ElementSize::VOID: break;
1365 1366 1367 1368 1369
        case ElementSize::BIT: builder.setDataElement<bool>(bounded(index) * ELEMENTS, false); break;
        case ElementSize::BYTE: builder.setDataElement<uint8_t>(bounded(index) * ELEMENTS, 0); break;
        case ElementSize::TWO_BYTES: builder.setDataElement<uint16_t>(bounded(index) * ELEMENTS, 0); break;
        case ElementSize::FOUR_BYTES: builder.setDataElement<uint32_t>(bounded(index) * ELEMENTS, 0); break;
        case ElementSize::EIGHT_BYTES: builder.setDataElement<uint64_t>(bounded(index) * ELEMENTS, 0);break;
1370 1371 1372

        case ElementSize::POINTER:
        case ElementSize::INLINE_COMPOSITE:
1373 1374 1375 1376 1377 1378 1379 1380
          KJ_UNREACHABLE;
      }
      return kj::mv(result);
    }

    case schema::Type::TEXT:
    case schema::Type::DATA:
    case schema::Type::LIST:
1381
    case schema::Type::ANY_POINTER:
1382 1383
    case schema::Type::INTERFACE: {
      auto value = operator[](index);
1384
      return Orphan<DynamicValue>(value, builder.getPointerElement(bounded(index) * ELEMENTS).disown());
1385 1386 1387 1388 1389 1390
    }

    case schema::Type::STRUCT: {
      // We have to make a copy.
      Orphan<DynamicStruct> result =
          Orphanage::getForMessageContaining(*this).newOrphan(schema.getStructElementType());
1391
      auto element = builder.getStructElement(bounded(index) * ELEMENTS);
1392 1393 1394 1395 1396 1397 1398 1399
      result.get().builder.transferContentFrom(element);
      element.clearAll();
      return kj::mv(result);
    }
  }
  KJ_UNREACHABLE;
}

1400
void DynamicList::Builder::copyFrom(std::initializer_list<DynamicValue::Reader> value) {
1401
  KJ_REQUIRE(value.size() == size(), "DynamicList::copyFrom() argument had different size.");
1402 1403 1404 1405 1406 1407
  uint i = 0;
  for (auto element: value) {
    set(i++, element);
  }
}

1408
DynamicList::Reader DynamicList::Builder::asReader() const {
1409
  return DynamicList::Reader(schema, builder.asReader());
Kenton Varda's avatar
Kenton Varda committed
1410 1411
}

Kenton Varda's avatar
Kenton Varda committed
1412 1413
// =======================================================================================

Kenton Varda's avatar
Kenton Varda committed
1414
DynamicValue::Reader::Reader(ConstSchema constant): type(VOID) {
1415
  auto type = constant.getType();
1416
  auto value = constant.getProto().getConst().getValue();
1417
  switch (type.which()) {
1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433
    case schema::Type::VOID: *this = capnp::VOID; break;
    case schema::Type::BOOL: *this = value.getBool(); break;
    case schema::Type::INT8: *this = value.getInt8(); break;
    case schema::Type::INT16: *this = value.getInt16(); break;
    case schema::Type::INT32: *this = value.getInt32(); break;
    case schema::Type::INT64: *this = value.getInt64(); break;
    case schema::Type::UINT8: *this = value.getUint8(); break;
    case schema::Type::UINT16: *this = value.getUint16(); break;
    case schema::Type::UINT32: *this = value.getUint32(); break;
    case schema::Type::UINT64: *this = value.getUint64(); break;
    case schema::Type::FLOAT32: *this = value.getFloat32(); break;
    case schema::Type::FLOAT64: *this = value.getFloat64(); break;
    case schema::Type::TEXT: *this = value.getText(); break;
    case schema::Type::DATA: *this = value.getData(); break;

    case schema::Type::ENUM:
1434
      *this = DynamicEnum(type.asEnum(), value.getEnum());
1435 1436 1437
      break;

    case schema::Type::STRUCT:
1438
      *this = value.getStruct().getAs<DynamicStruct>(type.asStruct());
1439 1440 1441
      break;

    case schema::Type::LIST:
1442
      *this = value.getList().getAs<DynamicList>(type.asList());
1443 1444
      break;

1445 1446
    case schema::Type::ANY_POINTER:
      *this = value.getAnyPointer();
1447 1448 1449 1450 1451 1452 1453
      break;

    case schema::Type::INTERFACE:
      KJ_FAIL_ASSERT("Constants can't have interface type.");
  }
}

1454
DynamicValue::Reader::Reader(const Reader& other) {
Kenton Varda's avatar
Kenton Varda committed
1455
  switch (other.type) {
1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466
    case UNKNOWN:
    case VOID:
    case BOOL:
    case INT:
    case UINT:
    case FLOAT:
    case TEXT:
    case DATA:
    case LIST:
    case ENUM:
    case STRUCT:
1467
    case ANY_POINTER:
1468 1469 1470 1471 1472 1473
      KJ_ASSERT_CAN_MEMCPY(Text::Reader);
      KJ_ASSERT_CAN_MEMCPY(Data::Reader);
      KJ_ASSERT_CAN_MEMCPY(DynamicList::Reader);
      KJ_ASSERT_CAN_MEMCPY(DynamicEnum);
      KJ_ASSERT_CAN_MEMCPY(DynamicStruct::Reader);
      KJ_ASSERT_CAN_MEMCPY(AnyPointer::Reader);
1474 1475 1476 1477 1478 1479 1480 1481 1482 1483
      break;

    case CAPABILITY:
      type = CAPABILITY;
      kj::ctor(capabilityValue, other.capabilityValue);
      return;
  }

  memcpy(this, &other, sizeof(*this));
}
Kenton Varda's avatar
Kenton Varda committed
1484 1485
DynamicValue::Reader::Reader(Reader&& other) noexcept {
  switch (other.type) {
1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496
    case UNKNOWN:
    case VOID:
    case BOOL:
    case INT:
    case UINT:
    case FLOAT:
    case TEXT:
    case DATA:
    case LIST:
    case ENUM:
    case STRUCT:
1497
    case ANY_POINTER:
1498 1499 1500 1501 1502 1503
      KJ_ASSERT_CAN_MEMCPY(Text::Reader);
      KJ_ASSERT_CAN_MEMCPY(Data::Reader);
      KJ_ASSERT_CAN_MEMCPY(DynamicList::Reader);
      KJ_ASSERT_CAN_MEMCPY(DynamicEnum);
      KJ_ASSERT_CAN_MEMCPY(DynamicStruct::Reader);
      KJ_ASSERT_CAN_MEMCPY(AnyPointer::Reader);
1504 1505 1506 1507 1508 1509 1510 1511 1512 1513
      break;

    case CAPABILITY:
      type = CAPABILITY;
      kj::ctor(capabilityValue, kj::mv(other.capabilityValue));
      return;
  }

  memcpy(this, &other, sizeof(*this));
}
Kenton Varda's avatar
Kenton Varda committed
1514
DynamicValue::Reader::~Reader() noexcept(false) {
1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535
  if (type == CAPABILITY) {
    kj::dtor(capabilityValue);
  }
}

DynamicValue::Reader& DynamicValue::Reader::operator=(const Reader& other) {
  if (type == CAPABILITY) {
    kj::dtor(capabilityValue);
  }
  kj::ctor(*this, other);
  return *this;
}
DynamicValue::Reader& DynamicValue::Reader::operator=(Reader&& other) {
  if (type == CAPABILITY) {
    kj::dtor(capabilityValue);
  }
  kj::ctor(*this, kj::mv(other));
  return *this;
}

DynamicValue::Builder::Builder(Builder& other) {
Kenton Varda's avatar
Kenton Varda committed
1536
  switch (other.type) {
1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547
    case UNKNOWN:
    case VOID:
    case BOOL:
    case INT:
    case UINT:
    case FLOAT:
    case TEXT:
    case DATA:
    case LIST:
    case ENUM:
    case STRUCT:
1548
    case ANY_POINTER:
1549
      // Unfortunately canMemcpy() doesn't work on these types due to the use of
1550 1551 1552 1553 1554 1555 1556
      // DisallowConstCopy, but __has_trivial_destructor should detect if any of these types
      // become non-trivial.
      static_assert(__has_trivial_destructor(Text::Builder) &&
                    __has_trivial_destructor(Data::Builder) &&
                    __has_trivial_destructor(DynamicList::Builder) &&
                    __has_trivial_destructor(DynamicEnum) &&
                    __has_trivial_destructor(DynamicStruct::Builder) &&
1557
                    __has_trivial_destructor(AnyPointer::Builder),
1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568
                    "Assumptions here don't hold.");
      break;

    case CAPABILITY:
      type = CAPABILITY;
      kj::ctor(capabilityValue, other.capabilityValue);
      return;
  }

  memcpy(this, &other, sizeof(*this));
}
Kenton Varda's avatar
Kenton Varda committed
1569 1570
DynamicValue::Builder::Builder(Builder&& other) noexcept {
  switch (other.type) {
1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581
    case UNKNOWN:
    case VOID:
    case BOOL:
    case INT:
    case UINT:
    case FLOAT:
    case TEXT:
    case DATA:
    case LIST:
    case ENUM:
    case STRUCT:
1582
    case ANY_POINTER:
1583 1584 1585 1586 1587 1588 1589 1590
      // Unfortunately __has_trivial_copy doesn't work on these types due to the use of
      // DisallowConstCopy, but __has_trivial_destructor should detect if any of these types
      // become non-trivial.
      static_assert(__has_trivial_destructor(Text::Builder) &&
                    __has_trivial_destructor(Data::Builder) &&
                    __has_trivial_destructor(DynamicList::Builder) &&
                    __has_trivial_destructor(DynamicEnum) &&
                    __has_trivial_destructor(DynamicStruct::Builder) &&
1591
                    __has_trivial_destructor(AnyPointer::Builder),
1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602
                    "Assumptions here don't hold.");
      break;

    case CAPABILITY:
      type = CAPABILITY;
      kj::ctor(capabilityValue, kj::mv(other.capabilityValue));
      return;
  }

  memcpy(this, &other, sizeof(*this));
}
Kenton Varda's avatar
Kenton Varda committed
1603
DynamicValue::Builder::~Builder() noexcept(false) {
1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623
  if (type == CAPABILITY) {
    kj::dtor(capabilityValue);
  }
}

DynamicValue::Builder& DynamicValue::Builder::operator=(Builder& other) {
  if (type == CAPABILITY) {
    kj::dtor(capabilityValue);
  }
  kj::ctor(*this, other);
  return *this;
}
DynamicValue::Builder& DynamicValue::Builder::operator=(Builder&& other) {
  if (type == CAPABILITY) {
    kj::dtor(capabilityValue);
  }
  kj::ctor(*this, kj::mv(other));
  return *this;
}

1624
DynamicValue::Reader DynamicValue::Builder::asReader() const {
Kenton Varda's avatar
Kenton Varda committed
1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636
  switch (type) {
    case UNKNOWN: return Reader();
    case VOID: return Reader(voidValue);
    case BOOL: return Reader(boolValue);
    case INT: return Reader(intValue);
    case UINT: return Reader(uintValue);
    case FLOAT: return Reader(floatValue);
    case TEXT: return Reader(textValue.asReader());
    case DATA: return Reader(dataValue.asReader());
    case LIST: return Reader(listValue.asReader());
    case ENUM: return Reader(enumValue);
    case STRUCT: return Reader(structValue.asReader());
1637
    case CAPABILITY: return Reader(capabilityValue);
1638
    case ANY_POINTER: return Reader(anyPointerValue.asReader());
Kenton Varda's avatar
Kenton Varda committed
1639
  }
1640
  KJ_FAIL_ASSERT("Missing switch case.");
Kenton Varda's avatar
Kenton Varda committed
1641 1642 1643
  return Reader();
}

1644
DynamicValue::Pipeline::Pipeline(Pipeline&& other) noexcept: type(other.type) {
1645 1646 1647 1648 1649
  switch (type) {
    case UNKNOWN: break;
    case STRUCT: kj::ctor(structValue, kj::mv(other.structValue)); break;
    case CAPABILITY: kj::ctor(capabilityValue, kj::mv(other.capabilityValue)); break;
    default:
1650 1651
      KJ_LOG(ERROR, "Unexpected pipeline type.", (uint)type);
      type = UNKNOWN;
1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670
      break;
  }
}
DynamicValue::Pipeline& DynamicValue::Pipeline::operator=(Pipeline&& other) {
  kj::dtor(*this);
  kj::ctor(*this, kj::mv(other));
  return *this;
}
DynamicValue::Pipeline::~Pipeline() noexcept(false) {
  switch (type) {
    case UNKNOWN: break;
    case STRUCT: kj::dtor(structValue); break;
    case CAPABILITY: kj::dtor(capabilityValue); break;
    default:
      KJ_FAIL_ASSERT("Unexpected pipeline type.", (uint)type) { type = UNKNOWN; break; }
      break;
  }
}

1671 1672 1673 1674
namespace {

template <typename T>
T signedToUnsigned(long long value) {
1675
  KJ_REQUIRE(value >= 0 && T(value) == value, "Value out-of-range for requested type.", value) {
1676
    // Use it anyway.
1677
    break;
1678 1679 1680 1681 1682 1683
  }
  return value;
}

template <>
uint64_t signedToUnsigned<uint64_t>(long long value) {
1684
  KJ_REQUIRE(value >= 0, "Value out-of-range for requested type.", value) {
1685
    // Use it anyway.
1686
    break;
1687 1688 1689 1690 1691 1692
  }
  return value;
}

template <typename T>
T unsignedToSigned(unsigned long long value) {
1693 1694
  KJ_REQUIRE(T(value) >= 0 && (unsigned long long)T(value) == value,
             "Value out-of-range for requested type.", value) {
1695
    // Use it anyway.
1696
    break;
1697 1698 1699 1700 1701 1702
  }
  return value;
}

template <>
int64_t unsignedToSigned<int64_t>(unsigned long long value) {
1703
  KJ_REQUIRE(int64_t(value) >= 0, "Value out-of-range for requested type.", value) {
1704
    // Use it anyway.
1705
    break;
1706 1707 1708 1709 1710 1711
  }
  return value;
}

template <typename T, typename U>
T checkRoundTrip(U value) {
1712
  KJ_REQUIRE(T(value) == value, "Value out-of-range for requested type.", value) {
1713
    // Use it anyway.
1714
    break;
1715 1716 1717 1718 1719 1720 1721
  }
  return value;
}

}  // namespace

#define HANDLE_NUMERIC_TYPE(typeName, ifInt, ifUint, ifFloat) \
1722
typeName DynamicValue::Reader::AsImpl<typeName>::apply(const Reader& reader) { \
1723 1724 1725 1726 1727 1728 1729 1730
  switch (reader.type) { \
    case INT: \
      return ifInt<typeName>(reader.intValue); \
    case UINT: \
      return ifUint<typeName>(reader.uintValue); \
    case FLOAT: \
      return ifFloat<typeName>(reader.floatValue); \
    default: \
Kenton Varda's avatar
Kenton Varda committed
1731
      KJ_FAIL_REQUIRE("Value type mismatch.") { \
1732
        return 0; \
1733
      } \
1734 1735
  } \
} \
1736
typeName DynamicValue::Builder::AsImpl<typeName>::apply(Builder& builder) { \
1737 1738 1739 1740 1741 1742 1743 1744
  switch (builder.type) { \
    case INT: \
      return ifInt<typeName>(builder.intValue); \
    case UINT: \
      return ifUint<typeName>(builder.uintValue); \
    case FLOAT: \
      return ifFloat<typeName>(builder.floatValue); \
    default: \
Kenton Varda's avatar
Kenton Varda committed
1745
      KJ_FAIL_REQUIRE("Value type mismatch.") { \
1746
        return 0; \
1747
      } \
1748 1749 1750 1751 1752 1753
  } \
}

HANDLE_NUMERIC_TYPE(int8_t, checkRoundTrip, unsignedToSigned, checkRoundTrip)
HANDLE_NUMERIC_TYPE(int16_t, checkRoundTrip, unsignedToSigned, checkRoundTrip)
HANDLE_NUMERIC_TYPE(int32_t, checkRoundTrip, unsignedToSigned, checkRoundTrip)
1754
HANDLE_NUMERIC_TYPE(int64_t, kj::implicitCast, unsignedToSigned, checkRoundTrip)
1755 1756 1757
HANDLE_NUMERIC_TYPE(uint8_t, signedToUnsigned, checkRoundTrip, checkRoundTrip)
HANDLE_NUMERIC_TYPE(uint16_t, signedToUnsigned, checkRoundTrip, checkRoundTrip)
HANDLE_NUMERIC_TYPE(uint32_t, signedToUnsigned, checkRoundTrip, checkRoundTrip)
1758 1759 1760
HANDLE_NUMERIC_TYPE(uint64_t, signedToUnsigned, kj::implicitCast, checkRoundTrip)
HANDLE_NUMERIC_TYPE(float, kj::implicitCast, kj::implicitCast, kj::implicitCast)
HANDLE_NUMERIC_TYPE(double, kj::implicitCast, kj::implicitCast, kj::implicitCast)
1761 1762 1763

#undef HANDLE_NUMERIC_TYPE

Kenton Varda's avatar
Kenton Varda committed
1764
#define HANDLE_TYPE(name, discrim, typeName) \
1765
ReaderFor<typeName> DynamicValue::Reader::AsImpl<typeName>::apply(const Reader& reader) { \
Kenton Varda's avatar
Kenton Varda committed
1766
  KJ_REQUIRE(reader.type == discrim, "Value type mismatch.") { \
1767 1768
    return ReaderFor<typeName>(); \
  } \
Kenton Varda's avatar
Kenton Varda committed
1769 1770
  return reader.name##Value; \
} \
1771
BuilderFor<typeName> DynamicValue::Builder::AsImpl<typeName>::apply(Builder& builder) { \
1772
  KJ_REQUIRE(builder.type == discrim, "Value type mismatch."); \
Kenton Varda's avatar
Kenton Varda committed
1773 1774 1775 1776 1777 1778 1779 1780 1781 1782
  return builder.name##Value; \
}

//HANDLE_TYPE(void, VOID, Void)
HANDLE_TYPE(bool, BOOL, bool)

HANDLE_TYPE(text, TEXT, Text)
HANDLE_TYPE(list, LIST, DynamicList)
HANDLE_TYPE(struct, STRUCT, DynamicStruct)
HANDLE_TYPE(enum, ENUM, DynamicEnum)
1783
HANDLE_TYPE(anyPointer, ANY_POINTER, AnyPointer)
Kenton Varda's avatar
Kenton Varda committed
1784 1785 1786

#undef HANDLE_TYPE

1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814
PipelineFor<DynamicStruct> DynamicValue::Pipeline::AsImpl<DynamicStruct>::apply(
    Pipeline& pipeline) {
  KJ_REQUIRE(pipeline.type == STRUCT, "Pipeline type mismatch.");
  return kj::mv(pipeline.structValue);
}

ReaderFor<DynamicCapability> DynamicValue::Reader::AsImpl<DynamicCapability>::apply(
    const Reader& reader) {
  KJ_REQUIRE(reader.type == CAPABILITY, "Value type mismatch.") {
    return DynamicCapability::Client();
  }
  return reader.capabilityValue;
}
BuilderFor<DynamicCapability> DynamicValue::Builder::AsImpl<DynamicCapability>::apply(
    Builder& builder) {
  KJ_REQUIRE(builder.type == CAPABILITY, "Value type mismatch.") {
    return DynamicCapability::Client();
  }
  return builder.capabilityValue;
}
PipelineFor<DynamicCapability> DynamicValue::Pipeline::AsImpl<DynamicCapability>::apply(
    Pipeline& pipeline) {
  KJ_REQUIRE(pipeline.type == CAPABILITY, "Pipeline type mismatch.") {
    return DynamicCapability::Client();
  }
  return kj::mv(pipeline.capabilityValue);
}

1815 1816 1817
Data::Reader DynamicValue::Reader::AsImpl<Data>::apply(const Reader& reader) {
  if (reader.type == TEXT) {
    // Coerce text to data.
1818
    return reader.textValue.asBytes();
1819 1820 1821 1822 1823 1824 1825 1826 1827
  }
  KJ_REQUIRE(reader.type == DATA, "Value type mismatch.") {
    return Data::Reader();
  }
  return reader.dataValue;
}
Data::Builder DynamicValue::Builder::AsImpl<Data>::apply(Builder& builder) {
  if (builder.type == TEXT) {
    // Coerce text to data.
1828
    return builder.textValue.asBytes();
1829 1830 1831 1832 1833 1834 1835
  }
  KJ_REQUIRE(builder.type == DATA, "Value type mismatch.") {
    return BuilderFor<Data>();
  }
  return builder.dataValue;
}

Kenton Varda's avatar
Kenton Varda committed
1836
// As in the header, HANDLE_TYPE(void, VOID, Void) crashes GCC 4.7.
1837
Void DynamicValue::Reader::AsImpl<Void>::apply(const Reader& reader) {
Kenton Varda's avatar
Kenton Varda committed
1838
  KJ_REQUIRE(reader.type == VOID, "Value type mismatch.") {
Kenton Varda's avatar
Kenton Varda committed
1839 1840 1841 1842
    return Void();
  }
  return reader.voidValue;
}
1843
Void DynamicValue::Builder::AsImpl<Void>::apply(Builder& builder) {
Kenton Varda's avatar
Kenton Varda committed
1844
  KJ_REQUIRE(builder.type == VOID, "Value type mismatch.") {
Kenton Varda's avatar
Kenton Varda committed
1845 1846 1847 1848 1849
    return Void();
  }
  return builder.voidValue;
}

Kenton Varda's avatar
Kenton Varda committed
1850 1851
// =======================================================================================

1852
namespace _ {  // private
Kenton Varda's avatar
Kenton Varda committed
1853

1854
DynamicStruct::Reader PointerHelpers<DynamicStruct, Kind::OTHER>::getDynamic(
1855
    PointerReader reader, StructSchema schema) {
1856 1857
  KJ_REQUIRE(!schema.getProto().getStruct().getIsGroup(),
             "Cannot form pointer to group type.");
1858
  return DynamicStruct::Reader(schema, reader.getStruct(nullptr));
Kenton Varda's avatar
Kenton Varda committed
1859
}
1860
DynamicStruct::Builder PointerHelpers<DynamicStruct, Kind::OTHER>::getDynamic(
1861
    PointerBuilder builder, StructSchema schema) {
1862 1863
  KJ_REQUIRE(!schema.getProto().getStruct().getIsGroup(),
             "Cannot form pointer to group type.");
1864
  return DynamicStruct::Builder(schema, builder.getStruct(
1865
      structSizeFromSchema(schema), nullptr));
Kenton Varda's avatar
Kenton Varda committed
1866
}
1867
void PointerHelpers<DynamicStruct, Kind::OTHER>::set(
1868
    PointerBuilder builder, const DynamicStruct::Reader& value) {
1869 1870
  KJ_REQUIRE(!value.schema.getProto().getStruct().getIsGroup(),
             "Cannot form pointer to group type.");
1871
  builder.setStruct(value.reader);
Kenton Varda's avatar
Kenton Varda committed
1872
}
1873
DynamicStruct::Builder PointerHelpers<DynamicStruct, Kind::OTHER>::init(
1874
    PointerBuilder builder, StructSchema schema) {
1875 1876
  KJ_REQUIRE(!schema.getProto().getStruct().getIsGroup(),
             "Cannot form pointer to group type.");
1877
  return DynamicStruct::Builder(schema,
1878
      builder.initStruct(structSizeFromSchema(schema)));
Kenton Varda's avatar
Kenton Varda committed
1879 1880
}

1881
DynamicList::Reader PointerHelpers<DynamicList, Kind::OTHER>::getDynamic(
1882
    PointerReader reader, ListSchema schema) {
1883
  return DynamicList::Reader(schema,
1884
      reader.getList(elementSizeFor(schema.whichElementType()), nullptr));
Kenton Varda's avatar
Kenton Varda committed
1885
}
1886
DynamicList::Builder PointerHelpers<DynamicList, Kind::OTHER>::getDynamic(
1887
    PointerBuilder builder, ListSchema schema) {
Kenton Varda's avatar
Kenton Varda committed
1888
  if (schema.whichElementType() == schema::Type::STRUCT) {
1889
    return DynamicList::Builder(schema,
1890
        builder.getStructList(
1891 1892 1893 1894
            structSizeFromSchema(schema.getStructElementType()),
            nullptr));
  } else {
    return DynamicList::Builder(schema,
1895
        builder.getList(elementSizeFor(schema.whichElementType()), nullptr));
1896
  }
Kenton Varda's avatar
Kenton Varda committed
1897
}
1898
void PointerHelpers<DynamicList, Kind::OTHER>::set(
1899 1900
    PointerBuilder builder, const DynamicList::Reader& value) {
  builder.setList(value.reader);
Kenton Varda's avatar
Kenton Varda committed
1901
}
1902
DynamicList::Builder PointerHelpers<DynamicList, Kind::OTHER>::init(
1903
    PointerBuilder builder, ListSchema schema, uint size) {
Kenton Varda's avatar
Kenton Varda committed
1904
  if (schema.whichElementType() == schema::Type::STRUCT) {
1905
    return DynamicList::Builder(schema,
1906
        builder.initStructList(bounded(size) * ELEMENTS,
1907
            structSizeFromSchema(schema.getStructElementType())));
Kenton Varda's avatar
Kenton Varda committed
1908
  } else {
1909
    return DynamicList::Builder(schema,
1910
        builder.initList(elementSizeFor(schema.whichElementType()), bounded(size) * ELEMENTS));
Kenton Varda's avatar
Kenton Varda committed
1911 1912 1913
  }
}

1914
DynamicCapability::Client PointerHelpers<DynamicCapability, Kind::OTHER>::getDynamic(
1915 1916 1917
    PointerReader reader, InterfaceSchema schema) {
  return DynamicCapability::Client(schema, reader.getCapability());
}
1918
DynamicCapability::Client PointerHelpers<DynamicCapability, Kind::OTHER>::getDynamic(
1919 1920 1921
    PointerBuilder builder, InterfaceSchema schema) {
  return DynamicCapability::Client(schema, builder.getCapability());
}
1922
void PointerHelpers<DynamicCapability, Kind::OTHER>::set(
1923
    PointerBuilder builder, DynamicCapability::Client& value) {
1924 1925
  builder.setCapability(value.hook->addRef());
}
1926
void PointerHelpers<DynamicCapability, Kind::OTHER>::set(
1927 1928 1929 1930
    PointerBuilder builder, DynamicCapability::Client&& value) {
  builder.setCapability(kj::mv(value.hook));
}

1931
}  // namespace _ (private)
Kenton Varda's avatar
Kenton Varda committed
1932

1933
template <>
1934
void AnyPointer::Builder::adopt<DynamicValue>(Orphan<DynamicValue>&& orphan) {
1935 1936 1937 1938 1939 1940 1941 1942
  switch (orphan.getType()) {
    case DynamicValue::UNKNOWN:
    case DynamicValue::VOID:
    case DynamicValue::BOOL:
    case DynamicValue::INT:
    case DynamicValue::UINT:
    case DynamicValue::FLOAT:
    case DynamicValue::ENUM:
1943
      KJ_FAIL_REQUIRE("AnyPointer cannot adopt primitive (non-object) value.");
1944 1945 1946 1947 1948

    case DynamicValue::STRUCT:
    case DynamicValue::LIST:
    case DynamicValue::TEXT:
    case DynamicValue::DATA:
1949
    case DynamicValue::CAPABILITY:
1950
    case DynamicValue::ANY_POINTER:
1951 1952 1953 1954 1955
      builder.adopt(kj::mv(orphan.builder));
      break;
  }
}

1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966
DynamicStruct::Reader::Reader(StructSchema schema, const _::OrphanBuilder& orphan)
    : schema(schema), reader(orphan.asStructReader(structSizeFromSchema(schema))) {}
DynamicStruct::Builder::Builder(StructSchema schema, _::OrphanBuilder& orphan)
    : schema(schema), builder(orphan.asStruct(structSizeFromSchema(schema))) {}

DynamicList::Reader::Reader(ListSchema schema, const _::OrphanBuilder& orphan)
    : schema(schema), reader(orphan.asListReader(elementSizeFor(schema.whichElementType()))) {}
DynamicList::Builder::Builder(ListSchema schema, _::OrphanBuilder& orphan)
    : schema(schema), builder(schema.whichElementType() == schema::Type::STRUCT
        ? orphan.asStructList(structSizeFromSchema(schema.getStructElementType()))
        : orphan.asList(elementSizeFor(schema.whichElementType()))) {}
1967

1968 1969
// -------------------------------------------------------------------

1970
Orphan<DynamicStruct> Orphanage::newOrphan(StructSchema schema) const {
1971
  return Orphan<DynamicStruct>(
1972
      schema, _::OrphanBuilder::initStruct(arena, capTable, structSizeFromSchema(schema)));
1973 1974
}

1975
Orphan<DynamicList> Orphanage::newOrphan(ListSchema schema, uint size) const {
Kenton Varda's avatar
Kenton Varda committed
1976
  if (schema.whichElementType() == schema::Type::STRUCT) {
1977
    return Orphan<DynamicList>(schema, _::OrphanBuilder::initStructList(
1978
        arena, capTable, bounded(size) * ELEMENTS,
1979
        structSizeFromSchema(schema.getStructElementType())));
1980 1981
  } else {
    return Orphan<DynamicList>(schema, _::OrphanBuilder::initList(
1982
        arena, capTable, bounded(size) * ELEMENTS,
1983
        elementSizeFor(schema.whichElementType())));
1984 1985 1986 1987 1988 1989 1990
  }
}

DynamicStruct::Builder Orphan<DynamicStruct>::get() {
  return DynamicStruct::Builder(schema, builder.asStruct(structSizeFromSchema(schema)));
}

1991 1992 1993 1994
DynamicStruct::Reader Orphan<DynamicStruct>::getReader() const {
  return DynamicStruct::Reader(schema, builder.asStructReader(structSizeFromSchema(schema)));
}

1995
DynamicList::Builder Orphan<DynamicList>::get() {
Kenton Varda's avatar
Kenton Varda committed
1996
  if (schema.whichElementType() == schema::Type::STRUCT) {
1997 1998 1999 2000 2001 2002 2003 2004
    return DynamicList::Builder(
        schema, builder.asStructList(structSizeFromSchema(schema.getStructElementType())));
  } else {
    return DynamicList::Builder(
        schema, builder.asList(elementSizeFor(schema.whichElementType())));
  }
}

2005 2006 2007 2008 2009
DynamicList::Reader Orphan<DynamicList>::getReader() const {
  return DynamicList::Reader(
      schema, builder.asListReader(elementSizeFor(schema.whichElementType())));
}

2010 2011 2012 2013 2014 2015 2016 2017
DynamicCapability::Client Orphan<DynamicCapability>::get() {
  return DynamicCapability::Client(schema, builder.asCapability());
}

DynamicCapability::Client Orphan<DynamicCapability>::getReader() const {
  return DynamicCapability::Client(schema, builder.asCapability());
}

2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032
Orphan<DynamicValue>::Orphan(DynamicValue::Builder value, _::OrphanBuilder&& builder)
    : type(value.getType()), builder(kj::mv(builder)) {
  switch (type) {
    case DynamicValue::UNKNOWN: break;
    case DynamicValue::VOID: voidValue = value.voidValue; break;
    case DynamicValue::BOOL: boolValue = value.boolValue; break;
    case DynamicValue::INT: intValue = value.intValue; break;
    case DynamicValue::UINT: uintValue = value.uintValue; break;
    case DynamicValue::FLOAT: floatValue = value.floatValue; break;
    case DynamicValue::ENUM: enumValue = value.enumValue; break;

    case DynamicValue::TEXT: break;
    case DynamicValue::DATA: break;
    case DynamicValue::LIST: listSchema = value.listValue.getSchema(); break;
    case DynamicValue::STRUCT: structSchema = value.structValue.getSchema(); break;
2033
    case DynamicValue::CAPABILITY: interfaceSchema = value.capabilityValue.getSchema(); break;
2034
    case DynamicValue::ANY_POINTER: break;
2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060
  }
}

DynamicValue::Builder Orphan<DynamicValue>::get() {
  switch (type) {
    case DynamicValue::UNKNOWN: return nullptr;
    case DynamicValue::VOID: return voidValue;
    case DynamicValue::BOOL: return boolValue;
    case DynamicValue::INT: return intValue;
    case DynamicValue::UINT: return uintValue;
    case DynamicValue::FLOAT: return floatValue;
    case DynamicValue::ENUM: return enumValue;

    case DynamicValue::TEXT: return builder.asText();
    case DynamicValue::DATA: return builder.asData();
    case DynamicValue::LIST:
      if (listSchema.whichElementType() == schema::Type::STRUCT) {
        return DynamicList::Builder(listSchema,
            builder.asStructList(structSizeFromSchema(listSchema.getStructElementType())));
      } else {
        return DynamicList::Builder(listSchema,
            builder.asList(elementSizeFor(listSchema.whichElementType())));
      }
    case DynamicValue::STRUCT:
      return DynamicStruct::Builder(structSchema,
          builder.asStruct(structSizeFromSchema(structSchema)));
2061 2062
    case DynamicValue::CAPABILITY:
      return DynamicCapability::Client(interfaceSchema, builder.asCapability());
2063 2064 2065
    case DynamicValue::ANY_POINTER:
      KJ_FAIL_REQUIRE("Can't get() an AnyPointer orphan; there is no underlying pointer to "
                      "wrap in an AnyPointer::Builder.");
2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086
  }
  KJ_UNREACHABLE;
}
DynamicValue::Reader Orphan<DynamicValue>::getReader() const {
  switch (type) {
    case DynamicValue::UNKNOWN: return nullptr;
    case DynamicValue::VOID: return voidValue;
    case DynamicValue::BOOL: return boolValue;
    case DynamicValue::INT: return intValue;
    case DynamicValue::UINT: return uintValue;
    case DynamicValue::FLOAT: return floatValue;
    case DynamicValue::ENUM: return enumValue;

    case DynamicValue::TEXT: return builder.asTextReader();
    case DynamicValue::DATA: return builder.asDataReader();
    case DynamicValue::LIST:
      return DynamicList::Reader(listSchema,
          builder.asListReader(elementSizeFor(listSchema.whichElementType())));
    case DynamicValue::STRUCT:
      return DynamicStruct::Reader(structSchema,
          builder.asStructReader(structSizeFromSchema(structSchema)));
2087 2088
    case DynamicValue::CAPABILITY:
      return DynamicCapability::Client(interfaceSchema, builder.asCapability());
2089 2090 2091
    case DynamicValue::ANY_POINTER:
      KJ_FAIL_ASSERT("Can't get() an AnyPointer orphan; there is no underlying pointer to "
                     "wrap in an AnyPointer::Builder.");
2092 2093 2094 2095
  }
  KJ_UNREACHABLE;
}

2096
template <>
2097 2098
Orphan<AnyPointer> Orphan<DynamicValue>::releaseAs<AnyPointer>() {
  KJ_REQUIRE(type == DynamicValue::ANY_POINTER, "Value type mismatch.");
2099
  type = DynamicValue::UNKNOWN;
2100
  return Orphan<AnyPointer>(kj::mv(builder));
2101
}
2102 2103 2104
template <>
Orphan<DynamicStruct> Orphan<DynamicValue>::releaseAs<DynamicStruct>() {
  KJ_REQUIRE(type == DynamicValue::STRUCT, "Value type mismatch.");
2105
  type = DynamicValue::UNKNOWN;
2106 2107 2108 2109 2110
  return Orphan<DynamicStruct>(structSchema, kj::mv(builder));
}
template <>
Orphan<DynamicList> Orphan<DynamicValue>::releaseAs<DynamicList>() {
  KJ_REQUIRE(type == DynamicValue::LIST, "Value type mismatch.");
2111
  type = DynamicValue::UNKNOWN;
2112 2113 2114
  return Orphan<DynamicList>(listSchema, kj::mv(builder));
}

2115 2116
template <>
Orphan<DynamicValue> Orphanage::newOrphanCopy<DynamicValue::Reader>(
2117
    DynamicValue::Reader copyFrom) const {
2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130
  switch (copyFrom.getType()) {
    case DynamicValue::UNKNOWN: return nullptr;
    case DynamicValue::VOID: return copyFrom.voidValue;
    case DynamicValue::BOOL: return copyFrom.boolValue;
    case DynamicValue::INT: return copyFrom.intValue;
    case DynamicValue::UINT: return copyFrom.uintValue;
    case DynamicValue::FLOAT: return copyFrom.floatValue;
    case DynamicValue::ENUM: return copyFrom.enumValue;

    case DynamicValue::TEXT: return newOrphanCopy(copyFrom.textValue);
    case DynamicValue::DATA: return newOrphanCopy(copyFrom.dataValue);
    case DynamicValue::LIST: return newOrphanCopy(copyFrom.listValue);
    case DynamicValue::STRUCT: return newOrphanCopy(copyFrom.structValue);
2131
    case DynamicValue::CAPABILITY: return newOrphanCopy(copyFrom.capabilityValue);
2132
    case DynamicValue::ANY_POINTER: return newOrphanCopy(copyFrom.anyPointerValue);
2133 2134 2135 2136
  }
  KJ_UNREACHABLE;
}

2137
}  // namespace capnp