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