// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_INTERNAL_H_ #define MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_INTERNAL_H_ #include #include #include "mojo/public/c/system/macros.h" #include "mojo/public/cpp/bindings/lib/bindings_internal.h" #include "mojo/public/cpp/bindings/lib/bindings_serialization.h" #include "mojo/public/cpp/bindings/lib/bounds_checker.h" #include "mojo/public/cpp/bindings/lib/buffer.h" #include "mojo/public/cpp/bindings/lib/map_data_internal.h" #include "mojo/public/cpp/bindings/lib/template_util.h" #include "mojo/public/cpp/bindings/lib/validate_params.h" #include "mojo/public/cpp/bindings/lib/validation_errors.h" #include "mojo/public/cpp/environment/logging.h" namespace mojo { template class Array; class String; namespace internal { // std::numeric_limits::max() is not a compile-time constant (until // C++11). const uint32_t kMaxUint32 = 0xFFFFFFFF; std::string MakeMessageWithArrayIndex(const char* message, size_t size, size_t index); std::string MakeMessageWithExpectedArraySize(const char* message, size_t size, size_t expected_size); template struct ArrayDataTraits { typedef T StorageType; typedef T& Ref; typedef T const& ConstRef; static const uint32_t kMaxNumElements = (kMaxUint32 - sizeof(ArrayHeader)) / sizeof(StorageType); static uint32_t GetStorageSize(uint32_t num_elements) { MOJO_DCHECK(num_elements <= kMaxNumElements); return sizeof(ArrayHeader) + sizeof(StorageType) * num_elements; } static Ref ToRef(StorageType* storage, size_t offset) { return storage[offset]; } static ConstRef ToConstRef(const StorageType* storage, size_t offset) { return storage[offset]; } }; template struct ArrayDataTraits { typedef StructPointer

