forked from BigfootDev/flatbuffers
For Vector or Array of structures the dereference operator of an iterator returns the pointer to the structure. However, IndirectHelper, which is used in the implementation of this operator, is instantiated in the way that the IndirectHelper::Read returns structure by value. This is because, Vector and Array instantiate IndirectHelper with const T*, but VectorIterator instantiates IndirectHelper with T. There are three IndirectHelper template definition: first for T, second for Offset<T> and the last one for const T*. Those have different IndirectHelper:Read implementations and (more importantly) return type. This is the reason of mismatch in VectorIterator::operator* between return type declaration and what was exactly returned. That is, for Array<T,...> where T is scalar the VectorIterator is instantiated as VectorIterator<T, T>, dereference operator returns T and its implementation uses IndirectHelper<T> which Read function returns T. When T is not scalar, then VectorIterator is instantiated as VectorIterator<T, const T *>, dereference operator returns const T * and its implementation uses IndirectHelper<T> which Read function returns T. The fix is done as follows: * implement type trait is_specialization_of_Offset and is_specialization_of_Offset64, * change partial specialization of IndirectHelper with const T * that it is instantiated by T and enabled only if T is not scalar and not specialization of Offset or Offset64, * remove type differentiation (due to scalar) from Array.. The above makes the IndirectHelper able to correctly instantiate itself basing only on T. Thus, the instantiation in VectorIterator correctly instantiate IndirectHelper::Read function, especially the return type.
219 lines
7.9 KiB
C++
219 lines
7.9 KiB
C++
/*
|
|
* Copyright 2021 Google Inc. All rights reserved.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#ifndef FLATBUFFERS_BUFFER_H_
|
|
#define FLATBUFFERS_BUFFER_H_
|
|
|
|
#include <algorithm>
|
|
|
|
#include "flatbuffers/base.h"
|
|
#include "flatbuffers/stl_emulation.h"
|
|
|
|
namespace flatbuffers {
|
|
|
|
// Wrapper for uoffset_t to allow safe template specialization.
|
|
// Value is allowed to be 0 to indicate a null object (see e.g. AddOffset).
|
|
template<typename T = void> struct Offset {
|
|
// The type of offset to use.
|
|
typedef uoffset_t offset_type;
|
|
|
|
offset_type o;
|
|
Offset() : o(0) {}
|
|
Offset(const offset_type _o) : o(_o) {}
|
|
Offset<> Union() const { return o; }
|
|
bool IsNull() const { return !o; }
|
|
};
|
|
|
|
template<typename T> struct is_specialisation_of_Offset : false_type {};
|
|
template<typename T>
|
|
struct is_specialisation_of_Offset<Offset<T>> : true_type {};
|
|
|
|
// Wrapper for uoffset64_t Offsets.
|
|
template<typename T = void> struct Offset64 {
|
|
// The type of offset to use.
|
|
typedef uoffset64_t offset_type;
|
|
|
|
offset_type o;
|
|
Offset64() : o(0) {}
|
|
Offset64(const offset_type offset) : o(offset) {}
|
|
Offset64<> Union() const { return o; }
|
|
bool IsNull() const { return !o; }
|
|
};
|
|
|
|
template<typename T> struct is_specialisation_of_Offset64 : false_type {};
|
|
template<typename T>
|
|
struct is_specialisation_of_Offset64<Offset64<T>> : true_type {};
|
|
|
|
// Litmus check for ensuring the Offsets are the expected size.
|
|
static_assert(sizeof(Offset<>) == 4, "Offset has wrong size");
|
|
static_assert(sizeof(Offset64<>) == 8, "Offset64 has wrong size");
|
|
|
|
inline void EndianCheck() {
|
|
int endiantest = 1;
|
|
// If this fails, see FLATBUFFERS_LITTLEENDIAN above.
|
|
FLATBUFFERS_ASSERT(*reinterpret_cast<char *>(&endiantest) ==
|
|
FLATBUFFERS_LITTLEENDIAN);
|
|
(void)endiantest;
|
|
}
|
|
|
|
template<typename T> FLATBUFFERS_CONSTEXPR size_t AlignOf() {
|
|
// clang-format off
|
|
#ifdef _MSC_VER
|
|
return __alignof(T);
|
|
#else
|
|
#ifndef alignof
|
|
return __alignof__(T);
|
|
#else
|
|
return alignof(T);
|
|
#endif
|
|
#endif
|
|
// clang-format on
|
|
}
|
|
|
|
// Lexicographically compare two strings (possibly containing nulls), and
|
|
// return true if the first is less than the second.
|
|
static inline bool StringLessThan(const char *a_data, uoffset_t a_size,
|
|
const char *b_data, uoffset_t b_size) {
|
|
const auto cmp = memcmp(a_data, b_data, (std::min)(a_size, b_size));
|
|
return cmp == 0 ? a_size < b_size : cmp < 0;
|
|
}
|
|
|
|
// When we read serialized data from memory, in the case of most scalars,
|
|
// we want to just read T, but in the case of Offset, we want to actually
|
|
// perform the indirection and return a pointer.
|
|
// The template specialization below does just that.
|
|
// It is wrapped in a struct since function templates can't overload on the
|
|
// return type like this.
|
|
// The typedef is for the convenience of callers of this function
|
|
// (avoiding the need for a trailing return decltype)
|
|
template<typename T, typename Enable = void> struct IndirectHelper {
|
|
typedef T return_type;
|
|
typedef T mutable_return_type;
|
|
static const size_t element_stride = sizeof(T);
|
|
|
|
static return_type Read(const uint8_t *p, const size_t i) {
|
|
return EndianScalar((reinterpret_cast<const T *>(p))[i]);
|
|
}
|
|
static mutable_return_type Read(uint8_t *p, const size_t i) {
|
|
return reinterpret_cast<mutable_return_type>(
|
|
Read(const_cast<const uint8_t *>(p), i));
|
|
}
|
|
};
|
|
|
|
// For vector of Offsets.
|
|
template<typename T, template<typename> class OffsetT>
|
|
struct IndirectHelper<OffsetT<T>> {
|
|
typedef const T *return_type;
|
|
typedef T *mutable_return_type;
|
|
typedef typename OffsetT<T>::offset_type offset_type;
|
|
static const offset_type element_stride = sizeof(offset_type);
|
|
|
|
static return_type Read(const uint8_t *const p, const offset_type i) {
|
|
// Offsets are relative to themselves, so first update the pointer to
|
|
// point to the offset location.
|
|
const uint8_t *const offset_location = p + i * element_stride;
|
|
|
|
// Then read the scalar value of the offset (which may be 32 or 64-bits) and
|
|
// then determine the relative location from the offset location.
|
|
return reinterpret_cast<return_type>(
|
|
offset_location + ReadScalar<offset_type>(offset_location));
|
|
}
|
|
static mutable_return_type Read(uint8_t *const p, const offset_type i) {
|
|
// Offsets are relative to themselves, so first update the pointer to
|
|
// point to the offset location.
|
|
uint8_t *const offset_location = p + i * element_stride;
|
|
|
|
// Then read the scalar value of the offset (which may be 32 or 64-bits) and
|
|
// then determine the relative location from the offset location.
|
|
return reinterpret_cast<mutable_return_type>(
|
|
offset_location + ReadScalar<offset_type>(offset_location));
|
|
}
|
|
};
|
|
|
|
// For vector of structs.
|
|
template<typename T>
|
|
struct IndirectHelper<
|
|
T, typename std::enable_if<
|
|
!std::is_scalar<typename std::remove_pointer<T>::type>::value &&
|
|
!is_specialisation_of_Offset<T>::value &&
|
|
!is_specialisation_of_Offset64<T>::value>::type> {
|
|
private:
|
|
typedef typename std::remove_pointer<typename std::remove_cv<T>::type>::type
|
|
pointee_type;
|
|
|
|
public:
|
|
typedef const pointee_type *return_type;
|
|
typedef pointee_type *mutable_return_type;
|
|
static const size_t element_stride = sizeof(pointee_type);
|
|
|
|
static return_type Read(const uint8_t *const p, const size_t i) {
|
|
// Structs are stored inline, relative to the first struct pointer.
|
|
return reinterpret_cast<return_type>(p + i * element_stride);
|
|
}
|
|
static mutable_return_type Read(uint8_t *const p, const size_t i) {
|
|
// Structs are stored inline, relative to the first struct pointer.
|
|
return reinterpret_cast<mutable_return_type>(p + i * element_stride);
|
|
}
|
|
};
|
|
|
|
/// @brief Get a pointer to the file_identifier section of the buffer.
|
|
/// @return Returns a const char pointer to the start of the file_identifier
|
|
/// characters in the buffer. The returned char * has length
|
|
/// 'flatbuffers::FlatBufferBuilder::kFileIdentifierLength'.
|
|
/// This function is UNDEFINED for FlatBuffers whose schema does not include
|
|
/// a file_identifier (likely points at padding or the start of a the root
|
|
/// vtable).
|
|
inline const char *GetBufferIdentifier(const void *buf,
|
|
bool size_prefixed = false) {
|
|
return reinterpret_cast<const char *>(buf) +
|
|
((size_prefixed) ? 2 * sizeof(uoffset_t) : sizeof(uoffset_t));
|
|
}
|
|
|
|
// Helper to see if the identifier in a buffer has the expected value.
|
|
inline bool BufferHasIdentifier(const void *buf, const char *identifier,
|
|
bool size_prefixed = false) {
|
|
return strncmp(GetBufferIdentifier(buf, size_prefixed), identifier,
|
|
flatbuffers::kFileIdentifierLength) == 0;
|
|
}
|
|
|
|
/// @cond FLATBUFFERS_INTERNAL
|
|
// Helpers to get a typed pointer to the root object contained in the buffer.
|
|
template<typename T> T *GetMutableRoot(void *buf) {
|
|
if (!buf) return nullptr;
|
|
EndianCheck();
|
|
return reinterpret_cast<T *>(
|
|
reinterpret_cast<uint8_t *>(buf) +
|
|
EndianScalar(*reinterpret_cast<uoffset_t *>(buf)));
|
|
}
|
|
|
|
template<typename T, typename SizeT = uoffset_t>
|
|
T *GetMutableSizePrefixedRoot(void *buf) {
|
|
return GetMutableRoot<T>(reinterpret_cast<uint8_t *>(buf) + sizeof(SizeT));
|
|
}
|
|
|
|
template<typename T> const T *GetRoot(const void *buf) {
|
|
return GetMutableRoot<T>(const_cast<void *>(buf));
|
|
}
|
|
|
|
template<typename T, typename SizeT = uoffset_t>
|
|
const T *GetSizePrefixedRoot(const void *buf) {
|
|
return GetRoot<T>(reinterpret_cast<const uint8_t *>(buf) + sizeof(SizeT));
|
|
}
|
|
|
|
} // namespace flatbuffers
|
|
|
|
#endif // FLATBUFFERS_BUFFER_H_
|