| // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors |
| // Licensed under the MIT License: |
| // |
| // 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: |
| // |
| // The above copyright notice and this permission notice shall be included in |
| // all copies or substantial portions of the Software. |
| // |
| // 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. |
| |
| #include "dynamic.h" |
| #include <kj/debug.h> |
| |
| namespace capnp { |
| |
| namespace { |
| |
| bool hasDiscriminantValue(const schema::Field::Reader& reader) { |
| return reader.getDiscriminantValue() != schema::Field::NO_DISCRIMINANT; |
| } |
| |
| template <typename T, typename U> |
| KJ_ALWAYS_INLINE(T bitCast(U value)); |
| |
| template <typename T, typename U> |
| inline T bitCast(U value) { |
| static_assert(sizeof(T) == sizeof(U), "Size must match."); |
| return value; |
| } |
| 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; |
| } |
| 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; |
| } |
| |
| ElementSize elementSizeFor(schema::Type::Which elementType) { |
| switch (elementType) { |
| 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; |
| case schema::Type::ANY_POINTER: KJ_FAIL_ASSERT("List(AnyPointer) not supported."); break; |
| } |
| |
| // Unknown type. Treat it as zero-size. |
| return ElementSize::VOID; |
| } |
| |
| inline _::StructSize structSizeFromSchema(StructSchema schema) { |
| auto node = schema.getProto().getStruct(); |
| return _::StructSize( |
| bounded(node.getDataWordCount()) * WORDS, |
| bounded(node.getPointerCount()) * POINTERS); |
| } |
| |
| } // namespace |
| |
| // ======================================================================================= |
| |
| kj::Maybe<EnumSchema::Enumerant> DynamicEnum::getEnumerant() const { |
| auto enumerants = schema.getEnumerants(); |
| if (value < enumerants.size()) { |
| return enumerants[value]; |
| } else { |
| return nullptr; |
| } |
| } |
| |
| uint16_t DynamicEnum::asImpl(uint64_t requestedTypeId) const { |
| KJ_REQUIRE(requestedTypeId == schema.getProto().getId(), |
| "Type mismatch in DynamicEnum.as().") { |
| // use it anyway |
| break; |
| } |
| return value; |
| } |
| |
| // ======================================================================================= |
| |
| bool DynamicStruct::Reader::isSetInUnion(StructSchema::Field field) const { |
| auto proto = field.getProto(); |
| if (hasDiscriminantValue(proto)) { |
| uint16_t discrim = reader.getDataField<uint16_t>( |
| assumeDataOffset(schema.getProto().getStruct().getDiscriminantOffset())); |
| return discrim == proto.getDiscriminantValue(); |
| } else { |
| return true; |
| } |
| } |
| |
| 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()); |
| } |
| |
| bool DynamicStruct::Builder::isSetInUnion(StructSchema::Field field) { |
| auto proto = field.getProto(); |
| if (hasDiscriminantValue(proto)) { |
| uint16_t discrim = builder.getDataField<uint16_t>( |
| assumeDataOffset(schema.getProto().getStruct().getDiscriminantOffset())); |
| return discrim == proto.getDiscriminantValue(); |
| } else { |
| return true; |
| } |
| } |
| |
| 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()); |
| } |
| |
| void DynamicStruct::Builder::setInUnion(StructSchema::Field field) { |
| // If a union member, set the discriminant to match. |
| auto proto = field.getProto(); |
| if (hasDiscriminantValue(proto)) { |
| builder.setDataField<uint16_t>( |
| assumeDataOffset(schema.getProto().getStruct().getDiscriminantOffset()), |
| proto.getDiscriminantValue()); |
| } |
| } |
| |
| DynamicValue::Reader DynamicStruct::Reader::get(StructSchema::Field field) const { |
| KJ_REQUIRE(field.getContainingStruct() == schema, "`field` is not a field of this struct."); |
| verifySetInUnion(field); |
| |
| auto type = field.getType(); |
| auto proto = field.getProto(); |
| switch (proto.which()) { |
| case schema::Field::SLOT: { |
| auto slot = proto.getSlot(); |
| |
| // Note that the default value might be "anyPointer" even if the type is some pointer 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. |
| auto dval = slot.getDefaultValue(); |
| |
| switch (type.which()) { |
| case schema::Type::VOID: |
| return reader.getDataField<Void>(assumeDataOffset(slot.getOffset())); |
| |
| #define HANDLE_TYPE(discrim, titleCase, type) \ |
| case schema::Type::discrim: \ |
| return reader.getDataField<type>( \ |
| assumeDataOffset(slot.getOffset()), \ |
| bitCast<_::Mask<type>>(dval.get##titleCase())); |
| |
| 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) |
| |
| #undef HANDLE_TYPE |
| |
| case schema::Type::ENUM: { |
| uint16_t typedDval = dval.getEnum(); |
| return DynamicEnum(type.asEnum(), |
| reader.getDataField<uint16_t>(assumeDataOffset(slot.getOffset()), typedDval)); |
| } |
| |
| case schema::Type::TEXT: { |
| Text::Reader typedDval = dval.isAnyPointer() ? Text::Reader() : dval.getText(); |
| return reader.getPointerField(assumePointerOffset(slot.getOffset())) |
| .getBlob<Text>(typedDval.begin(), |
| assumeMax<MAX_TEXT_SIZE>(typedDval.size()) * BYTES); |
| } |
| |
| case schema::Type::DATA: { |
| Data::Reader typedDval = dval.isAnyPointer() ? Data::Reader() : dval.getData(); |
| return reader.getPointerField(assumePointerOffset(slot.getOffset())) |
| .getBlob<Data>(typedDval.begin(), |
| assumeBits<BLOB_SIZE_BITS>(typedDval.size()) * BYTES); |
| } |
| |
| case schema::Type::LIST: { |
| auto elementType = type.asList().getElementType(); |
| return DynamicList::Reader(type.asList(), |
| reader.getPointerField(assumePointerOffset(slot.getOffset())) |
| .getList(elementSizeFor(elementType.which()), dval.isAnyPointer() ? nullptr : |
| dval.getList().getAs<_::UncheckedMessage>())); |
| } |
| |
| case schema::Type::STRUCT: |
| return DynamicStruct::Reader(type.asStruct(), |
| reader.getPointerField(assumePointerOffset(slot.getOffset())) |
| .getStruct(dval.isAnyPointer() ? nullptr : |
| dval.getStruct().getAs<_::UncheckedMessage>())); |
| |
| case schema::Type::ANY_POINTER: |
| return AnyPointer::Reader(reader.getPointerField(assumePointerOffset(slot.getOffset()))); |
| |
| case schema::Type::INTERFACE: |
| return DynamicCapability::Client(type.asInterface(), |
| reader.getPointerField(assumePointerOffset(slot.getOffset())).getCapability()); |
| } |
| |
| KJ_UNREACHABLE; |
| } |
| |
| case schema::Field::GROUP: |
| return DynamicStruct::Reader(type.asStruct(), reader); |
| } |
| |
| KJ_UNREACHABLE; |
| } |
| |
| DynamicValue::Builder DynamicStruct::Builder::get(StructSchema::Field field) { |
| KJ_REQUIRE(field.getContainingStruct() == schema, "`field` is not a field of this struct."); |
| verifySetInUnion(field); |
| |
| auto proto = field.getProto(); |
| auto type = field.getType(); |
| switch (proto.which()) { |
| case schema::Field::SLOT: { |
| auto slot = proto.getSlot(); |
| |
| // Note that the default value might be "anyPointer" even if the type is some pointer 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. |
| auto dval = slot.getDefaultValue(); |
| |
| switch (type.which()) { |
| case schema::Type::VOID: |
| return builder.getDataField<Void>(assumeDataOffset(slot.getOffset())); |
| |
| #define HANDLE_TYPE(discrim, titleCase, type) \ |
| case schema::Type::discrim: \ |
| return builder.getDataField<type>( \ |
| assumeDataOffset(slot.getOffset()), \ |
| bitCast<_::Mask<type>>(dval.get##titleCase())); |
| |
| 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) |
| |
| #undef HANDLE_TYPE |
| |
| case schema::Type::ENUM: { |
| uint16_t typedDval = dval.getEnum(); |
| return DynamicEnum(type.asEnum(), |
| builder.getDataField<uint16_t>(assumeDataOffset(slot.getOffset()), typedDval)); |
| } |
| |
| case schema::Type::TEXT: { |
| Text::Reader typedDval = dval.isAnyPointer() ? Text::Reader() : dval.getText(); |
| return builder.getPointerField(assumePointerOffset(slot.getOffset())) |
| .getBlob<Text>(typedDval.begin(), |
| assumeMax<MAX_TEXT_SIZE>(typedDval.size()) * BYTES); |
| } |
| |
| case schema::Type::DATA: { |
| Data::Reader typedDval = dval.isAnyPointer() ? Data::Reader() : dval.getData(); |
| return builder.getPointerField(assumePointerOffset(slot.getOffset())) |
| .getBlob<Data>(typedDval.begin(), |
| assumeBits<BLOB_SIZE_BITS>(typedDval.size()) * BYTES); |
| } |
| |
| case schema::Type::LIST: { |
| ListSchema listType = type.asList(); |
| if (listType.whichElementType() == schema::Type::STRUCT) { |
| return DynamicList::Builder(listType, |
| builder.getPointerField(assumePointerOffset(slot.getOffset())) |
| .getStructList(structSizeFromSchema(listType.getStructElementType()), |
| dval.isAnyPointer() ? nullptr : |
| dval.getList().getAs<_::UncheckedMessage>())); |
| } else { |
| return DynamicList::Builder(listType, |
| builder.getPointerField(assumePointerOffset(slot.getOffset())) |
| .getList(elementSizeFor(listType.whichElementType()), |
| dval.isAnyPointer() ? nullptr : |
| dval.getList().getAs<_::UncheckedMessage>())); |
| } |
| } |
| |
| case schema::Type::STRUCT: { |
| auto structSchema = type.asStruct(); |
| return DynamicStruct::Builder(structSchema, |
| builder.getPointerField(assumePointerOffset(slot.getOffset())) |
| .getStruct(structSizeFromSchema(structSchema), |
| dval.isAnyPointer() ? nullptr : |
| dval.getStruct().getAs<_::UncheckedMessage>())); |
| } |
| |
| case schema::Type::ANY_POINTER: |
| return AnyPointer::Builder( |
| builder.getPointerField(assumePointerOffset(slot.getOffset()))); |
| |
| case schema::Type::INTERFACE: |
| return DynamicCapability::Client(type.asInterface(), |
| builder.getPointerField(assumePointerOffset(slot.getOffset())).getCapability()); |
| } |
| |
| KJ_UNREACHABLE; |
| } |
| |
| case schema::Field::GROUP: |
| return DynamicStruct::Builder(type.asStruct(), builder); |
| } |
| |
| KJ_UNREACHABLE; |
| } |
| |
| DynamicValue::Pipeline DynamicStruct::Pipeline::get(StructSchema::Field field) { |
| KJ_REQUIRE(field.getContainingStruct() == schema, "`field` is not a field of this struct."); |
| |
| auto proto = field.getProto(); |
| KJ_REQUIRE(!hasDiscriminantValue(proto), "Can't pipeline on union members."); |
| |
| auto type = field.getType(); |
| |
| switch (proto.which()) { |
| case schema::Field::SLOT: { |
| auto slot = proto.getSlot(); |
| |
| switch (type.which()) { |
| case schema::Type::STRUCT: |
| return DynamicStruct::Pipeline(type.asStruct(), |
| typeless.getPointerField(slot.getOffset())); |
| |
| case schema::Type::INTERFACE: |
| return DynamicCapability::Client(type.asInterface(), |
| typeless.getPointerField(slot.getOffset()).asCap()); |
| |
| 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."); |
| } |
| |
| default: |
| KJ_FAIL_REQUIRE("Can only pipeline on struct and interface fields."); |
| } |
| |
| KJ_UNREACHABLE; |
| } |
| |
| case schema::Field::GROUP: |
| return DynamicStruct::Pipeline(type.asStruct(), typeless.noop()); |
| } |
| |
| KJ_UNREACHABLE; |
| } |
| |
| bool DynamicStruct::Reader::has(StructSchema::Field field, HasMode mode) const { |
| KJ_REQUIRE(field.getContainingStruct() == schema, "`field` is not a field of this struct."); |
| |
| auto proto = field.getProto(); |
| if (hasDiscriminantValue(proto)) { |
| uint16_t discrim = reader.getDataField<uint16_t>( |
| assumeDataOffset(schema.getProto().getStruct().getDiscriminantOffset())); |
| if (discrim != proto.getDiscriminantValue()) { |
| // Field is not active in the union. |
| return false; |
| } |
| } |
| |
| switch (proto.which()) { |
| case schema::Field::SLOT: |
| // Continue to below. |
| break; |
| |
| case schema::Field::GROUP: |
| return true; |
| } |
| |
| auto slot = proto.getSlot(); |
| auto type = field.getType(); |
| |
| switch (type.which()) { |
| case schema::Type::VOID: |
| // Void is always equal to the default. |
| return mode == HasMode::NON_NULL; |
| |
| case schema::Type::BOOL: |
| return mode == HasMode::NON_NULL || |
| reader.getDataField<bool>(assumeDataOffset(slot.getOffset()), 0) != 0; |
| |
| case schema::Type::INT8: |
| case schema::Type::UINT8: |
| return mode == HasMode::NON_NULL || |
| reader.getDataField<uint8_t>(assumeDataOffset(slot.getOffset()), 0) != 0; |
| |
| case schema::Type::INT16: |
| case schema::Type::UINT16: |
| case schema::Type::ENUM: |
| return mode == HasMode::NON_NULL || |
| reader.getDataField<uint16_t>(assumeDataOffset(slot.getOffset()), 0) != 0; |
| |
| case schema::Type::INT32: |
| case schema::Type::UINT32: |
| case schema::Type::FLOAT32: |
| return mode == HasMode::NON_NULL || |
| reader.getDataField<uint32_t>(assumeDataOffset(slot.getOffset()), 0) != 0; |
| |
| case schema::Type::INT64: |
| case schema::Type::UINT64: |
| case schema::Type::FLOAT64: |
| return mode == HasMode::NON_NULL || |
| reader.getDataField<uint64_t>(assumeDataOffset(slot.getOffset()), 0) != 0; |
| |
| case schema::Type::TEXT: |
| case schema::Type::DATA: |
| case schema::Type::LIST: |
| case schema::Type::STRUCT: |
| case schema::Type::ANY_POINTER: |
| case schema::Type::INTERFACE: |
| return !reader.getPointerField(assumePointerOffset(slot.getOffset())).isNull(); |
| } |
| |
| // 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; |
| } |
| |
| uint16_t discrim = reader.getDataField<uint16_t>( |
| assumeDataOffset(structProto.getDiscriminantOffset())); |
| return schema.getFieldByDiscriminant(discrim); |
| } |
| |
| kj::Maybe<StructSchema::Field> DynamicStruct::Builder::which() { |
| auto structProto = schema.getProto().getStruct(); |
| if (structProto.getDiscriminantCount() == 0) { |
| return nullptr; |
| } |
| |
| uint16_t discrim = builder.getDataField<uint16_t>( |
| assumeDataOffset(structProto.getDiscriminantOffset())); |
| 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(); |
| auto type = field.getType(); |
| switch (proto.which()) { |
| case schema::Field::SLOT: { |
| auto slot = proto.getSlot(); |
| auto dval = slot.getDefaultValue(); |
| |
| switch (type.which()) { |
| case schema::Type::VOID: |
| builder.setDataField<Void>(assumeDataOffset(slot.getOffset()), value.as<Void>()); |
| return; |
| |
| #define HANDLE_TYPE(discrim, titleCase, type) \ |
| case schema::Type::discrim: \ |
| builder.setDataField<type>( \ |
| assumeDataOffset(slot.getOffset()), value.as<type>(), \ |
| bitCast<_::Mask<type> >(dval.get##titleCase())); \ |
| 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) |
| |
| #undef HANDLE_TYPE |
| |
| case schema::Type::ENUM: { |
| uint16_t rawValue; |
| auto enumSchema = type.asEnum(); |
| if (value.getType() == DynamicValue::TEXT) { |
| // Convert from text. |
| rawValue = enumSchema.getEnumerantByName(value.as<Text>()).getOrdinal(); |
| } else if (value.getType() == DynamicValue::INT || |
| value.getType() == DynamicValue::UINT) { |
| rawValue = value.as<uint16_t>(); |
| } else { |
| DynamicEnum enumValue = value.as<DynamicEnum>(); |
| KJ_REQUIRE(enumValue.getSchema() == enumSchema, "Value type mismatch.") { |
| return; |
| } |
| rawValue = enumValue.getRaw(); |
| } |
| builder.setDataField<uint16_t>(assumeDataOffset(slot.getOffset()), rawValue, |
| dval.getEnum()); |
| return; |
| } |
| |
| case schema::Type::TEXT: |
| builder.getPointerField(assumePointerOffset(slot.getOffset())) |
| .setBlob<Text>(value.as<Text>()); |
| return; |
| |
| case schema::Type::DATA: |
| builder.getPointerField(assumePointerOffset(slot.getOffset())) |
| .setBlob<Data>(value.as<Data>()); |
| return; |
| |
| case schema::Type::LIST: { |
| ListSchema listType = type.asList(); |
| auto listValue = value.as<DynamicList>(); |
| KJ_REQUIRE(listValue.getSchema() == listType, "Value type mismatch.") { |
| return; |
| } |
| builder.getPointerField(assumePointerOffset(slot.getOffset())) |
| .setList(listValue.reader); |
| return; |
| } |
| |
| case schema::Type::STRUCT: { |
| auto structType = type.asStruct(); |
| auto structValue = value.as<DynamicStruct>(); |
| KJ_REQUIRE(structValue.getSchema() == structType, "Value type mismatch.") { |
| return; |
| } |
| builder.getPointerField(assumePointerOffset(slot.getOffset())) |
| .setStruct(structValue.reader); |
| return; |
| } |
| |
| case schema::Type::ANY_POINTER: { |
| auto target = AnyPointer::Builder( |
| builder.getPointerField(assumePointerOffset(slot.getOffset()))); |
| |
| 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; |
| } |
| |
| case schema::Type::INTERFACE: { |
| auto interfaceType = type.asInterface(); |
| auto capability = value.as<DynamicCapability>(); |
| KJ_REQUIRE(capability.getSchema().extends(interfaceType), "Value type mismatch.") { |
| return; |
| } |
| builder.getPointerField(assumePointerOffset(slot.getOffset())) |
| .setCapability(kj::mv(capability.hook)); |
| return; |
| } |
| } |
| |
| KJ_UNREACHABLE; |
| } |
| |
| case schema::Field::GROUP: { |
| 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)); |
| } |
| } |
| } |
| } |
| |
| KJ_UNREACHABLE; |
| } |
| |
| 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(); |
| auto type = field.getType(); |
| |
| switch (proto.which()) { |
| case schema::Field::SLOT: { |
| auto slot = proto.getSlot(); |
| switch (type.which()) { |
| case schema::Type::STRUCT: { |
| auto subSchema = type.asStruct(); |
| return DynamicStruct::Builder(subSchema, |
| builder.getPointerField(assumePointerOffset(slot.getOffset())) |
| .initStruct(structSizeFromSchema(subSchema))); |
| } |
| case schema::Type::ANY_POINTER: { |
| auto pointer = builder.getPointerField(assumePointerOffset(slot.getOffset())); |
| pointer.clear(); |
| return AnyPointer::Builder(pointer); |
| } |
| default: |
| KJ_FAIL_REQUIRE("init() without a size is only valid for struct and object fields."); |
| } |
| } |
| |
| case schema::Field::GROUP: { |
| clear(field); |
| return DynamicStruct::Builder(type.asStruct(), builder); |
| } |
| } |
| |
| KJ_UNREACHABLE; |
| } |
| |
| 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(); |
| auto type = field.getType(); |
| |
| switch (proto.which()) { |
| case schema::Field::SLOT: { |
| auto slot = proto.getSlot(); |
| switch (type.which()) { |
| case schema::Type::LIST: { |
| auto listType = type.asList(); |
| if (listType.whichElementType() == schema::Type::STRUCT) { |
| return DynamicList::Builder(listType, |
| builder.getPointerField(assumePointerOffset(slot.getOffset())) |
| .initStructList(bounded(size) * ELEMENTS, |
| structSizeFromSchema(listType.getStructElementType()))); |
| } else { |
| return DynamicList::Builder(listType, |
| builder.getPointerField(assumePointerOffset(slot.getOffset())) |
| .initList(elementSizeFor(listType.whichElementType()), |
| bounded(size) * ELEMENTS)); |
| } |
| } |
| case schema::Type::TEXT: |
| return builder.getPointerField(assumePointerOffset(slot.getOffset())) |
| .initBlob<Text>(bounded(size) * BYTES); |
| case schema::Type::DATA: |
| return builder.getPointerField(assumePointerOffset(slot.getOffset())) |
| .initBlob<Data>(bounded(size) * BYTES); |
| default: |
| KJ_FAIL_REQUIRE( |
| "init() with size is only valid for list, text, or data fields.", |
| (uint)type.which()); |
| break; |
| } |
| KJ_UNREACHABLE; |
| } |
| |
| case schema::Field::GROUP: |
| KJ_FAIL_REQUIRE("init() with size is only valid for list, text, or data fields."); |
| } |
| |
| KJ_UNREACHABLE; |
| } |
| |
| 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()) { |
| case schema::Field::SLOT: { |
| auto slot = proto.getSlot(); |
| auto type = field.getType(); |
| |
| 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: |
| KJ_REQUIRE(orphan.getType() == DynamicValue::TEXT, "Value type mismatch."); |
| break; |
| |
| case schema::Type::DATA: |
| KJ_REQUIRE(orphan.getType() == DynamicValue::DATA, "Value type mismatch."); |
| break; |
| |
| case schema::Type::LIST: { |
| ListSchema listType = type.asList(); |
| KJ_REQUIRE(orphan.getType() == DynamicValue::LIST && orphan.listSchema == listType, |
| "Value type mismatch.") { |
| return; |
| } |
| break; |
| } |
| |
| case schema::Type::STRUCT: { |
| auto structType = type.asStruct(); |
| KJ_REQUIRE(orphan.getType() == DynamicValue::STRUCT && orphan.structSchema == structType, |
| "Value type mismatch.") { |
| return; |
| } |
| break; |
| } |
| |
| case schema::Type::ANY_POINTER: |
| KJ_REQUIRE(orphan.getType() == DynamicValue::STRUCT || |
| orphan.getType() == DynamicValue::LIST || |
| orphan.getType() == DynamicValue::TEXT || |
| orphan.getType() == DynamicValue::DATA || |
| orphan.getType() == DynamicValue::CAPABILITY || |
| orphan.getType() == DynamicValue::ANY_POINTER, |
| "Value type mismatch.") { |
| return; |
| } |
| break; |
| |
| case schema::Type::INTERFACE: { |
| auto interfaceType = type.asInterface(); |
| KJ_REQUIRE(orphan.getType() == DynamicValue::CAPABILITY && |
| orphan.interfaceSchema.extends(interfaceType), |
| "Value type mismatch.") { |
| return; |
| } |
| break; |
| } |
| } |
| |
| builder.getPointerField(assumePointerOffset(slot.getOffset())).adopt(kj::mv(orphan.builder)); |
| 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()) { |
| case schema::Field::SLOT: { |
| auto slot = proto.getSlot(); |
| |
| switch (field.getType().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: { |
| 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: |
| case schema::Type::ANY_POINTER: |
| case schema::Type::INTERFACE: { |
| auto value = get(field); |
| return Orphan<DynamicValue>( |
| value, builder.getPointerField(assumePointerOffset(slot.getOffset())).disown()); |
| } |
| } |
| 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; |
| } |
| |
| 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(); |
| auto type = field.getType(); |
| switch (proto.which()) { |
| case schema::Field::SLOT: { |
| auto slot = proto.getSlot(); |
| |
| switch (type.which()) { |
| case schema::Type::VOID: |
| builder.setDataField<Void>(assumeDataOffset(slot.getOffset()), VOID); |
| return; |
| |
| #define HANDLE_TYPE(discrim, type) \ |
| case schema::Type::discrim: \ |
| builder.setDataField<type>(assumeDataOffset(slot.getOffset()), 0); \ |
| 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 |
| |
| case schema::Type::TEXT: |
| case schema::Type::DATA: |
| case schema::Type::LIST: |
| case schema::Type::STRUCT: |
| case schema::Type::ANY_POINTER: |
| case schema::Type::INTERFACE: |
| builder.getPointerField(assumePointerOffset(slot.getOffset())).clear(); |
| return; |
| } |
| |
| KJ_UNREACHABLE; |
| } |
| |
| case schema::Field::GROUP: { |
| DynamicStruct::Builder group(type.asStruct(), builder); |
| |
| // 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. |
| KJ_IF_MAYBE(unionField, group.schema.getFieldByDiscriminant(0)) { |
| group.clear(*unionField); |
| } |
| |
| for (auto subField: group.schema.getNonUnionFields()) { |
| group.clear(subField); |
| } |
| return; |
| } |
| } |
| |
| 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)); |
| } |
| DynamicValue::Pipeline DynamicStruct::Pipeline::get(kj::StringPtr name) { |
| return get(schema.getFieldByName(name)); |
| } |
| bool DynamicStruct::Reader::has(kj::StringPtr name, HasMode mode) const { |
| return has(schema.getFieldByName(name), mode); |
| } |
| bool DynamicStruct::Builder::has(kj::StringPtr name, HasMode mode) { |
| return has(schema.getFieldByName(name), mode); |
| } |
| 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); |
| } |
| 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)); |
| } |
| void DynamicStruct::Builder::clear(kj::StringPtr name) { |
| clear(schema.getFieldByName(name)); |
| } |
| |
| // ======================================================================================= |
| |
| DynamicValue::Reader DynamicList::Reader::operator[](uint index) const { |
| KJ_REQUIRE(index < size(), "List index out-of-bounds."); |
| |
| switch (schema.whichElementType()) { |
| #define HANDLE_TYPE(name, discrim, typeName) \ |
| case schema::Type::discrim: \ |
| return reader.getDataElement<typeName>(bounded(index) * ELEMENTS); |
| |
| 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) |
| #undef HANDLE_TYPE |
| |
| case schema::Type::TEXT: |
| return reader.getPointerElement(bounded(index) * ELEMENTS) |
| .getBlob<Text>(nullptr, ZERO * BYTES); |
| case schema::Type::DATA: |
| return reader.getPointerElement(bounded(index) * ELEMENTS) |
| .getBlob<Data>(nullptr, ZERO * BYTES); |
| |
| case schema::Type::LIST: { |
| auto elementType = schema.getListElementType(); |
| return DynamicList::Reader(elementType, |
| reader.getPointerElement(bounded(index) * ELEMENTS) |
| .getList(elementSizeFor(elementType.whichElementType()), nullptr)); |
| } |
| |
| case schema::Type::STRUCT: |
| return DynamicStruct::Reader(schema.getStructElementType(), |
| reader.getStructElement(bounded(index) * ELEMENTS)); |
| |
| case schema::Type::ENUM: |
| return DynamicEnum(schema.getEnumElementType(), |
| reader.getDataElement<uint16_t>(bounded(index) * ELEMENTS)); |
| |
| case schema::Type::ANY_POINTER: |
| return AnyPointer::Reader(reader.getPointerElement(bounded(index) * ELEMENTS)); |
| |
| case schema::Type::INTERFACE: |
| return DynamicCapability::Client(schema.getInterfaceElementType(), |
| reader.getPointerElement(bounded(index) * ELEMENTS) |
| .getCapability()); |
| } |
| |
| return nullptr; |
| } |
| |
| DynamicValue::Builder DynamicList::Builder::operator[](uint index) { |
| KJ_REQUIRE(index < size(), "List index out-of-bounds."); |
| |
| switch (schema.whichElementType()) { |
| #define HANDLE_TYPE(name, discrim, typeName) \ |
| case schema::Type::discrim: \ |
| return builder.getDataElement<typeName>(bounded(index) * ELEMENTS); |
| |
| 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) |
| #undef HANDLE_TYPE |
| |
| case schema::Type::TEXT: |
| return builder.getPointerElement(bounded(index) * ELEMENTS) |
| .getBlob<Text>(nullptr, ZERO * BYTES); |
| case schema::Type::DATA: |
| return builder.getPointerElement(bounded(index) * ELEMENTS) |
| .getBlob<Data>(nullptr, ZERO * BYTES); |
| |
| case schema::Type::LIST: { |
| ListSchema elementType = schema.getListElementType(); |
| if (elementType.whichElementType() == schema::Type::STRUCT) { |
| return DynamicList::Builder(elementType, |
| builder.getPointerElement(bounded(index) * ELEMENTS) |
| .getStructList(structSizeFromSchema(elementType.getStructElementType()), |
| nullptr)); |
| } else { |
| return DynamicList::Builder(elementType, |
| builder.getPointerElement(bounded(index) * ELEMENTS) |
| .getList(elementSizeFor(elementType.whichElementType()), nullptr)); |
| } |
| } |
| |
| case schema::Type::STRUCT: |
| return DynamicStruct::Builder(schema.getStructElementType(), |
| builder.getStructElement(bounded(index) * ELEMENTS)); |
| |
| case schema::Type::ENUM: |
| return DynamicEnum(schema.getEnumElementType(), |
| builder.getDataElement<uint16_t>(bounded(index) * ELEMENTS)); |
| |
| case schema::Type::ANY_POINTER: |
| KJ_FAIL_ASSERT("List(AnyPointer) not supported."); |
| return nullptr; |
| |
| case schema::Type::INTERFACE: |
| return DynamicCapability::Client(schema.getInterfaceElementType(), |
| builder.getPointerElement(bounded(index) * ELEMENTS) |
| .getCapability()); |
| } |
| |
| return nullptr; |
| } |
| |
| void DynamicList::Builder::set(uint index, const DynamicValue::Reader& value) { |
| KJ_REQUIRE(index < size(), "List index out-of-bounds.") { |
| return; |
| } |
| |
| switch (schema.whichElementType()) { |
| #define HANDLE_TYPE(name, discrim, typeName) \ |
| case schema::Type::discrim: \ |
| builder.setDataElement<typeName>(bounded(index) * ELEMENTS, value.as<typeName>()); \ |
| return; |
| |
| 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) |
| #undef HANDLE_TYPE |
| |
| case schema::Type::TEXT: |
| builder.getPointerElement(bounded(index) * ELEMENTS).setBlob<Text>(value.as<Text>()); |
| return; |
| case schema::Type::DATA: |
| builder.getPointerElement(bounded(index) * ELEMENTS).setBlob<Data>(value.as<Data>()); |
| return; |
| |
| case schema::Type::LIST: { |
| auto listValue = value.as<DynamicList>(); |
| KJ_REQUIRE(listValue.getSchema() == schema.getListElementType(), "Value type mismatch.") { |
| return; |
| } |
| builder.getPointerElement(bounded(index) * ELEMENTS).setList(listValue.reader); |
| return; |
| } |
| |
| case schema::Type::STRUCT: { |
| auto structValue = value.as<DynamicStruct>(); |
| KJ_REQUIRE(structValue.getSchema() == schema.getStructElementType(), "Value type mismatch.") { |
| return; |
| } |
| builder.getStructElement(bounded(index) * ELEMENTS).copyContentFrom(structValue.reader); |
| return; |
| } |
| |
| case schema::Type::ENUM: { |
| 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>(); |
| KJ_REQUIRE(schema.getEnumElementType() == enumValue.getSchema(), |
| "Type mismatch when using DynamicList::Builder::set().") { |
| return; |
| } |
| rawValue = enumValue.getRaw(); |
| } |
| builder.setDataElement<uint16_t>(bounded(index) * ELEMENTS, rawValue); |
| return; |
| } |
| |
| case schema::Type::ANY_POINTER: |
| KJ_FAIL_ASSERT("List(AnyPointer) not supported.") { |
| return; |
| } |
| |
| case schema::Type::INTERFACE: { |
| auto capValue = value.as<DynamicCapability>(); |
| KJ_REQUIRE(capValue.getSchema().extends(schema.getInterfaceElementType()), |
| "Value type mismatch.") { |
| return; |
| } |
| builder.getPointerElement(bounded(index) * ELEMENTS).setCapability(kj::mv(capValue.hook)); |
| return; |
| } |
| } |
| |
| KJ_FAIL_REQUIRE("can't set element of unknown type", (uint)schema.whichElementType()) { |
| return; |
| } |
| } |
| |
| DynamicValue::Builder DynamicList::Builder::init(uint index, uint size) { |
| KJ_REQUIRE(index < this->size(), "List index out-of-bounds."); |
| |
| 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: |
| case schema::Type::STRUCT: |
| case schema::Type::INTERFACE: |
| KJ_FAIL_REQUIRE("Expected a list or blob."); |
| return nullptr; |
| |
| case schema::Type::TEXT: |
| return builder.getPointerElement(bounded(index) * ELEMENTS) |
| .initBlob<Text>(bounded(size) * BYTES); |
| |
| case schema::Type::DATA: |
| return builder.getPointerElement(bounded(index) * ELEMENTS) |
| .initBlob<Data>(bounded(size) * BYTES); |
| |
| case schema::Type::LIST: { |
| auto elementType = schema.getListElementType(); |
| |
| if (elementType.whichElementType() == schema::Type::STRUCT) { |
| return DynamicList::Builder(elementType, |
| builder.getPointerElement(bounded(index) * ELEMENTS) |
| .initStructList(bounded(size) * ELEMENTS, |
| structSizeFromSchema(elementType.getStructElementType()))); |
| } else { |
| return DynamicList::Builder(elementType, |
| builder.getPointerElement(bounded(index) * ELEMENTS) |
| .initList(elementSizeFor(elementType.whichElementType()), |
| bounded(size) * ELEMENTS)); |
| } |
| } |
| |
| case schema::Type::ANY_POINTER: { |
| KJ_FAIL_ASSERT("List(AnyPointer) not supported."); |
| return nullptr; |
| } |
| } |
| |
| return nullptr; |
| } |
| |
| 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."); |
| builder.getPointerElement(bounded(index) * ELEMENTS).adopt(kj::mv(orphan.builder)); |
| return; |
| |
| case schema::Type::DATA: |
| KJ_REQUIRE(orphan.getType() == DynamicValue::DATA, "Value type mismatch."); |
| builder.getPointerElement(bounded(index) * ELEMENTS).adopt(kj::mv(orphan.builder)); |
| return; |
| |
| case schema::Type::LIST: { |
| ListSchema elementType = schema.getListElementType(); |
| KJ_REQUIRE(orphan.getType() == DynamicValue::LIST && orphan.listSchema == elementType, |
| "Value type mismatch."); |
| builder.getPointerElement(bounded(index) * ELEMENTS).adopt(kj::mv(orphan.builder)); |
| return; |
| } |
| |
| case schema::Type::STRUCT: { |
| auto elementType = schema.getStructElementType(); |
| KJ_REQUIRE(orphan.getType() == DynamicValue::STRUCT && orphan.structSchema == elementType, |
| "Value type mismatch."); |
| builder.getStructElement(bounded(index) * ELEMENTS).transferContentFrom( |
| orphan.builder.asStruct(structSizeFromSchema(elementType))); |
| return; |
| } |
| |
| case schema::Type::ANY_POINTER: |
| KJ_FAIL_ASSERT("List(AnyPointer) not supported."); |
| |
| case schema::Type::INTERFACE: { |
| auto elementType = schema.getInterfaceElementType(); |
| KJ_REQUIRE(orphan.getType() == DynamicValue::CAPABILITY && |
| orphan.interfaceSchema.extends(elementType), |
| "Value type mismatch."); |
| builder.getPointerElement(bounded(index) * ELEMENTS).adopt(kj::mv(orphan.builder)); |
| return; |
| } |
| } |
| |
| 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())) { |
| case ElementSize::VOID: break; |
| 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; |
| |
| case ElementSize::POINTER: |
| case ElementSize::INLINE_COMPOSITE: |
| KJ_UNREACHABLE; |
| } |
| return kj::mv(result); |
| } |
| |
| case schema::Type::TEXT: |
| case schema::Type::DATA: |
| case schema::Type::LIST: |
| case schema::Type::ANY_POINTER: |
| case schema::Type::INTERFACE: { |
| auto value = operator[](index); |
| return Orphan<DynamicValue>(value, builder.getPointerElement(bounded(index) * ELEMENTS).disown()); |
| } |
| |
| case schema::Type::STRUCT: { |
| // We have to make a copy. |
| Orphan<DynamicStruct> result = |
| Orphanage::getForMessageContaining(*this).newOrphan(schema.getStructElementType()); |
| auto element = builder.getStructElement(bounded(index) * ELEMENTS); |
| result.get().builder.transferContentFrom(element); |
| element.clearAll(); |
| return kj::mv(result); |
| } |
| } |
| KJ_UNREACHABLE; |
| } |
| |
| void DynamicList::Builder::copyFrom(std::initializer_list<DynamicValue::Reader> value) { |
| KJ_REQUIRE(value.size() == size(), "DynamicList::copyFrom() argument had different size."); |
| uint i = 0; |
| for (auto element: value) { |
| set(i++, element); |
| } |
| } |
| |
| DynamicList::Reader DynamicList::Builder::asReader() const { |
| return DynamicList::Reader(schema, builder.asReader()); |
| } |
| |
| // ======================================================================================= |
| |
| DynamicValue::Reader::Reader(ConstSchema constant): type(VOID) { |
| auto type = constant.getType(); |
| auto value = constant.getProto().getConst().getValue(); |
| switch (type.which()) { |
| 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: |
| *this = DynamicEnum(type.asEnum(), value.getEnum()); |
| break; |
| |
| case schema::Type::STRUCT: |
| *this = value.getStruct().getAs<DynamicStruct>(type.asStruct()); |
| break; |
| |
| case schema::Type::LIST: |
| *this = value.getList().getAs<DynamicList>(type.asList()); |
| break; |
| |
| case schema::Type::ANY_POINTER: |
| *this = value.getAnyPointer(); |
| break; |
| |
| case schema::Type::INTERFACE: |
| KJ_FAIL_ASSERT("Constants can't have interface type."); |
| } |
| } |
| |
| #if __GNUC__ && !__clang__ && __GNUC__ >= 9 |
| // In the copy constructors below, we use memcpy() to copy only after verifying that it is safe. |
| // But GCC 9 doesn't know we've checked, and whines. I suppose GCC is probably right: our checks |
| // probably don't technically make memcpy safe according to the standard. But it works in practice, |
| // and if it ever stops working, the tests will catch it. |
| #pragma GCC diagnostic ignored "-Wclass-memaccess" |
| #endif |
| |
| DynamicValue::Reader::Reader(const Reader& other) { |
| switch (other.type) { |
| case UNKNOWN: |
| case VOID: |
| case BOOL: |
| case INT: |
| case UINT: |
| case FLOAT: |
| case TEXT: |
| case DATA: |
| case LIST: |
| case ENUM: |
| case STRUCT: |
| case ANY_POINTER: |
| 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); |
| break; |
| |
| case CAPABILITY: |
| type = CAPABILITY; |
| kj::ctor(capabilityValue, other.capabilityValue); |
| return; |
| } |
| |
| memcpy(this, &other, sizeof(*this)); |
| } |
| DynamicValue::Reader::Reader(Reader&& other) noexcept { |
| switch (other.type) { |
| case UNKNOWN: |
| case VOID: |
| case BOOL: |
| case INT: |
| case UINT: |
| case FLOAT: |
| case TEXT: |
| case DATA: |
| case LIST: |
| case ENUM: |
| case STRUCT: |
| case ANY_POINTER: |
| 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); |
| break; |
| |
| case CAPABILITY: |
| type = CAPABILITY; |
| kj::ctor(capabilityValue, kj::mv(other.capabilityValue)); |
| return; |
| } |
| |
| memcpy(this, &other, sizeof(*this)); |
| } |
| DynamicValue::Reader::~Reader() noexcept(false) { |
| 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) { |
| switch (other.type) { |
| case UNKNOWN: |
| case VOID: |
| case BOOL: |
| case INT: |
| case UINT: |
| case FLOAT: |
| case TEXT: |
| case DATA: |
| case LIST: |
| case ENUM: |
| case STRUCT: |
| case ANY_POINTER: |
| // Unfortunately canMemcpy() 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) && |
| __has_trivial_destructor(AnyPointer::Builder), |
| "Assumptions here don't hold."); |
| break; |
| |
| case CAPABILITY: |
| type = CAPABILITY; |
| kj::ctor(capabilityValue, other.capabilityValue); |
| return; |
| } |
| |
| memcpy(this, &other, sizeof(*this)); |
| } |
| DynamicValue::Builder::Builder(Builder&& other) noexcept { |
| switch (other.type) { |
| case UNKNOWN: |
| case VOID: |
| case BOOL: |
| case INT: |
| case UINT: |
| case FLOAT: |
| case TEXT: |
| case DATA: |
| case LIST: |
| case ENUM: |
| case STRUCT: |
| case ANY_POINTER: |
| // 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) && |
| __has_trivial_destructor(AnyPointer::Builder), |
| "Assumptions here don't hold."); |
| break; |
| |
| case CAPABILITY: |
| type = CAPABILITY; |
| kj::ctor(capabilityValue, kj::mv(other.capabilityValue)); |
| return; |
| } |
| |
| memcpy(this, &other, sizeof(*this)); |
| } |
| DynamicValue::Builder::~Builder() noexcept(false) { |
| 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; |
| } |
| |
| DynamicValue::Reader DynamicValue::Builder::asReader() const { |
| 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()); |
| case CAPABILITY: return Reader(capabilityValue); |
| case ANY_POINTER: return Reader(anyPointerValue.asReader()); |
| } |
| KJ_FAIL_ASSERT("Missing switch case."); |
| return Reader(); |
| } |
| |
| DynamicValue::Pipeline::Pipeline(Pipeline&& other) noexcept: type(other.type) { |
| 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: |
| KJ_LOG(ERROR, "Unexpected pipeline type.", (uint)type); |
| type = UNKNOWN; |
| 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; |
| } |
| } |
| |
| namespace { |
| |
| template <typename T> |
| T signedToUnsigned(long long value) { |
| KJ_REQUIRE(value >= 0 && T(value) == value, "Value out-of-range for requested type.", value) { |
| // Use it anyway. |
| break; |
| } |
| return value; |
| } |
| |
| template <> |
| uint64_t signedToUnsigned<uint64_t>(long long value) { |
| KJ_REQUIRE(value >= 0, "Value out-of-range for requested type.", value) { |
| // Use it anyway. |
| break; |
| } |
| return value; |
| } |
| |
| template <typename T> |
| T unsignedToSigned(unsigned long long value) { |
| KJ_REQUIRE(T(value) >= 0 && (unsigned long long)T(value) == value, |
| "Value out-of-range for requested type.", value) { |
| // Use it anyway. |
| break; |
| } |
| return value; |
| } |
| |
| template <> |
| int64_t unsignedToSigned<int64_t>(unsigned long long value) { |
| KJ_REQUIRE(int64_t(value) >= 0, "Value out-of-range for requested type.", value) { |
| // Use it anyway. |
| break; |
| } |
| return value; |
| } |
| |
| template <typename T, typename U> |
| T checkRoundTrip(U value) { |
| T result = value; |
| KJ_REQUIRE(U(result) == value, "Value out-of-range for requested type.", value) { |
| // Use it anyway. |
| break; |
| } |
| return result; |
| } |
| |
| template <typename T, typename U> |
| T checkRoundTripFromFloat(U value) { |
| // When `U` is `float` or `double`, we have to use a different approach, because casting an |
| // out-of-range float to an integer is, surprisingly, UB. |
| constexpr T MIN = kj::minValue; |
| constexpr T MAX = kj::maxValue; |
| KJ_REQUIRE(value >= U(MIN), "Value out-of-range for requested type.", value) { |
| return MIN; |
| } |
| KJ_REQUIRE(value <= U(MAX), "Value out-of-range for requested type.", value) { |
| return MAX; |
| } |
| T result = value; |
| KJ_REQUIRE(U(result) == value, "Value out-of-range for requested type.", value) { |
| // Use it anyway. |
| break; |
| } |
| return result; |
| } |
| |
| } // namespace |
| |
| #define HANDLE_NUMERIC_TYPE(typeName, ifInt, ifUint, ifFloat) \ |
| typeName DynamicValue::Reader::AsImpl<typeName>::apply(const Reader& reader) { \ |
| 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: \ |
| KJ_FAIL_REQUIRE("Value type mismatch.") { \ |
| return 0; \ |
| } \ |
| } \ |
| } \ |
| typeName DynamicValue::Builder::AsImpl<typeName>::apply(Builder& builder) { \ |
| 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: \ |
| KJ_FAIL_REQUIRE("Value type mismatch.") { \ |
| return 0; \ |
| } \ |
| } \ |
| } |
| |
| HANDLE_NUMERIC_TYPE(int8_t, checkRoundTrip, unsignedToSigned, checkRoundTripFromFloat) |
| HANDLE_NUMERIC_TYPE(int16_t, checkRoundTrip, unsignedToSigned, checkRoundTripFromFloat) |
| HANDLE_NUMERIC_TYPE(int32_t, checkRoundTrip, unsignedToSigned, checkRoundTripFromFloat) |
| HANDLE_NUMERIC_TYPE(int64_t, kj::implicitCast, unsignedToSigned, checkRoundTripFromFloat) |
| HANDLE_NUMERIC_TYPE(uint8_t, signedToUnsigned, checkRoundTrip, checkRoundTripFromFloat) |
| HANDLE_NUMERIC_TYPE(uint16_t, signedToUnsigned, checkRoundTrip, checkRoundTripFromFloat) |
| HANDLE_NUMERIC_TYPE(uint32_t, signedToUnsigned, checkRoundTrip, checkRoundTripFromFloat) |
| HANDLE_NUMERIC_TYPE(uint64_t, signedToUnsigned, kj::implicitCast, checkRoundTripFromFloat) |
| HANDLE_NUMERIC_TYPE(float, kj::implicitCast, kj::implicitCast, kj::implicitCast) |
| HANDLE_NUMERIC_TYPE(double, kj::implicitCast, kj::implicitCast, kj::implicitCast) |
| |
| #undef HANDLE_NUMERIC_TYPE |
| |
| #define HANDLE_TYPE(name, discrim, typeName) \ |
| ReaderFor<typeName> DynamicValue::Reader::AsImpl<typeName>::apply(const Reader& reader) { \ |
| KJ_REQUIRE(reader.type == discrim, "Value type mismatch.") { \ |
| return ReaderFor<typeName>(); \ |
| } \ |
| return reader.name##Value; \ |
| } \ |
| BuilderFor<typeName> DynamicValue::Builder::AsImpl<typeName>::apply(Builder& builder) { \ |
| KJ_REQUIRE(builder.type == discrim, "Value type mismatch."); \ |
| 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) |
| HANDLE_TYPE(anyPointer, ANY_POINTER, AnyPointer) |
| |
| #undef HANDLE_TYPE |
| |
| 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); |
| } |
| |
| Data::Reader DynamicValue::Reader::AsImpl<Data>::apply(const Reader& reader) { |
| if (reader.type == TEXT) { |
| // Coerce text to data. |
| return reader.textValue.asBytes(); |
| } |
| 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. |
| return builder.textValue.asBytes(); |
| } |
| KJ_REQUIRE(builder.type == DATA, "Value type mismatch.") { |
| return BuilderFor<Data>(); |
| } |
| return builder.dataValue; |
| } |
| |
| // As in the header, HANDLE_TYPE(void, VOID, Void) crashes GCC 4.7. |
| Void DynamicValue::Reader::AsImpl<Void>::apply(const Reader& reader) { |
| KJ_REQUIRE(reader.type == VOID, "Value type mismatch.") { |
| return Void(); |
| } |
| return reader.voidValue; |
| } |
| Void DynamicValue::Builder::AsImpl<Void>::apply(Builder& builder) { |
| KJ_REQUIRE(builder.type == VOID, "Value type mismatch.") { |
| return Void(); |
| } |
| return builder.voidValue; |
| } |
| |
| // ======================================================================================= |
| |
| namespace _ { // private |
| |
| DynamicStruct::Reader PointerHelpers<DynamicStruct, Kind::OTHER>::getDynamic( |
| PointerReader reader, StructSchema schema) { |
| KJ_REQUIRE(!schema.getProto().getStruct().getIsGroup(), |
| "Cannot form pointer to group type."); |
| return DynamicStruct::Reader(schema, reader.getStruct(nullptr)); |
| } |
| DynamicStruct::Builder PointerHelpers<DynamicStruct, Kind::OTHER>::getDynamic( |
| PointerBuilder builder, StructSchema schema) { |
| KJ_REQUIRE(!schema.getProto().getStruct().getIsGroup(), |
| "Cannot form pointer to group type."); |
| return DynamicStruct::Builder(schema, builder.getStruct( |
| structSizeFromSchema(schema), nullptr)); |
| } |
| void PointerHelpers<DynamicStruct, Kind::OTHER>::set( |
| PointerBuilder builder, const DynamicStruct::Reader& value) { |
| KJ_REQUIRE(!value.schema.getProto().getStruct().getIsGroup(), |
| "Cannot form pointer to group type."); |
| builder.setStruct(value.reader); |
| } |
| DynamicStruct::Builder PointerHelpers<DynamicStruct, Kind::OTHER>::init( |
| PointerBuilder builder, StructSchema schema) { |
| KJ_REQUIRE(!schema.getProto().getStruct().getIsGroup(), |
| "Cannot form pointer to group type."); |
| return DynamicStruct::Builder(schema, |
| builder.initStruct(structSizeFromSchema(schema))); |
| } |
| |
| DynamicList::Reader PointerHelpers<DynamicList, Kind::OTHER>::getDynamic( |
| PointerReader reader, ListSchema schema) { |
| return DynamicList::Reader(schema, |
| reader.getList(elementSizeFor(schema.whichElementType()), nullptr)); |
| } |
| DynamicList::Builder PointerHelpers<DynamicList, Kind::OTHER>::getDynamic( |
| PointerBuilder builder, ListSchema schema) { |
| if (schema.whichElementType() == schema::Type::STRUCT) { |
| return DynamicList::Builder(schema, |
| builder.getStructList( |
| structSizeFromSchema(schema.getStructElementType()), |
| nullptr)); |
| } else { |
| return DynamicList::Builder(schema, |
| builder.getList(elementSizeFor(schema.whichElementType()), nullptr)); |
| } |
| } |
| void PointerHelpers<DynamicList, Kind::OTHER>::set( |
| PointerBuilder builder, const DynamicList::Reader& value) { |
| builder.setList(value.reader); |
| } |
| DynamicList::Builder PointerHelpers<DynamicList, Kind::OTHER>::init( |
| PointerBuilder builder, ListSchema schema, uint size) { |
| if (schema.whichElementType() == schema::Type::STRUCT) { |
| return DynamicList::Builder(schema, |
| builder.initStructList(bounded(size) * ELEMENTS, |
| structSizeFromSchema(schema.getStructElementType()))); |
| } else { |
| return DynamicList::Builder(schema, |
| builder.initList(elementSizeFor(schema.whichElementType()), bounded(size) * ELEMENTS)); |
| } |
| } |
| |
| DynamicCapability::Client PointerHelpers<DynamicCapability, Kind::OTHER>::getDynamic( |
| PointerReader reader, InterfaceSchema schema) { |
| return DynamicCapability::Client(schema, reader.getCapability()); |
| } |
| DynamicCapability::Client PointerHelpers<DynamicCapability, Kind::OTHER>::getDynamic( |
| PointerBuilder builder, InterfaceSchema schema) { |
| return DynamicCapability::Client(schema, builder.getCapability()); |
| } |
| void PointerHelpers<DynamicCapability, Kind::OTHER>::set( |
| PointerBuilder builder, DynamicCapability::Client& value) { |
| builder.setCapability(value.hook->addRef()); |
| } |
| void PointerHelpers<DynamicCapability, Kind::OTHER>::set( |
| PointerBuilder builder, DynamicCapability::Client&& value) { |
| builder.setCapability(kj::mv(value.hook)); |
| } |
| |
| } // namespace _ (private) |
| |
| template <> |
| void AnyPointer::Builder::adopt<DynamicValue>(Orphan<DynamicValue>&& orphan) { |
| switch (orphan.getType()) { |
| case DynamicValue::UNKNOWN: |
| case DynamicValue::VOID: |
| case DynamicValue::BOOL: |
| case DynamicValue::INT: |
| case DynamicValue::UINT: |
| case DynamicValue::FLOAT: |
| case DynamicValue::ENUM: |
| KJ_FAIL_REQUIRE("AnyPointer cannot adopt primitive (non-object) value."); |
| |
| case DynamicValue::STRUCT: |
| case DynamicValue::LIST: |
| case DynamicValue::TEXT: |
| case DynamicValue::DATA: |
| case DynamicValue::CAPABILITY: |
| case DynamicValue::ANY_POINTER: |
| builder.adopt(kj::mv(orphan.builder)); |
| break; |
| } |
| } |
| |
| 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()))) {} |
| |
| // ------------------------------------------------------------------- |
| |
| Orphan<DynamicStruct> Orphanage::newOrphan(StructSchema schema) const { |
| return Orphan<DynamicStruct>( |
| schema, _::OrphanBuilder::initStruct(arena, capTable, structSizeFromSchema(schema))); |
| } |
| |
| Orphan<DynamicList> Orphanage::newOrphan(ListSchema schema, uint size) const { |
| if (schema.whichElementType() == schema::Type::STRUCT) { |
| return Orphan<DynamicList>(schema, _::OrphanBuilder::initStructList( |
| arena, capTable, bounded(size) * ELEMENTS, |
| structSizeFromSchema(schema.getStructElementType()))); |
| } else { |
| return Orphan<DynamicList>(schema, _::OrphanBuilder::initList( |
| arena, capTable, bounded(size) * ELEMENTS, |
| elementSizeFor(schema.whichElementType()))); |
| } |
| } |
| |
| DynamicStruct::Builder Orphan<DynamicStruct>::get() { |
| return DynamicStruct::Builder(schema, builder.asStruct(structSizeFromSchema(schema))); |
| } |
| |
| DynamicStruct::Reader Orphan<DynamicStruct>::getReader() const { |
| return DynamicStruct::Reader(schema, builder.asStructReader(structSizeFromSchema(schema))); |
| } |
| |
| DynamicList::Builder Orphan<DynamicList>::get() { |
| if (schema.whichElementType() == schema::Type::STRUCT) { |
| return DynamicList::Builder( |
| schema, builder.asStructList(structSizeFromSchema(schema.getStructElementType()))); |
| } else { |
| return DynamicList::Builder( |
| schema, builder.asList(elementSizeFor(schema.whichElementType()))); |
| } |
| } |
| |
| DynamicList::Reader Orphan<DynamicList>::getReader() const { |
| return DynamicList::Reader( |
| schema, builder.asListReader(elementSizeFor(schema.whichElementType()))); |
| } |
| |
| DynamicCapability::Client Orphan<DynamicCapability>::get() { |
| return DynamicCapability::Client(schema, builder.asCapability()); |
| } |
| |
| DynamicCapability::Client Orphan<DynamicCapability>::getReader() const { |
| return DynamicCapability::Client(schema, builder.asCapability()); |
| } |
| |
| 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; |
| case DynamicValue::CAPABILITY: interfaceSchema = value.capabilityValue.getSchema(); break; |
| case DynamicValue::ANY_POINTER: break; |
| } |
| } |
| |
| 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))); |
| case DynamicValue::CAPABILITY: |
| return DynamicCapability::Client(interfaceSchema, builder.asCapability()); |
| case DynamicValue::ANY_POINTER: |
| KJ_FAIL_REQUIRE("Can't get() an AnyPointer orphan; there is no underlying pointer to " |
| "wrap in an AnyPointer::Builder."); |
| } |
| 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))); |
| case DynamicValue::CAPABILITY: |
| return DynamicCapability::Client(interfaceSchema, builder.asCapability()); |
| case DynamicValue::ANY_POINTER: |
| KJ_FAIL_ASSERT("Can't get() an AnyPointer orphan; there is no underlying pointer to " |
| "wrap in an AnyPointer::Builder."); |
| } |
| KJ_UNREACHABLE; |
| } |
| |
| template <> |
| Orphan<AnyPointer> Orphan<DynamicValue>::releaseAs<AnyPointer>() { |
| KJ_REQUIRE(type == DynamicValue::ANY_POINTER, "Value type mismatch."); |
| type = DynamicValue::UNKNOWN; |
| return Orphan<AnyPointer>(kj::mv(builder)); |
| } |
| template <> |
| Orphan<DynamicStruct> Orphan<DynamicValue>::releaseAs<DynamicStruct>() { |
| KJ_REQUIRE(type == DynamicValue::STRUCT, "Value type mismatch."); |
| type = DynamicValue::UNKNOWN; |
| return Orphan<DynamicStruct>(structSchema, kj::mv(builder)); |
| } |
| template <> |
| Orphan<DynamicList> Orphan<DynamicValue>::releaseAs<DynamicList>() { |
| KJ_REQUIRE(type == DynamicValue::LIST, "Value type mismatch."); |
| type = DynamicValue::UNKNOWN; |
| return Orphan<DynamicList>(listSchema, kj::mv(builder)); |
| } |
| |
| template <> |
| Orphan<DynamicValue> Orphanage::newOrphanCopy<DynamicValue::Reader>( |
| DynamicValue::Reader copyFrom) const { |
| 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); |
| case DynamicValue::CAPABILITY: return newOrphanCopy(copyFrom.capabilityValue); |
| case DynamicValue::ANY_POINTER: return newOrphanCopy(copyFrom.anyPointerValue); |
| } |
| KJ_UNREACHABLE; |
| } |
| |
| } // namespace capnp |