1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include <assert.h>
20 #include <stddef.h>
21 #include <stdint.h>
22 #include <stdlib.h>
23 #include <string.h>
24 
25 namespace emugl {
26 
27 // A helper template to extract values form the wire protocol stream
28 // and convert them to appropriate host values.
29 //
30 // The wire protocol uses 32-bit exclusively when transferring
31 // GLintptr or GLsizei values, as well as opaque handles like GLeglImage,
32 // from the guest (even when the guest is 64-bit).
33 //
34 // The corresponding host definitions depend on the host bitness. For
35 // example, GLintptr is 64-bit on linux-x86_64. The following is a set
36 // of templates that can simplify the conversion of protocol values
37 // into host ones.
38 //
39 // The most important one is:
40 //
41 //     unpack<HOST_TYPE,SIZE_TYPE>(const void* ptr)
42 //
43 // Which reads bytes from |ptr|, using |SIZE_TYPE| as the underlying
44 // sized-integer specifier (e.g. 'uint32_t'), and converting the result
45 // into a |HOST_TYPE| value. For example:
46 //
47 //     unpack<EGLImage,uint32_t>(ptr + 12);
48 //
49 // will read a 4-byte value from |ptr + 12| and convert it into
50 // an EGLImage, which is a host void*. The template detects host
51 // pointer types to perform proper type casting.
52 //
53 // TODO(digit): Add custom unpackers to handle generic opaque void* values.
54 //              and map them to unique 32-bit values.
55 
56 template <typename T, typename S>
57 struct UnpackerT {
unpackUnpackerT58     static T unpack(const void* ptr) {
59         static_assert(sizeof(T) == sizeof(S),
60                       "Bad input arguments, have to be of the same size");
61         return *(const T*)ptr;
62     }
63 };
64 
65 template <typename T, typename S>
66 struct UnpackerT<T*, S> {
67     static T* unpack(const void* ptr) {
68         return (T*)(uintptr_t)(*(const S*)ptr);
69     }
70 };
71 
72 template <>
73 struct UnpackerT<ssize_t, uint32_t> {
74     static ssize_t unpack(const void* ptr) {
75         return (ssize_t)*(const int32_t*)ptr;
76     }
77 };
78 
79 template <typename T, typename S>
80 inline T Unpack(const void* ptr) {
81     return UnpackerT<T, S>::unpack(ptr);
82 }
83 
84 // Helper classes GenericInputBuffer and GenericOutputBuffer used to ensure
85 // input and output buffers passed to EGL/GL functions are properly aligned
86 // (preventing crashes with some backends).
87 //
88 // Usage example:
89 //
90 //    GenericInputBuffer<> inputBuffer(ptrIn, sizeIn);
91 //    GenericOutputBuffer<> outputBuffer(ptrOut, sizeOut);
92 //    glDoGetStuff(inputBuffer.get(), outputBuffer.get());
93 //    outputBuffer.flush();
94 //
95 // get() will return the original value of |ptr| if it was aligned on the
96 // configured boundary (8 bytes by default). Otherwise, it will return the
97 // address of an aligned copy of the original |size| bytes starting from |ptr|.
98 //
99 // Allowed alignment values are 1, 2, 4, 8.
100 //
101 // outputBuffer.flush() copies the content of the copy back to |ptr| explictly,
102 // if needed. It is a no-op if |ptr| was aligned.
103 //
104 // Both classes try to minimize heap usage as much as possible - the first
105 // template argument defines the size of an internal array Generic*Buffer-s use
106 // if the |ptr|'s |size| is small enough. If it doesn't fit into the internal
107 // array, an aligned copy is allocated on the heap and freed in the dtor.
108 
109 template <size_t StackSize = 1024, size_t Align = 8>
110 class GenericInputBuffer {
111     static_assert(Align == 1 || Align == 2 || Align == 4 || Align == 8,
112                   "Bad alignment parameter");
113 
114 public:
115     GenericInputBuffer(const void* input, size_t size) : mOrigBuff(input) {
116         if (((uintptr_t)input & (Align - 1U)) == 0) {
117             mPtr = const_cast<void*>(input);
118         } else {
119             if (size <= StackSize) {
120                 mPtr = &mArray[0];
121             } else {
122                 mPtr = malloc(size);
123             }
124             memcpy(mPtr, input, size);
125         }
126     }
127 
128     ~GenericInputBuffer() {
129         if (mPtr != mOrigBuff && mPtr != &mArray[0]) {
130             free(mPtr);
131         }
132     }
133 
134     const void* get() const { return mPtr; }
135 
136 private:
137     // A pointer to the aligned buffer, might point either to mOrgBuf, to mArray
138     // start or to a heap-allocated chunk of data.
139     void* mPtr;
140     // Original buffer.
141     const void* mOrigBuff;
142     // Inplace aligned array for small enough buffers.
143     char __attribute__((__aligned__(Align))) mArray[StackSize];
144 };
145 
146 template <size_t StackSize = 1024, size_t Align = 8>
147 class GenericOutputBuffer {
148     static_assert(Align == 1 || Align == 2 || Align == 4 || Align == 8,
149                   "Bad alignment parameter");
150 
151 public:
152     GenericOutputBuffer(unsigned char* ptr, size_t size) :
153             mOrigBuff(ptr), mSize(size) {
154         if (((uintptr_t)ptr & (Align - 1U)) == 0) {
155             mPtr = ptr;
156         } else {
157             if (size <= StackSize) {
158                 mPtr = &mArray[0];
159             } else {
160                 mPtr = calloc(1, size);
161             }
162         }
163     }
164 
165     ~GenericOutputBuffer() {
166         if (mPtr != mOrigBuff && mPtr != &mArray[0]) {
167             free(mPtr);
168         }
169     }
170 
171     void* get() const { return mPtr; }
172 
173     void flush() {
174         if (mPtr != mOrigBuff) {
175             memcpy(mOrigBuff, mPtr, mSize);
176         }
177     }
178 
179 private:
180     // A pointer to the aligned buffer, might point either to mOrgBuf, to mArray
181     // start or to a heap-allocated chunk of data.
182     void* mPtr;
183     // Original buffer.
184     unsigned char* mOrigBuff;
185     // Original buffer size.
186     size_t mSize;
187     // Inplace array for small enough buffers.
188     unsigned char __attribute__((__aligned__(Align))) mArray[StackSize];
189 };
190 
191 // Pin the defaults for the commonly used type names
192 using InputBuffer = GenericInputBuffer<>;
193 using OutputBuffer = GenericOutputBuffer<>;
194 
195 }  // namespace emugl
196