StorageType; typedef P*& Ref; typedef P* const& ConstRef; static const uint32_t kMaxNumElements = (kMaxUint32 - sizeof(ArrayHeader)) / sizeof(StorageType); static uint32_t GetStorageSize(uint32_t num_elements) { MOJO_DCHECK(num_elements <= kMaxNumElements); return sizeof(ArrayHeader) + sizeof(StorageType) * num_elements; } static Ref ToRef(StorageType* storage, size_t offset) { return storage[offset].ptr; } static ConstRef ToConstRef(const StorageType* storage, size_t offset) { return storage[offset].ptr; } }; template struct ArrayDataTraits*> { typedef ArrayPointer StorageType; typedef Array_Data*& Ref; typedef Array_Data* const& ConstRef; static const uint32_t kMaxNumElements = (kMaxUint32 - sizeof(ArrayHeader)) / sizeof(StorageType); static uint32_t GetStorageSize(uint32_t num_elements) { MOJO_DCHECK(num_elements <= kMaxNumElements); return sizeof(ArrayHeader) + sizeof(StorageType) * num_elements; } static Ref ToRef(StorageType* storage, size_t offset) { return storage[offset].ptr; } static ConstRef ToConstRef(const StorageType* storage, size_t offset) { return storage[offset].ptr; } }; // Specialization of Arrays for bools, optimized for space. It has the // following differences from a generalized Array: // * Each element takes up a single bit of memory. // * Accessing a non-const single element uses a helper class |BitRef|, which // emulates a reference to a bool. template <> struct ArrayDataTraits { // Helper class to emulate a reference to a bool, used for direct element // access. class BitRef { public: ~BitRef(); BitRef& operator=(bool value); BitRef& operator=(const BitRef& value); operator bool() const; private: friend struct ArrayDataTraits; BitRef(uint8_t* storage, uint8_t mask); BitRef(); uint8_t* storage_; uint8_t mask_; }; // Because each element consumes only 1/8 byte. static const uint32_t kMaxNumElements = kMaxUint32; typedef uint8_t StorageType; typedef BitRef Ref; typedef bool ConstRef; static uint32_t GetStorageSize(uint32_t num_elements) { return sizeof(ArrayHeader) + ((num_elements + 7) / 8); } static BitRef ToRef(StorageType* storage, size_t offset) { return BitRef(&storage[offset / 8], 1 << (offset % 8)); } static bool ToConstRef(const StorageType* storage, size_t offset) { return (storage[offset / 8] & (1 << (offset % 8))) != 0; } }; // What follows is code to support the serialization of Array_Data. There // are two interesting cases: arrays of primitives and arrays of objects. // Arrays of objects are represented as arrays of pointers to objects. template struct ArraySerializationHelper; template struct ArraySerializationHelper { typedef typename ArrayDataTraits::StorageType ElementType; static void EncodePointersAndHandles(const ArrayHeader* header, ElementType* elements, std::vector* handles) {} static void DecodePointersAndHandles(const ArrayHeader* header, ElementType* elements, std::vector* handles) {} template static bool ValidateElements(const ArrayHeader* header, const ElementType* elements, BoundsChecker* bounds_checker) { static_assert(!element_is_nullable, "Primitive type should be non-nullable"); static_assert((IsSame::value), "Primitive type should not have array validate params"); return true; } }; template <> struct ArraySerializationHelper { typedef ArrayDataTraits::StorageType ElementType; static void EncodePointersAndHandles(const ArrayHeader* header, ElementType* elements, std::vector* handles); static void DecodePointersAndHandles(const ArrayHeader* header, ElementType* elements, std::vector* handles); template static bool ValidateElements(const ArrayHeader* header, const ElementType* elements, BoundsChecker* bounds_checker) { static_assert((IsSame::value), "Handle type should not have array validate params"); for (uint32_t i = 0; i < header->num_elements; ++i) { if (!element_is_nullable && elements[i].value() == kEncodedInvalidHandleValue) { ReportValidationError( VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE, MakeMessageWithArrayIndex( "invalid handle in array expecting valid handles", header->num_elements, i).c_str()); return false; } if (!bounds_checker->ClaimHandle(elements[i])) { ReportValidationError(VALIDATION_ERROR_ILLEGAL_HANDLE); return false; } } return true; } }; template struct ArraySerializationHelper { typedef typename ArrayDataTraits::StorageType ElementType; static void EncodePointersAndHandles(const ArrayHeader* header, ElementType* elements, std::vector* handles) { ArraySerializationHelper::EncodePointersAndHandles( header, elements, handles); } static void DecodePointersAndHandles(const ArrayHeader* header, ElementType* elements, std::vector* handles) { ArraySerializationHelper::DecodePointersAndHandles( header, elements, handles); } template static bool ValidateElements(const ArrayHeader* header, const ElementType* elements, BoundsChecker* bounds_checker) { return ArraySerializationHelper::ValidateElements< element_is_nullable, ElementValidateParams>(header, elements, bounds_checker); } }; template struct ArraySerializationHelper { typedef typename ArrayDataTraits::StorageType ElementType; static void EncodePointersAndHandles(const ArrayHeader* header, ElementType* elements, std::vector* handles) { for (uint32_t i = 0; i < header->num_elements; ++i) Encode(&elements[i], handles); } static void DecodePointersAndHandles(const ArrayHeader* header, ElementType* elements, std::vector* handles) { for (uint32_t i = 0; i < header->num_elements; ++i) Decode(&elements[i], handles); } template static bool ValidateElements(const ArrayHeader* header, const ElementType* elements, BoundsChecker* bounds_checker) { for (uint32_t i = 0; i < header->num_elements; ++i) { if (!element_is_nullable && !elements[i].offset) { ReportValidationError( VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, MakeMessageWithArrayIndex("null in array expecting valid pointers", header->num_elements, i).c_str()); return false; } if (!ValidateEncodedPointer(&elements[i].offset)) { ReportValidationError(VALIDATION_ERROR_ILLEGAL_POINTER); return false; } if (!ValidateCaller::Run( DecodePointerRaw(&elements[i].offset), bounds_checker)) { return false; } } return true; } private: template struct ValidateCaller { static bool Run(const void* data, BoundsChecker* bounds_checker) { static_assert((IsSame::value), "Struct type should not have array validate params"); return T::Validate(data, bounds_checker); } }; template struct ValidateCaller, Params> { static bool Run(const void* data, BoundsChecker* bounds_checker) { return Map_Data::template Validate(data, bounds_checker); } }; template struct ValidateCaller, Params> { static bool Run(const void* data, BoundsChecker* bounds_checker) { return Array_Data::template Validate(data, bounds_checker); } }; }; template class Array_Data { public: typedef ArrayDataTraits Traits; typedef typename Traits::StorageType StorageType; typedef typename Traits::Ref Ref; typedef typename Traits::ConstRef ConstRef; typedef ArraySerializationHelper::value> Helper; // Returns null if |num_elements| or the corresponding storage size cannot be // stored in uint32_t. static Array_Data* New(size_t num_elements, Buffer* buf) { if (num_elements > Traits::kMaxNumElements) return nullptr; uint32_t num_bytes = Traits::GetStorageSize(static_cast(num_elements)); return new (buf->Allocate(num_bytes)) Array_Data(num_bytes, static_cast(num_elements)); } template static bool Validate(const void* data, BoundsChecker* bounds_checker) { if (!data) return true; if (!IsAligned(data)) { ReportValidationError(VALIDATION_ERROR_MISALIGNED_OBJECT); return false; } if (!bounds_checker->IsValidRange(data, sizeof(ArrayHeader))) { ReportValidationError(VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE); return false; } const ArrayHeader* header = static_cast(data); if (header->num_elements > Traits::kMaxNumElements || header->num_bytes < Traits::GetStorageSize(header->num_elements)) { ReportValidationError(VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER); return false; } if (Params::expected_num_elements != 0 && header->num_elements != Params::expected_num_elements) { ReportValidationError(VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER, MakeMessageWithExpectedArraySize( "fixed-size array has wrong number of elements", header->num_elements, Params::expected_num_elements).c_str()); return false; } if (!bounds_checker->ClaimMemory(data, header->num_bytes)) { ReportValidationError(VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE); return false; } const Array_Data* object = static_cast*>(data); return Helper::template ValidateElements< Params::element_is_nullable, typename Params::ElementValidateParams>( &object->header_, object->storage(), bounds_checker); } size_t size() const { return header_.num_elements; } Ref at(size_t offset) { MOJO_DCHECK(offset < static_cast(header_.num_elements)); return Traits::ToRef(storage(), offset); } ConstRef at(size_t offset) const { MOJO_DCHECK(offset < static_cast(header_.num_elements)); return Traits::ToConstRef(storage(), offset); } StorageType* storage() { return reinterpret_cast(reinterpret_cast(this) + sizeof(*this)); } const StorageType* storage() const { return reinterpret_cast( reinterpret_cast(this) + sizeof(*this)); } void EncodePointersAndHandles(std::vector* handles) { Helper::EncodePointersAndHandles(&header_, storage(), handles); } void DecodePointersAndHandles(std::vector* handles) { Helper::DecodePointersAndHandles(&header_, storage(), handles); } private: Array_Data(uint32_t num_bytes, uint32_t num_elements) { header_.num_bytes = num_bytes; header_.num_elements = num_elements; } ~Array_Data() = delete; internal::ArrayHeader header_; // Elements of type internal::ArrayDataTraits::StorageType follow. }; static_assert(sizeof(Array_Data) == 8, "Bad sizeof(Array_Data)"); // UTF-8 encoded typedef Array_Data String_Data; template struct ArrayTraits {}; template struct ArrayTraits { typedef T StorageType; typedef typename std::vector::reference RefType; typedef typename std::vector::const_reference ConstRefType; typedef ConstRefType ForwardType; static inline void Initialize(std::vector* vec) {} static inline void Finalize(std::vector* vec) {} static inline ConstRefType at(const std::vector* vec, size_t offset) { return vec->at(offset); } static inline RefType at(std::vector* vec, size_t offset) { return vec->at(offset); } static inline void Resize(std::vector* vec, size_t size) { vec->resize(size); } static inline void PushBack(std::vector* vec, ForwardType value) { vec->push_back(value); } static inline void Clone(const std::vector& src_vec, std::vector* dest_vec) { dest_vec->assign(src_vec.begin(), src_vec.end()); } }; template struct ArrayTraits { struct StorageType { char buf[sizeof(T) + (8 - (sizeof(T) % 8)) % 8]; // Make 8-byte aligned. }; typedef T& RefType; typedef const T& ConstRefType; typedef T ForwardType; static inline void Initialize(std::vector* vec) { for (size_t i = 0; i < vec->size(); ++i) new (vec->at(i).buf) T(); } static inline void Finalize(std::vector* vec) { for (size_t i = 0; i < vec->size(); ++i) reinterpret_cast(vec->at(i).buf)->~T(); } static inline ConstRefType at(const std::vector* vec, size_t offset) { return *reinterpret_cast(vec->at(offset).buf); } static inline RefType at(std::vector* vec, size_t offset) { return *reinterpret_cast(vec->at(offset).buf); } static inline void Resize(std::vector* vec, size_t size) { size_t old_size = vec->size(); for (size_t i = size; i < old_size; i++) reinterpret_cast(vec->at(i).buf)->~T(); ResizeStorage(vec, size); for (size_t i = old_size; i < vec->size(); i++) new (vec->at(i).buf) T(); } static inline void PushBack(std::vector* vec, RefType value) { size_t old_size = vec->size(); ResizeStorage(vec, old_size + 1); new (vec->at(old_size).buf) T(value.Pass()); } static inline void ResizeStorage(std::vector* vec, size_t size) { if (size <= vec->capacity()) { vec->resize(size); return; } std::vector new_storage(size); for (size_t i = 0; i < vec->size(); i++) new (new_storage.at(i).buf) T(at(vec, i).Pass()); vec->swap(new_storage); Finalize(&new_storage); } static inline void Clone(const std::vector& src_vec, std::vector* dest_vec) { Resize(dest_vec, src_vec.size()); for (size_t i = 0; i < src_vec.size(); ++i) at(dest_vec, i) = at(&src_vec, i).Clone(); } }; template <> struct WrapperTraits { typedef String_Data* DataType; }; } // namespace internal } // namespace mojo #endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_INTERNAL_H_