slang_rs_reflect_utils.cpp revision 24f9b09fbb55725a6952b942418ac7902007a7f2
1#include "slang_rs_reflect_utils.h"
2
3#include <cstdlib>
4#include <cstdio>
5#include <cstring>
6
7#include <sys/stat.h>
8#include <sys/types.h>
9
10namespace slang {
11
12using std::string;
13
14string RSSlangReflectUtils::ComputePackagedPath(
15    const char *prefixPath, const char *packageName) {
16    string packaged_path(prefixPath);
17    if (!packaged_path.empty() &&
18        (packaged_path[packaged_path.length() - 1] != '/')) {
19        packaged_path += "/";
20    }
21    size_t s = packaged_path.length();
22    packaged_path += packageName;
23    while (s < packaged_path.length()) {
24        if (packaged_path[s] == '.') {
25            packaged_path[s] = '/';
26        }
27        ++s;
28    }
29    return packaged_path;
30}
31
32static string InternalFileNameConvert(const char *rsFileName, bool toLower) {
33    const char *dot = rsFileName + strlen(rsFileName);
34    const char *slash = dot - 1;
35    while (slash >= rsFileName) {
36        if (*slash == '/') {
37            break;
38        }
39        if ((*slash == '.') && (*dot == 0)) {
40            dot = slash;
41        }
42        --slash;
43    }
44    ++slash;
45    char ret[256];
46    int i = 0;
47    for (; (i < 255) && (slash < dot); ++slash) {
48        if (isalnum(*slash) || *slash == '_') {
49            if (toLower) {
50                ret[i] = tolower(*slash);
51            } else {
52                ret[i] = *slash;
53            }
54            ++i;
55        }
56    }
57    ret[i] = 0;
58    return string(ret);
59}
60
61std::string RSSlangReflectUtils::JavaClassNameFromRSFileName(
62    const char *rsFileName) {
63    return InternalFileNameConvert(rsFileName, false);
64}
65
66
67std::string RSSlangReflectUtils::BCFileNameFromRSFileName(
68    const char *rsFileName) {
69    return InternalFileNameConvert(rsFileName, true);
70}
71
72bool RSSlangReflectUtils::mkdir_p(const char *path) {
73    char buf[256];
74    char *tmp, *p = NULL;
75    size_t len = strlen(path);
76
77    if (len + 1 <= sizeof(buf))
78        tmp = buf;
79    else
80        tmp = new char[len + 1];
81
82    strcpy(tmp, path);
83
84    if (tmp[len - 1] == '/')
85        tmp[len - 1] = 0;
86
87    for (p = tmp + 1; *p; p++) {
88        if (*p == '/') {
89            *p = 0;
90            mkdir(tmp, S_IRWXU);
91            *p = '/';
92        }
93    }
94    mkdir(tmp, S_IRWXU);
95
96    if (tmp != buf)
97        delete[] tmp;
98
99    return true;
100}
101
102static bool GenerateAccessorHeader(
103    const RSSlangReflectUtils::BitCodeAccessorContext &context, FILE *pfout) {
104    fprintf(pfout, "/*\n");
105    fprintf(pfout, " * This file is auto-generated. DO NOT MODIFY!\n");
106    fprintf(pfout, " * The source RenderScript file: %s\n", context.rsFileName);
107    fprintf(pfout, " */\n\n");
108    fprintf(pfout, "package %s;\n\n", context.packageName);
109
110    // add imports here.
111
112    return true;
113}
114
115static bool GenerateAccessorMethodSignature(
116    const RSSlangReflectUtils::BitCodeAccessorContext &context, FILE *pfout) {
117    // the prototype of the accessor method
118    fprintf(pfout, "  // return byte array representation of the bitcode.\n");
119    fprintf(pfout, "  public static byte[] getBitCode() {\n");
120    return true;
121}
122
123// Java method size must not exceed 64k,
124// so we have to split the bitcode into multiple segments.
125static bool GenerateSegmentMethod(
126    const char *buff, int blen, int seg_num, FILE *pfout) {
127
128    fprintf(pfout, "  private static byte[] getSegment_%d() {\n", seg_num);
129    fprintf(pfout, "    byte[] data = {\n");
130
131    static const int LINE_BYTE_NUM = 16;
132    char out_line[LINE_BYTE_NUM*6 + 10];
133    const char *out_line_end = out_line + sizeof(out_line);
134    char *p = out_line;
135
136    int write_length = 0;
137    while (write_length < blen) {
138        p += snprintf(p, out_line_end - p,
139                      " %4d,", static_cast<int>(buff[write_length]));
140        ++write_length;
141        if (((write_length % LINE_BYTE_NUM) == 0)
142            || (write_length == blen)) {
143          fprintf(pfout, "     ");
144          fprintf(pfout, "%s", out_line);
145          fprintf(pfout, "\n");
146          p = out_line;
147        }
148    }
149
150    fprintf(pfout, "    };\n");
151    fprintf(pfout, "    return data;\n");
152    fprintf(pfout, "  }\n\n");
153
154    return true;
155}
156
157static bool GenerateJavaCodeAccessorMethod(
158    const RSSlangReflectUtils::BitCodeAccessorContext &context, FILE *pfout) {
159    FILE *pfin = fopen(context.bcFileName, "rb");
160    if (pfin == NULL) {
161        fprintf(stderr, "Error: could not read file %s\n", context.bcFileName);
162        return false;
163    }
164
165    // start the accessor method
166    GenerateAccessorMethodSignature(context, pfout);
167    fprintf(pfout, "    return getBitCodeInternal();\n");
168    // end the accessor method
169    fprintf(pfout, "  };\n\n");
170
171    // output the data
172    // make sure the generated function for a segment won't break the Javac
173    // size limitation (64K).
174    static const int SEG_SIZE = 0x2000;
175    char *buff = new char[SEG_SIZE];
176    int read_length;
177    int seg_num = 0;
178    int total_length = 0;
179    while ((read_length = fread(buff, 1, SEG_SIZE, pfin)) > 0) {
180        GenerateSegmentMethod(buff, read_length, seg_num, pfout);
181        ++seg_num;
182        total_length += read_length;
183    }
184    delete []buff;
185    fclose(pfin);
186
187    // output the internal accessor method
188    fprintf(pfout, "  private static int bitCodeLength = %d;\n\n", total_length);
189    fprintf(pfout, "  private static byte[] getBitCodeInternal() {\n");
190    fprintf(pfout, "    byte[] bc = new byte[bitCodeLength];\n");
191    fprintf(pfout, "    int offset = 0;\n");
192    fprintf(pfout, "    byte[] seg;\n");
193    for (int i = 0; i < seg_num; ++i) {
194    fprintf(pfout, "    seg = getSegment_%d();\n", i);
195    fprintf(pfout, "    System.arraycopy(seg, 0, bc, offset, seg.length);\n");
196    fprintf(pfout, "    offset += seg.length;\n");
197    }
198    fprintf(pfout, "    return bc;\n");
199    fprintf(pfout, "  }\n\n");
200
201    return true;
202}
203
204static bool GenerateAccessorClass(
205    const RSSlangReflectUtils::BitCodeAccessorContext &context,
206    const char *clazz_name, FILE *pfout) {
207    // begin the class.
208    fprintf(pfout, "/**\n");
209    fprintf(pfout, " * @hide\n");
210    fprintf(pfout, " */\n");
211    fprintf(pfout, "public class %s {\n", clazz_name);
212    fprintf(pfout, "\n");
213
214    bool ret = true;
215    switch (context.bcStorage) {
216      case BCST_APK_RESOURCE:
217        break;
218      case BCST_JAVA_CODE:
219        ret = GenerateJavaCodeAccessorMethod(context, pfout);
220        break;
221      default:
222        ret = false;
223    }
224
225    // end the class.
226    fprintf(pfout, "}\n");
227
228    return ret;
229}
230
231
232bool RSSlangReflectUtils::GenerateBitCodeAccessor(
233    const BitCodeAccessorContext &context) {
234    string output_path = ComputePackagedPath(context.reflectPath,
235                                             context.packageName);
236    if (!mkdir_p(output_path.c_str())) {
237        fprintf(stderr, "Error: could not create dir %s\n",
238                output_path.c_str());
239        return false;
240    }
241
242    string clazz_name(JavaClassNameFromRSFileName(context.rsFileName));
243    clazz_name += "BitCode";
244    string filename(clazz_name);
245    filename += ".java";
246
247    string output_filename(output_path);
248    output_filename += "/";
249    output_filename += filename;
250    printf("Generating %s ...\n", filename.c_str());
251    FILE *pfout = fopen(output_filename.c_str(), "w");
252    if (pfout == NULL) {
253        fprintf(stderr, "Error: could not write to file %s\n",
254                output_filename.c_str());
255        return false;
256    }
257
258    bool ret = GenerateAccessorHeader(context, pfout) &&
259               GenerateAccessorClass(context, clazz_name.c_str(), pfout);
260
261    fclose(pfout);
262    return ret;
263}
264}
265