1/*
2 * Copyright 2012, 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//===----------------------------------------------------------------------===//
18// This file implements RSInfo::ReadFromFile()
19//===----------------------------------------------------------------------===//
20
21#include "bcc/Renderscript/RSInfo.h"
22
23#include <new>
24
25#include <utils/FileMap.h>
26
27#include "bcc/Support/Log.h"
28#include "bcc/Support/InputFile.h"
29
30using namespace bcc;
31
32namespace {
33
34template<typename ItemType, typename ItemContainer>
35inline bool helper_read_list_item(const ItemType &pItem,
36                                  const RSInfo &pInfo,
37                                  ItemContainer &pResult);
38
39// Process DependencyTableItem in the file
40template<> inline bool
41helper_read_list_item<rsinfo::DependencyTableItem, RSInfo::DependencyTableTy>(
42    const rsinfo::DependencyTableItem &pItem,
43    const RSInfo &pInfo,
44    RSInfo::DependencyTableTy &pResult)
45{
46  const char *id = pInfo.getStringFromPool(pItem.id);
47  const uint8_t *sha1 =
48      reinterpret_cast<const uint8_t *>(pInfo.getStringFromPool(pItem.sha1));
49
50  if (id == NULL) {
51    ALOGE("Invalid string index %d for source id in RS dependenct table.",
52          pItem.id);
53    return false;
54  }
55
56  if (sha1 == NULL) {
57    ALOGE("Invalid string index %d for SHA-1 checksum in RS dependenct table.",
58          pItem.id);
59    return false;
60  }
61
62  pResult.push(std::make_pair(id, sha1));
63  return true;
64}
65
66// Process PragmaItem in the file
67template<> inline bool
68helper_read_list_item<rsinfo::PragmaItem, RSInfo::PragmaListTy>(
69    const rsinfo::PragmaItem &pItem,
70    const RSInfo &pInfo,
71    RSInfo::PragmaListTy &pResult)
72{
73  const char *key = pInfo.getStringFromPool(pItem.key);
74  const char *value =pInfo.getStringFromPool(pItem.value);
75
76  if (key == NULL) {
77    ALOGE("Invalid string index %d for key in RS pragma list.", pItem.key);
78    return false;
79  }
80
81  if (value == NULL) {
82    ALOGE("Invalid string index %d for value in RS pragma list.", pItem.value);
83    return false;
84  }
85
86  pResult.push(std::make_pair(key, value));
87  return true;
88}
89
90// Procee ObjectSlotItem in the file
91template<> inline bool
92helper_read_list_item<rsinfo::ObjectSlotItem, RSInfo::ObjectSlotListTy>(
93    const rsinfo::ObjectSlotItem &pItem,
94    const RSInfo &pInfo,
95    RSInfo::ObjectSlotListTy &pResult)
96{
97  pResult.push(pItem.slot);
98  return true;
99}
100
101// Procee ExportVarNameItem in the file
102template<> inline bool
103helper_read_list_item<rsinfo::ExportVarNameItem, RSInfo::ExportVarNameListTy>(
104    const rsinfo::ExportVarNameItem &pItem,
105    const RSInfo &pInfo,
106    RSInfo::ExportVarNameListTy &pResult)
107{
108  const char *name = pInfo.getStringFromPool(pItem.name);
109
110  if (name == NULL) {
111    ALOGE("Invalid string index %d for name in RS export vars.", pItem.name);
112    return false;
113  }
114
115  pResult.push(name);
116  return true;
117}
118
119// Procee ExportFuncNameItem in the file
120template<> inline bool
121helper_read_list_item<rsinfo::ExportFuncNameItem, RSInfo::ExportFuncNameListTy>(
122    const rsinfo::ExportFuncNameItem &pItem,
123    const RSInfo &pInfo,
124    RSInfo::ExportFuncNameListTy &pResult)
125{
126  const char *name = pInfo.getStringFromPool(pItem.name);
127
128  if (name == NULL) {
129    ALOGE("Invalid string index %d for name in RS export funcs.", pItem.name);
130    return false;
131  }
132
133  pResult.push(name);
134  return true;
135}
136
137// Procee ExportForeachFuncItem in the file
138template<> inline bool
139helper_read_list_item<rsinfo::ExportForeachFuncItem, RSInfo::ExportForeachFuncListTy>(
140    const rsinfo::ExportForeachFuncItem &pItem,
141    const RSInfo &pInfo,
142    RSInfo::ExportForeachFuncListTy &pResult)
143{
144  const char *name = pInfo.getStringFromPool(pItem.name);
145
146  if (name == NULL) {
147    ALOGE("Invalid string index %d for name in RS export foreachs.", pItem.name);
148    return false;
149  }
150
151  pResult.push(std::make_pair(name, pItem.signature));
152  return true;
153}
154
155template<typename ItemType, typename ItemContainer>
156inline bool helper_read_list(const uint8_t *pData,
157                             const RSInfo &pInfo,
158                             const rsinfo::ListHeader &pHeader,
159                             ItemContainer &pResult) {
160  const ItemType *item;
161
162  // Out-of-range exception has been checked.
163  for (uint32_t i = 0; i < pHeader.count; i++) {
164    item = reinterpret_cast<const ItemType *>(pData +
165                                              pHeader.offset +
166                                              i * pHeader.itemSize);
167    if (!helper_read_list_item<ItemType, ItemContainer>(*item, pInfo, pResult)) {
168      return false;
169    }
170  }
171  return true;
172}
173
174} // end anonymous namespace
175
176RSInfo *RSInfo::ReadFromFile(InputFile &pInput, const DependencyTableTy &pDeps) {
177  android::FileMap *map = NULL;
178  RSInfo *result = NULL;
179  const uint8_t *data;
180  const rsinfo::Header *header;
181  size_t filesize;
182  const char *input_filename = pInput.getName().c_str();
183  const off_t cur_input_offset = pInput.tell();
184
185  if (pInput.hasError()) {
186    ALOGE("Invalid RS info file %s! (%s)", input_filename,
187                                           pInput.getErrorMessage().c_str());
188    goto bail;
189  }
190
191  filesize = pInput.getSize();
192  if (pInput.hasError()) {
193    ALOGE("Failed to get the size of RS info file %s! (%s)",
194          input_filename, pInput.getErrorMessage().c_str());
195    goto bail;
196  }
197
198  // Create memory map for the file.
199  map = pInput.createMap(/* pOffset */cur_input_offset,
200                         /* pLength */filesize - cur_input_offset);
201  if (map == NULL) {
202    ALOGE("Failed to map RS info file %s to the memory! (%s)",
203          input_filename, pInput.getErrorMessage().c_str());
204    goto bail;
205  }
206
207  data = reinterpret_cast<const uint8_t *>(map->getDataPtr());
208
209  // Header starts at the beginning of the file.
210  header = reinterpret_cast<const rsinfo::Header *>(data);
211
212  // Check the magic.
213  if (::memcmp(header->magic, RSINFO_MAGIC, sizeof(header->magic)) != 0) {
214    ALOGV("Wrong magic found in the RS info file %s. Treat it as a dirty "
215          "cache.", input_filename);
216    goto bail;
217  }
218
219  // Check the version.
220  if (::memcmp(header->version,
221               RSINFO_VERSION,
222               sizeof((header->version)) != 0)) {
223    ALOGV("Mismatch the version of RS info file %s: (current) %s v.s. (file) "
224          "%s. Treat it as as a dirty cache.", input_filename, RSINFO_VERSION,
225          header->version);
226    goto bail;
227  }
228
229  // Check the size.
230  if ((header->headerSize != sizeof(rsinfo::Header)) ||
231      (header->dependencyTable.itemSize != sizeof(rsinfo::DependencyTableItem)) ||
232      (header->pragmaList.itemSize != sizeof(rsinfo::PragmaItem)) ||
233      (header->objectSlotList.itemSize != sizeof(rsinfo::ObjectSlotItem)) ||
234      (header->exportVarNameList.itemSize != sizeof(rsinfo::ExportVarNameItem)) ||
235      (header->exportFuncNameList.itemSize != sizeof(rsinfo::ExportFuncNameItem)) ||
236      (header->exportForeachFuncList.itemSize != sizeof(rsinfo::ExportForeachFuncItem))) {
237    ALOGW("Corrupted RS info file %s! (unexpected size found)", input_filename);
238    goto bail;
239  }
240
241  // Check the range.
242#define LIST_DATA_RANGE(_list_header) \
243  ((_list_header).offset + (_list_header).count * (_list_header).itemSize)
244  if (((header->headerSize + header->strPoolSize) > filesize) ||
245      (LIST_DATA_RANGE(header->dependencyTable) > filesize) ||
246      (LIST_DATA_RANGE(header->pragmaList) > filesize) ||
247      (LIST_DATA_RANGE(header->objectSlotList) > filesize) ||
248      (LIST_DATA_RANGE(header->exportVarNameList) > filesize) ||
249      (LIST_DATA_RANGE(header->exportFuncNameList) > filesize) ||
250      (LIST_DATA_RANGE(header->exportForeachFuncList) > filesize)) {
251    ALOGW("Corrupted RS info file %s! (data out of the range)", input_filename);
252    goto bail;
253  }
254#undef LIST_DATA_RANGE
255
256  // File seems ok, create result RSInfo object.
257  result = new (std::nothrow) RSInfo(header->strPoolSize);
258  if (result == NULL) {
259    ALOGE("Out of memory when create RSInfo object for %s!", input_filename);
260    goto bail;
261  }
262
263  // Make advice on our access pattern.
264  map->advise(android::FileMap::SEQUENTIAL);
265
266  // Copy the header.
267  ::memcpy(&result->mHeader, header, sizeof(rsinfo::Header));
268
269  if (header->strPoolSize > 0) {
270    // Copy the string pool. The string pool is immediately after the header at
271    // the offset header->headerSize.
272    if (result->mStringPool == NULL) {
273      ALOGE("Out of memory when allocate string pool for RS info file %s!",
274            input_filename);
275      goto bail;
276    }
277    ::memcpy(result->mStringPool, data + result->mHeader.headerSize,
278             result->mHeader.strPoolSize);
279  }
280
281  // Populate all the data to the result object.
282  if (!helper_read_list<rsinfo::DependencyTableItem, DependencyTableTy>
283        (data, *result, header->dependencyTable, result->mDependencyTable)) {
284    goto bail;
285  }
286
287  // Check dependency to see whether the cache is dirty or not.
288  if (!CheckDependency(*result, pInput.getName().c_str(), pDeps)) {
289    goto bail;
290  }
291
292  if (!helper_read_list<rsinfo::PragmaItem, PragmaListTy>
293        (data, *result, header->pragmaList, result->mPragmas)) {
294    goto bail;
295  }
296
297  if (!helper_read_list<rsinfo::ObjectSlotItem, ObjectSlotListTy>
298        (data, *result, header->objectSlotList, result->mObjectSlots)) {
299    goto bail;
300  }
301
302  if (!helper_read_list<rsinfo::ExportVarNameItem, ExportVarNameListTy>
303        (data, *result, header->exportVarNameList, result->mExportVarNames)) {
304    goto bail;
305  }
306
307  if (!helper_read_list<rsinfo::ExportFuncNameItem, ExportFuncNameListTy>
308        (data, *result, header->exportFuncNameList, result->mExportFuncNames)) {
309    goto bail;
310  }
311
312  if (!helper_read_list<rsinfo::ExportForeachFuncItem, ExportForeachFuncListTy>
313        (data, *result, header->exportForeachFuncList, result->mExportForeachFuncs)) {
314    goto bail;
315  }
316
317  // Clean up.
318  map->release();
319
320  return result;
321
322bail:
323  if (map != NULL) {
324    map->release();
325  }
326
327  delete result;
328
329  return NULL;
330} // RSInfo::ReadFromFile
331