master
h 171 lines 5.24 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 gslhelpers.h
8
9 Abstract:
10
11 This file contains various GSL helper methods.
12
13 --*/
14
15 #pragma once
16
17 #include <gsl/span>
18
19 namespace gslhelpers {
20
21 template <class T>
22 constexpr bool is_standard_layout_v = std::is_standard_layout_v<T>;
23
24 namespace details {
25 //
26 // Helper function, do not use outside of this header.
27 //
28 // A performant bounds check that ensures that it is valid to read
29 // access_size starting from span[offset].
30 //
31
32 template <class SpanType, class OffsetType, class AccessSizeType>
33 __forceinline bool is_range_okay(SpanType s, OffsetType offset, AccessSizeType access_size)
34 {
35 //
36 // Span size_bytes returns an unsigned value.
37 //
38 // If offset is signed, casting to unsigned results in it becoming a massive
39 // unsigned value.
40 //
41
42 return (
43 (s.size_bytes() >= static_cast<size_t>(access_size)) &&
44 (s.size_bytes() - static_cast<size_t>(access_size) >= static_cast<size_t>(offset)));
45 }
46
47 //
48 // Helper function, do not use outside of this header.
49 //
50 // A performant bounds check that ensures that it is valid to dereference type T
51 // from &span[offset].
52 //
53
54 template <class T, class SpanType, class OffsetType>
55 __forceinline bool is_range_okay(SpanType s, OffsetType offset)
56 {
57 return is_range_okay(s, offset, sizeof(T));
58 }
59 } // namespace details
60
61 template <class T>
62 struct CanAliasStorage
63 : public std::bool_constant<
64 std::is_same<T, gsl::byte>::value || std::is_same<T, const gsl::byte>::value || std::is_same<T, unsigned char>::value ||
65 std::is_same<T, const unsigned char>::value || std::is_same<T, signed char>::value ||
66 std::is_same<T, const signed char>::value || std::is_same<T, char>::value || std::is_same<T, const char>::value>
67 {
68 };
69
70 //
71 // get_struct is used to safely retrieve a pointer to a structure contained
72 // within a span. This is useful for tasks such as protocol parsing.
73 //
74 // offset denotes the offset in bytes from the start of the span where the
75 // structure begins.
76 //
77
78 template <class T, class SpanType, class OffsetType>
79 __forceinline T* get_struct(SpanType s, OffsetType offset)
80 {
81 static_assert(
82 CanAliasStorage<typename SpanType::element_type>::value, "You can only call get_struct on a span of bytes or chars.");
83 static_assert(is_standard_layout_v<T>, "Your destination type should be standard-layout");
84
85 Expects(details::is_range_okay<T>(s, offset));
86
87 return reinterpret_cast<T*>(s.data() + offset);
88 }
89
90 template <class T, class SpanType>
91 __forceinline T* get_struct(SpanType s)
92 {
93 return get_struct<T>(s, 0);
94 }
95
96 //
97 // try_get_struct is used to conditionally retrieve a pointer to a structure
98 // contained within a span. This is useful for tasks such as protocol parsing.
99 // This helper differs from get_struct in that it does not failfast in the
100 // case of a range violation, but rather returns nullptr.
101 //
102 // offset denotes the offset in bytes from the start of the span where the
103 // structure begins.
104 //
105
106 template <class T, class SpanType, class OffsetType>
107 __forceinline T* try_get_struct(SpanType s, OffsetType offset)
108 {
109 static_assert(
110 CanAliasStorage<typename SpanType::element_type>::value, "You can only call try_get_struct on a span of bytes or chars.");
111 static_assert(is_standard_layout_v<T>, "Your destination type should be standard-layout");
112
113 if (details::is_range_okay<T>(s, offset))
114 {
115 return reinterpret_cast<T*>(s.data() + offset);
116 }
117 else
118 {
119 return nullptr;
120 }
121 }
122
123 template <class T, class SpanType>
124 __forceinline T* try_get_struct(SpanType s)
125 {
126 return try_get_struct<T>(s, 0);
127 }
128
129 template <class ByteType = gsl::byte, class T>
130 __forceinline gsl::span<const ByteType> struct_as_bytes(const T& structure)
131 {
132 static_assert(CanAliasStorage<ByteType>::value, "struct_as_bytes may only convert to a span of bytes or chars.");
133 static_assert(is_standard_layout_v<T>, "Your input structure type should be standard-layout");
134
135 return {reinterpret_cast<const ByteType*>(&structure), sizeof(structure)};
136 }
137
138 //
139 // Convert a struct to a writeable span of bytes.
140 // Accepts alternate single byte types but defaults to gsl::byte.
141 //
142
143 template <class ByteType = gsl::byte, class T>
144 __forceinline gsl::span<ByteType> struct_as_writeable_bytes(T& structure)
145 {
146 static_assert(CanAliasStorage<ByteType>::value, "struct_as_writeable_bytes may only convert to a span of bytes or chars.");
147 static_assert(is_standard_layout_v<T>, "Your input structure type should be standard-layout");
148
149 return {reinterpret_cast<ByteType*>(&structure), sizeof(structure)};
150 }
151
152 template <class NewClass, class CurrentSpan>
153 __forceinline gsl::span<NewClass> convert_span_truncate(CurrentSpan s)
154 {
155 NewClass* ptr = reinterpret_cast<NewClass*>(s.data());
156 return {ptr, s.size_bytes() / sizeof(NewClass)};
157 }
158
159 //
160 // Convert a span of one type to another type. Throws if
161 // the new type doesn't fit correctly in the existing span.
162 //
163
164 template <class NewClass, class CurrentSpan>
165 __forceinline gsl::span<NewClass> convert_span(CurrentSpan s)
166 {
167 Expects(s.size_bytes() % sizeof(NewClass) == 0);
168
169 return convert_span_truncate<NewClass, CurrentSpan>(s);
170 }
171 } // namespace gslhelpers