/src/leptonica/src/stringcode.c
Line | Count | Source (jump to first uncovered line) |
1 | | /*====================================================================* |
2 | | - Copyright (C) 2001 Leptonica. All rights reserved. |
3 | | - |
4 | | - Redistribution and use in source and binary forms, with or without |
5 | | - modification, are permitted provided that the following conditions |
6 | | - are met: |
7 | | - 1. Redistributions of source code must retain the above copyright |
8 | | - notice, this list of conditions and the following disclaimer. |
9 | | - 2. Redistributions in binary form must reproduce the above |
10 | | - copyright notice, this list of conditions and the following |
11 | | - disclaimer in the documentation and/or other materials |
12 | | - provided with the distribution. |
13 | | - |
14 | | - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
15 | | - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
16 | | - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
17 | | - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY |
18 | | - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
19 | | - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
20 | | - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
21 | | - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
22 | | - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
23 | | - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
24 | | - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
25 | | *====================================================================*/ |
26 | | |
27 | | /*! |
28 | | * \file stringcode.c |
29 | | * <pre> |
30 | | * |
31 | | * Generation of code for storing and extracting serializable |
32 | | * leptonica objects (such as pixa, recog, ...). |
33 | | * |
34 | | * The input is a set of files with serialized data. |
35 | | * The output is two files, that must be compiled and linked: |
36 | | * ~ autogen.*.c: code for base64 unencoding the strings and |
37 | | * deserializing the result. |
38 | | * ~ autogen.*.h: function prototypes and base64 encoded strings |
39 | | * of the input data |
40 | | * |
41 | | * This should work for any data structures in leptonica that have |
42 | | * *Write() and *Read() serialization functions. An array of 20 |
43 | | * of these, including the Pix, is given below. (The Pix is a special |
44 | | * case, because it is serialized by standardized compression |
45 | | * techniques, instead of a file format determined by leptonica.) |
46 | | * |
47 | | * Each time the generator function is invoked, three sets of strings are |
48 | | * produced, which are written into their respective string arrays: |
49 | | * ~ string of serialized, gzipped and base 64 encoded data |
50 | | * ~ case string for base64 decoding, gunzipping and deserialization, |
51 | | * to return the data struct in memory |
52 | | * ~ description string for selecting which struct to return |
53 | | * To create the two output files, a finalize function is invoked. |
54 | | * |
55 | | * There are two ways to do this, both shown in prog/autogentest1.c. |
56 | | * ~ Explicitly call strcodeGenerate() for each file with the |
57 | | * serialized data structure, followed by strcodeFinalize(). |
58 | | * ~ Put the filenames of the serialized data structures in a file, |
59 | | * and call strcodeCreateFromFile(). |
60 | | * |
61 | | * The generated code in autogen.X.c and autogen.X.h (where X is an |
62 | | * integer supplied to strcodeCreate()) is then compiled, and the |
63 | | * original data can be regenerated using the function l_autodecode_X(). |
64 | | * A test example is found in the two prog files: |
65 | | * prog/autogentest1.c -- generates autogen.137.c, autogen.137.h |
66 | | * prog/autogentest2.c -- uses autogen.137.c, autogen.137.h |
67 | | * In general, the generator (e.g., autogentest1) would be compiled and |
68 | | * run before compiling and running the application (e.g., autogentest2). |
69 | | * |
70 | | * L_STRCODE *strcodeCreate() |
71 | | * static void strcodeDestroy() (called as part of finalize) |
72 | | * void strcodeCreateFromFile() |
73 | | * l_int32 strcodeGenerate() |
74 | | * l_int32 strcodeFinalize() |
75 | | * l_int32 l_getStructStrFromFile() (useful externally) |
76 | | * |
77 | | * Static helpers |
78 | | * static l_int32 l_getIndexFromType() |
79 | | * static l_int32 l_getIndexFromStructname() |
80 | | * static l_int32 l_getIndexFromFile() |
81 | | * static char *l_genDataString() |
82 | | * static char *l_genCaseString() |
83 | | * static char *l_genDescrString() |
84 | | * </pre> |
85 | | */ |
86 | | |
87 | | #ifdef HAVE_CONFIG_H |
88 | | #include <config_auto.h> |
89 | | #endif /* HAVE_CONFIG_H */ |
90 | | |
91 | | #include <string.h> |
92 | | #include "allheaders.h" |
93 | | #include "stringcode.h" |
94 | | |
95 | 0 | #define TEMPLATE1 "stringtemplate1.txt" /* for assembling autogen.*.c */ |
96 | 0 | #define TEMPLATE2 "stringtemplate2.txt" /* for assembling autogen.*.h */ |
97 | | |
98 | | /*! Associations between names and functions */ |
99 | | struct L_GenAssoc |
100 | | { |
101 | | l_int32 index; |
102 | | char type[16]; /* e.g., "PIXA" */ |
103 | | char structname[16]; /* e.g., "Pixa" */ |
104 | | char reader[16]; /* e.g., "pixaRead" */ |
105 | | char memreader[20]; /* e.g., "pixaReadMem" */ |
106 | | }; |
107 | | |
108 | | /*! Number of serializable data types */ |
109 | | static const l_int32 l_ntypes = 19; |
110 | | /*! Serializable data types */ |
111 | | static const struct L_GenAssoc l_assoc[] = { |
112 | | {0, "INVALID", "invalid", "invalid", "invalid" }, |
113 | | {1, "BOXA", "Boxa", "boxaRead", "boxaReadMem" }, |
114 | | {2, "BOXAA", "Boxaa", "boxaaRead", "boxaaReadMem" }, |
115 | | {3, "L_DEWARP", "Dewarp", "dewarpRead", "dewarpReadMem" }, |
116 | | {4, "L_DEWARPA", "Dewarpa", "dewarpaRead", "dewarpaReadMem" }, |
117 | | {5, "L_DNA", "L_Dna", "l_dnaRead", "l_dnaReadMem" }, |
118 | | {6, "L_DNAA", "L_Dnaa", "l_dnaaRead", "l_dnaaReadMem" }, |
119 | | {7, "DPIX", "DPix", "dpixRead", "dpixReadMem" }, |
120 | | {8, "FPIX", "FPix", "fpixRead", "fpixReadMem" }, |
121 | | {9, "NUMA", "Numa", "numaRead", "numaReadMem" }, |
122 | | {10, "NUMAA", "Numaa", "numaaRead", "numaaReadMem" }, |
123 | | {11, "PIX", "Pix", "pixRead", "pixReadMem" }, |
124 | | {12, "PIXA", "Pixa", "pixaRead", "pixaReadMem" }, |
125 | | {13, "PIXAA", "Pixaa", "pixaaRead", "pixaaReadMem" }, |
126 | | {14, "PIXACOMP", "Pixacomp", "pixacompRead", "pixacompReadMem" }, |
127 | | {15, "PIXCMAP", "Pixcmap", "pixcmapRead", "pixcmapReadMem" }, |
128 | | {16, "PTA", "Pta", "ptaRead", "ptaReadMem" }, |
129 | | {17, "PTAA", "Ptaa", "ptaaRead", "ptaaReadMem" }, |
130 | | {18, "RECOG", "Recog", "recogRead", "recogReadMem" }, |
131 | | {19, "SARRAY", "Sarray", "sarrayRead", "sarrayReadMem" } |
132 | | }; |
133 | | |
134 | | static l_int32 l_getIndexFromType(const char *type, l_int32 *pindex); |
135 | | static l_int32 l_getIndexFromStructname(const char *sn, l_int32 *pindex); |
136 | | static l_int32 l_getIndexFromFile(const char *file, l_int32 *pindex); |
137 | | static char *l_genDataString(const char *filein, l_int32 ifunc); |
138 | | static char *l_genCaseString(l_int32 ifunc, l_int32 itype); |
139 | | static char *l_genDescrString(const char *filein, l_int32 ifunc, l_int32 itype); |
140 | | |
141 | | /*---------------------------------------------------------------------*/ |
142 | | /* Stringcode functions */ |
143 | | /*---------------------------------------------------------------------*/ |
144 | | /*! |
145 | | * \brief strcodeCreate() |
146 | | * |
147 | | * \param[in] fileno integer that labels the two output files |
148 | | * \return initialized L_StrCode, or NULL on error |
149 | | * |
150 | | * <pre> |
151 | | * Notes: |
152 | | * (1) This struct exists to build two files containing code for |
153 | | * any number of data objects. The two files are named |
154 | | * autogen.[fileno].c |
155 | | * autogen.[fileno].h |
156 | | * </pre> |
157 | | */ |
158 | | L_STRCODE * |
159 | | strcodeCreate(l_int32 fileno) |
160 | 0 | { |
161 | 0 | L_STRCODE *strcode; |
162 | |
|
163 | 0 | lept_mkdir("lept/auto"); |
164 | |
|
165 | 0 | if ((strcode = (L_STRCODE *)LEPT_CALLOC(1, sizeof(L_STRCODE))) == NULL) |
166 | 0 | return (L_STRCODE *)ERROR_PTR("strcode not made", __func__, NULL); |
167 | | |
168 | 0 | strcode->fileno = fileno; |
169 | 0 | strcode->function = sarrayCreate(0); |
170 | 0 | strcode->data = sarrayCreate(0); |
171 | 0 | strcode->descr = sarrayCreate(0); |
172 | 0 | return strcode; |
173 | 0 | } |
174 | | |
175 | | |
176 | | /*! |
177 | | * \brief strcodeDestroy() |
178 | | * |
179 | | * \param[out] pstrcode will be set to null after destroying the sarrays |
180 | | * \return void |
181 | | */ |
182 | | static void |
183 | | strcodeDestroy(L_STRCODE **pstrcode) |
184 | 0 | { |
185 | 0 | L_STRCODE *strcode; |
186 | |
|
187 | 0 | if (pstrcode == NULL) { |
188 | 0 | L_WARNING("ptr address is null!\n", __func__); |
189 | 0 | return; |
190 | 0 | } |
191 | | |
192 | 0 | if ((strcode = *pstrcode) == NULL) |
193 | 0 | return; |
194 | | |
195 | 0 | sarrayDestroy(&strcode->function); |
196 | 0 | sarrayDestroy(&strcode->data); |
197 | 0 | sarrayDestroy(&strcode->descr); |
198 | 0 | LEPT_FREE(strcode); |
199 | 0 | *pstrcode = NULL; |
200 | 0 | } |
201 | | |
202 | | |
203 | | /*! |
204 | | * \brief strcodeCreateFromFile() |
205 | | * |
206 | | * \param[in] filein containing filenames of serialized data |
207 | | * \param[in] fileno integer that labels the two output files |
208 | | * \param[in] outdir [optional] if null, files are made in /tmp/lept/auto |
209 | | * \return 0 if OK, 1 on error |
210 | | * |
211 | | * <pre> |
212 | | * Notes: |
213 | | * (1) The %filein has one filename on each line. |
214 | | * Comment lines begin with "#". |
215 | | * (2) The output is 2 files: |
216 | | * autogen.[fileno].c |
217 | | * autogen.[fileno].h |
218 | | * </pre> |
219 | | */ |
220 | | l_ok |
221 | | strcodeCreateFromFile(const char *filein, |
222 | | l_int32 fileno, |
223 | | const char *outdir) |
224 | 0 | { |
225 | 0 | char *fname; |
226 | 0 | const char *type; |
227 | 0 | l_uint8 *data; |
228 | 0 | size_t nbytes; |
229 | 0 | l_int32 i, n, index; |
230 | 0 | SARRAY *sa; |
231 | 0 | L_STRCODE *strcode; |
232 | |
|
233 | 0 | if (!filein) |
234 | 0 | return ERROR_INT("filein not defined", __func__, 1); |
235 | | |
236 | 0 | if ((data = l_binaryRead(filein, &nbytes)) == NULL) |
237 | 0 | return ERROR_INT("data not read from file", __func__, 1); |
238 | 0 | sa = sarrayCreateLinesFromString((char *)data, 0); |
239 | 0 | LEPT_FREE(data); |
240 | 0 | if (!sa) |
241 | 0 | return ERROR_INT("sa not made", __func__, 1); |
242 | 0 | if ((n = sarrayGetCount(sa)) == 0) { |
243 | 0 | sarrayDestroy(&sa); |
244 | 0 | return ERROR_INT("no filenames in the file", __func__, 1); |
245 | 0 | } |
246 | | |
247 | 0 | strcode = strcodeCreate(fileno); |
248 | |
|
249 | 0 | for (i = 0; i < n; i++) { |
250 | 0 | fname = sarrayGetString(sa, i, L_NOCOPY); |
251 | 0 | if (fname[0] == '#') continue; |
252 | 0 | if (l_getIndexFromFile(fname, &index)) { |
253 | 0 | L_ERROR("File %s has no recognizable type\n", __func__, fname); |
254 | 0 | } else { |
255 | 0 | type = l_assoc[index].type; |
256 | 0 | L_INFO("File %s is type %s\n", __func__, fname, type); |
257 | 0 | strcodeGenerate(strcode, fname, type); |
258 | 0 | } |
259 | 0 | } |
260 | 0 | strcodeFinalize(&strcode, outdir); |
261 | 0 | sarrayDestroy(&sa); |
262 | 0 | return 0; |
263 | 0 | } |
264 | | |
265 | | |
266 | | /*! |
267 | | * \brief strcodeGenerate() |
268 | | * |
269 | | * \param[in] strcode for accumulating data |
270 | | * \param[in] filein input file with serialized data |
271 | | * \param[in] type of data; use the typedef string |
272 | | * \return 0 if OK, 1 on error. |
273 | | * |
274 | | * <pre> |
275 | | * Notes: |
276 | | * (1) The generated function name is |
277 | | * l_autodecode_[fileno]() |
278 | | * where [fileno] is the index label for the pair of output files. |
279 | | * (2) To deserialize this data, the function is called with the |
280 | | * argument 'ifunc', which increments each time strcodeGenerate() |
281 | | * is called. |
282 | | * </pre> |
283 | | */ |
284 | | l_ok |
285 | | strcodeGenerate(L_STRCODE *strcode, |
286 | | const char *filein, |
287 | | const char *type) |
288 | 0 | { |
289 | 0 | char *strdata, *strfunc, *strdescr; |
290 | 0 | l_int32 itype; |
291 | |
|
292 | 0 | if (!strcode) |
293 | 0 | return ERROR_INT("strcode not defined", __func__, 1); |
294 | 0 | if (!filein) |
295 | 0 | return ERROR_INT("filein not defined", __func__, 1); |
296 | 0 | if (!type) |
297 | 0 | return ERROR_INT("type not defined", __func__, 1); |
298 | | |
299 | | /* Get the index corresponding to type and validate */ |
300 | 0 | if (l_getIndexFromType(type, &itype) == 1) |
301 | 0 | return ERROR_INT("data type unknown", __func__, 1); |
302 | | |
303 | | /* Generate the encoded data string */ |
304 | 0 | if ((strdata = l_genDataString(filein, strcode->ifunc)) == NULL) |
305 | 0 | return ERROR_INT("strdata not made", __func__, 1); |
306 | 0 | sarrayAddString(strcode->data, strdata, L_INSERT); |
307 | | |
308 | | /* Generate the case data for the decoding function */ |
309 | 0 | strfunc = l_genCaseString(strcode->ifunc, itype); |
310 | 0 | sarrayAddString(strcode->function, strfunc, L_INSERT); |
311 | | |
312 | | /* Generate row of table for function type selection */ |
313 | 0 | strdescr = l_genDescrString(filein, strcode->ifunc, itype); |
314 | 0 | sarrayAddString(strcode->descr, strdescr, L_INSERT); |
315 | |
|
316 | 0 | strcode->n++; |
317 | 0 | strcode->ifunc++; |
318 | 0 | return 0; |
319 | 0 | } |
320 | | |
321 | | |
322 | | /*! |
323 | | * \brief strcodeFinalize() |
324 | | * |
325 | | * \param[in,out] pstrcode destroys and sets to null after .c and .h files |
326 | | * have been generated |
327 | | * \param[in] outdir [optional] if NULL, make files in /tmp/lept/auto |
328 | | * \return 0 if OK; 1 on error |
329 | | */ |
330 | | l_int32 |
331 | | strcodeFinalize(L_STRCODE **pstrcode, |
332 | | const char *outdir) |
333 | 0 | { |
334 | 0 | char buf[256]; |
335 | 0 | char *filestr, *casestr, *descr, *datastr, *realoutdir; |
336 | 0 | l_int32 actstart, end, newstart, fileno, nbytes; |
337 | 0 | size_t size; |
338 | 0 | L_STRCODE *strcode; |
339 | 0 | SARRAY *sa1, *sa2, *sa3; |
340 | |
|
341 | 0 | lept_mkdir("lept/auto"); |
342 | |
|
343 | 0 | if (!pstrcode || *pstrcode == NULL) |
344 | 0 | return ERROR_INT("No input data", __func__, 1); |
345 | 0 | strcode = *pstrcode; |
346 | 0 | if (!outdir) { |
347 | 0 | L_INFO("no outdir specified; writing to /tmp/lept/auto\n", __func__); |
348 | 0 | realoutdir = stringNew("/tmp/lept/auto"); |
349 | 0 | } else { |
350 | 0 | realoutdir = stringNew(outdir); |
351 | 0 | } |
352 | | |
353 | | /* ------------------------------------------------------- */ |
354 | | /* Make the output autogen.*.c file */ |
355 | | /* ------------------------------------------------------- */ |
356 | | |
357 | | /* Make array of textlines from TEMPLATE1 */ |
358 | 0 | filestr = (char *)l_binaryRead(TEMPLATE1, &size); |
359 | 0 | sa1 = sarrayCreateLinesFromString(filestr, 1); |
360 | 0 | LEPT_FREE(filestr); |
361 | 0 | sa3 = sarrayCreate(0); |
362 | | |
363 | | /* Copyright notice */ |
364 | 0 | sarrayParseRange(sa1, 0, &actstart, &end, &newstart, "--", 0); |
365 | 0 | sarrayAppendRange(sa3, sa1, actstart, end); |
366 | | |
367 | | /* File name comment */ |
368 | 0 | fileno = strcode->fileno; |
369 | 0 | snprintf(buf, sizeof(buf), " * autogen.%d.c", fileno); |
370 | 0 | sarrayAddString(sa3, buf, L_COPY); |
371 | | |
372 | | /* More text */ |
373 | 0 | sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); |
374 | 0 | sarrayAppendRange(sa3, sa1, actstart, end); |
375 | | |
376 | | /* Description of function types by index */ |
377 | 0 | descr = sarrayToString(strcode->descr, 1); |
378 | 0 | descr[strlen(descr) - 1] = '\0'; |
379 | 0 | sarrayAddString(sa3, descr, L_INSERT); |
380 | | |
381 | | /* Includes */ |
382 | 0 | sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); |
383 | 0 | sarrayAppendRange(sa3, sa1, actstart, end); |
384 | 0 | snprintf(buf, sizeof(buf), "#include \"autogen.%d.h\"", fileno); |
385 | 0 | sarrayAddString(sa3, buf, L_COPY); |
386 | | |
387 | | /* Header for auto-generated deserializers */ |
388 | 0 | sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); |
389 | 0 | sarrayAppendRange(sa3, sa1, actstart, end); |
390 | | |
391 | | /* Function name (as comment) */ |
392 | 0 | snprintf(buf, sizeof(buf), " * \\brief l_autodecode_%d()", fileno); |
393 | 0 | sarrayAddString(sa3, buf, L_COPY); |
394 | | |
395 | | /* Input and return values */ |
396 | 0 | sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); |
397 | 0 | sarrayAppendRange(sa3, sa1, actstart, end); |
398 | | |
399 | | /* Function name */ |
400 | 0 | snprintf(buf, sizeof(buf), "l_autodecode_%d(l_int32 index)", fileno); |
401 | 0 | sarrayAddString(sa3, buf, L_COPY); |
402 | | |
403 | | /* Stack vars */ |
404 | 0 | sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); |
405 | 0 | sarrayAppendRange(sa3, sa1, actstart, end); |
406 | | |
407 | | /* Declaration of nfunc on stack */ |
408 | 0 | snprintf(buf, sizeof(buf), "l_int32 nfunc = %d;\n", strcode->n); |
409 | 0 | sarrayAddString(sa3, buf, L_COPY); |
410 | | |
411 | | /* Declaration of PROCNAME */ |
412 | 0 | snprintf(buf, sizeof(buf), " PROCNAME(\"l_autodecode_%d\");", fileno); |
413 | 0 | sarrayAddString(sa3, buf, L_COPY); |
414 | | |
415 | | /* Test input variables */ |
416 | 0 | sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); |
417 | 0 | sarrayAppendRange(sa3, sa1, actstart, end); |
418 | | |
419 | | /* Insert case string */ |
420 | 0 | casestr = sarrayToString(strcode->function, 0); |
421 | 0 | casestr[strlen(casestr) - 1] = '\0'; |
422 | 0 | sarrayAddString(sa3, casestr, L_INSERT); |
423 | | |
424 | | /* End of function */ |
425 | 0 | sarrayParseRange(sa1, newstart, &actstart, &end, &newstart, "--", 0); |
426 | 0 | sarrayAppendRange(sa3, sa1, actstart, end); |
427 | | |
428 | | /* Flatten to string and output to autogen*.c file */ |
429 | 0 | filestr = sarrayToString(sa3, 1); |
430 | 0 | nbytes = strlen(filestr); |
431 | 0 | snprintf(buf, sizeof(buf), "%s/autogen.%d.c", realoutdir, fileno); |
432 | 0 | l_binaryWrite(buf, "w", filestr, nbytes); |
433 | 0 | LEPT_FREE(filestr); |
434 | 0 | sarrayDestroy(&sa1); |
435 | 0 | sarrayDestroy(&sa3); |
436 | | |
437 | | /* ------------------------------------------------------- */ |
438 | | /* Make the output autogen.*.h file */ |
439 | | /* ------------------------------------------------------- */ |
440 | | |
441 | | /* Make array of textlines from TEMPLATE2 */ |
442 | 0 | filestr = (char *)l_binaryRead(TEMPLATE2, &size); |
443 | 0 | sa2 = sarrayCreateLinesFromString(filestr, 1); |
444 | 0 | LEPT_FREE(filestr); |
445 | 0 | sa3 = sarrayCreate(0); |
446 | | |
447 | | /* Copyright notice */ |
448 | 0 | sarrayParseRange(sa2, 0, &actstart, &end, &newstart, "--", 0); |
449 | 0 | sarrayAppendRange(sa3, sa2, actstart, end); |
450 | | |
451 | | /* File name comment */ |
452 | 0 | snprintf(buf, sizeof(buf), " * autogen.%d.h", fileno); |
453 | 0 | sarrayAddString(sa3, buf, L_COPY); |
454 | | |
455 | | /* More text */ |
456 | 0 | sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); |
457 | 0 | sarrayAppendRange(sa3, sa2, actstart, end); |
458 | | |
459 | | /* Beginning header protection */ |
460 | 0 | snprintf(buf, sizeof(buf), "#ifndef LEPTONICA_AUTOGEN_%d_H\n" |
461 | 0 | "#define LEPTONICA_AUTOGEN_%d_H", |
462 | 0 | fileno, fileno); |
463 | 0 | sarrayAddString(sa3, buf, L_COPY); |
464 | | |
465 | | /* Prototype header text */ |
466 | 0 | sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); |
467 | 0 | sarrayAppendRange(sa3, sa2, actstart, end); |
468 | | |
469 | | /* Prototype declaration */ |
470 | 0 | snprintf(buf, sizeof(buf), "void *l_autodecode_%d(l_int32 index);", fileno); |
471 | 0 | sarrayAddString(sa3, buf, L_COPY); |
472 | | |
473 | | /* Prototype trailer text */ |
474 | 0 | sarrayParseRange(sa2, newstart, &actstart, &end, &newstart, "--", 0); |
475 | 0 | sarrayAppendRange(sa3, sa2, actstart, end); |
476 | | |
477 | | /* Insert serialized data strings */ |
478 | 0 | datastr = sarrayToString(strcode->data, 1); |
479 | 0 | datastr[strlen(datastr) - 1] = '\0'; |
480 | 0 | sarrayAddString(sa3, datastr, L_INSERT); |
481 | | |
482 | | /* End header protection */ |
483 | 0 | snprintf(buf, sizeof(buf), "#endif /* LEPTONICA_AUTOGEN_%d_H */", fileno); |
484 | 0 | sarrayAddString(sa3, buf, L_COPY); |
485 | | |
486 | | /* Flatten to string and output to autogen*.h file */ |
487 | 0 | filestr = sarrayToString(sa3, 1); |
488 | 0 | nbytes = strlen(filestr); |
489 | 0 | snprintf(buf, sizeof(buf), "%s/autogen.%d.h", realoutdir, fileno); |
490 | 0 | l_binaryWrite(buf, "w", filestr, nbytes); |
491 | 0 | LEPT_FREE(filestr); |
492 | 0 | LEPT_FREE(realoutdir); |
493 | 0 | sarrayDestroy(&sa2); |
494 | 0 | sarrayDestroy(&sa3); |
495 | | |
496 | | /* Cleanup */ |
497 | 0 | strcodeDestroy(pstrcode); |
498 | 0 | return 0; |
499 | 0 | } |
500 | | |
501 | | |
502 | | /*! |
503 | | * \brief l_getStructStrFromFile() |
504 | | * |
505 | | * \param[in] filename |
506 | | * \param[in] field (L_STR_TYPE, L_STR_NAME, L_STR_READER, L_STR_MEMREADER) |
507 | | * \param[out] pstr struct string for this file |
508 | | * \return 0 if found, 1 on error. |
509 | | * |
510 | | * <pre> |
511 | | * Notes: |
512 | | * (1) For example, if %field == L_STR_NAME, and the file is a serialized |
513 | | * pixa, this will return "Pixa", the name of the struct. |
514 | | * (2) Caller must free the returned string. |
515 | | * </pre> |
516 | | */ |
517 | | l_int32 |
518 | | l_getStructStrFromFile(const char *filename, |
519 | | l_int32 field, |
520 | | char **pstr) |
521 | 0 | { |
522 | 0 | l_int32 index; |
523 | |
|
524 | 0 | if (!pstr) |
525 | 0 | return ERROR_INT("&str not defined", __func__, 1); |
526 | 0 | *pstr = NULL; |
527 | 0 | if (!filename) |
528 | 0 | return ERROR_INT("filename not defined", __func__, 1); |
529 | 0 | if (field != L_STR_TYPE && field != L_STR_NAME && |
530 | 0 | field != L_STR_READER && field != L_STR_MEMREADER) |
531 | 0 | return ERROR_INT("invalid field", __func__, 1); |
532 | | |
533 | 0 | if (l_getIndexFromFile(filename, &index)) |
534 | 0 | return ERROR_INT("index not retrieved", __func__, 1); |
535 | 0 | if (field == L_STR_TYPE) |
536 | 0 | *pstr = stringNew(l_assoc[index].type); |
537 | 0 | else if (field == L_STR_NAME) |
538 | 0 | *pstr = stringNew(l_assoc[index].structname); |
539 | 0 | else if (field == L_STR_READER) |
540 | 0 | *pstr = stringNew(l_assoc[index].reader); |
541 | 0 | else /* field == L_STR_MEMREADER */ |
542 | 0 | *pstr = stringNew(l_assoc[index].memreader); |
543 | 0 | return 0; |
544 | 0 | } |
545 | | |
546 | | |
547 | | /*---------------------------------------------------------------------*/ |
548 | | /* Static helpers */ |
549 | | /*---------------------------------------------------------------------*/ |
550 | | /*! |
551 | | * \brief l_getIndexFromType() |
552 | | * |
553 | | * \param[in] type e.g., "PIXA" |
554 | | * \param[out] pindex found index |
555 | | * \return 0 if found, 1 if not. |
556 | | * |
557 | | * <pre> |
558 | | * Notes: |
559 | | * (1) For valid type, %found == true and %index > 0. |
560 | | * </pre> |
561 | | */ |
562 | | static l_int32 |
563 | | l_getIndexFromType(const char *type, |
564 | | l_int32 *pindex) |
565 | 0 | { |
566 | 0 | l_int32 i, found; |
567 | |
|
568 | 0 | if (!pindex) |
569 | 0 | return ERROR_INT("&index not defined", __func__, 1); |
570 | 0 | *pindex = 0; |
571 | 0 | if (!type) |
572 | 0 | return ERROR_INT("type string not defined", __func__, 1); |
573 | | |
574 | 0 | found = 0; |
575 | 0 | for (i = 1; i <= l_ntypes; i++) { |
576 | 0 | if (strcmp(type, l_assoc[i].type) == 0) { |
577 | 0 | found = 1; |
578 | 0 | *pindex = i; |
579 | 0 | break; |
580 | 0 | } |
581 | 0 | } |
582 | 0 | return !found; |
583 | 0 | } |
584 | | |
585 | | |
586 | | /*! |
587 | | * \brief l_getIndexFromStructname() |
588 | | * |
589 | | * \param[in] sn structname e.g., "Pixa" |
590 | | * \param[out] pindex found index |
591 | | * \return 0 if found, 1 if not. |
592 | | * |
593 | | * <pre> |
594 | | * Notes: |
595 | | * (1) This is used to identify the type of serialized file; |
596 | | * the first word in the file is the structname. |
597 | | * (2) For valid structname, %found == true and %index > 0. |
598 | | * </pre> |
599 | | */ |
600 | | static l_int32 |
601 | | l_getIndexFromStructname(const char *sn, |
602 | | l_int32 *pindex) |
603 | 0 | { |
604 | 0 | l_int32 i, found; |
605 | |
|
606 | 0 | if (!pindex) |
607 | 0 | return ERROR_INT("&index not defined", __func__, 1); |
608 | 0 | *pindex = 0; |
609 | 0 | if (!sn) |
610 | 0 | return ERROR_INT("sn string not defined", __func__, 1); |
611 | | |
612 | 0 | found = 0; |
613 | 0 | for (i = 1; i <= l_ntypes; i++) { |
614 | 0 | if (strcmp(sn, l_assoc[i].structname) == 0) { |
615 | 0 | found = 1; |
616 | 0 | *pindex = i; |
617 | 0 | break; |
618 | 0 | } |
619 | 0 | } |
620 | 0 | return !found; |
621 | 0 | } |
622 | | |
623 | | |
624 | | /*! |
625 | | * \brief l_getIndexFromFile() |
626 | | * |
627 | | * \param[in] filename |
628 | | * \param[out] pindex found index |
629 | | * \return 0 if found, 1 on error. |
630 | | */ |
631 | | static l_int32 |
632 | | l_getIndexFromFile(const char *filename, |
633 | | l_int32 *pindex) |
634 | 0 | { |
635 | 0 | char buf[256]; |
636 | 0 | char *word; |
637 | 0 | FILE *fp; |
638 | 0 | l_int32 notfound, format; |
639 | 0 | SARRAY *sa; |
640 | |
|
641 | 0 | if (!pindex) |
642 | 0 | return ERROR_INT("&index not defined", __func__, 1); |
643 | 0 | *pindex = 0; |
644 | 0 | if (!filename) |
645 | 0 | return ERROR_INT("filename not defined", __func__, 1); |
646 | | |
647 | | /* Open the stream, read lines until you find one with more |
648 | | * than a newline, and grab the first word. */ |
649 | 0 | if ((fp = fopenReadStream(filename)) == NULL) |
650 | 0 | return ERROR_INT_1("stream not opened", filename, __func__, 1); |
651 | 0 | do { |
652 | 0 | if ((fgets(buf, sizeof(buf), fp)) == NULL) { |
653 | 0 | fclose(fp); |
654 | 0 | return ERROR_INT_1("fgets read fail", filename, __func__, 1); |
655 | 0 | } |
656 | 0 | } while (buf[0] == '\n'); |
657 | 0 | fclose(fp); |
658 | 0 | sa = sarrayCreateWordsFromString(buf); |
659 | 0 | word = sarrayGetString(sa, 0, L_NOCOPY); |
660 | | |
661 | | /* Find the index associated with the word. If it is not |
662 | | * found, test to see if the file is a compressed pix. */ |
663 | 0 | notfound = l_getIndexFromStructname(word, pindex); |
664 | 0 | sarrayDestroy(&sa); |
665 | 0 | if (notfound) { /* maybe a Pix */ |
666 | 0 | if (findFileFormat(filename, &format) == 0) { |
667 | 0 | l_getIndexFromStructname("Pix", pindex); |
668 | 0 | } else { |
669 | 0 | return ERROR_INT_1("no file type identified", |
670 | 0 | filename, __func__, 1); |
671 | 0 | } |
672 | 0 | } |
673 | | |
674 | 0 | return 0; |
675 | 0 | } |
676 | | |
677 | | |
678 | | /*! |
679 | | * \brief l_genDataString() |
680 | | * |
681 | | * \param[in] filein input file of serialized data |
682 | | * \param[in] ifunc index into set of functions in output file |
683 | | * \return encoded ascii data string, or NULL on error reading from file |
684 | | */ |
685 | | static char * |
686 | | l_genDataString(const char *filein, |
687 | | l_int32 ifunc) |
688 | 0 | { |
689 | 0 | char buf[80]; |
690 | 0 | char *cdata1, *cdata2, *cdata3; |
691 | 0 | l_uint8 *data1, *data2; |
692 | 0 | l_int32 csize1, csize2; |
693 | 0 | size_t size1, size2; |
694 | 0 | SARRAY *sa; |
695 | |
|
696 | 0 | if (!filein) |
697 | 0 | return (char *)ERROR_PTR("filein not defined", __func__, NULL); |
698 | | |
699 | | /* Read it in, gzip it, encode, and reformat. We gzip because some |
700 | | * serialized data has a significant amount of ascii content. */ |
701 | 0 | if ((data1 = l_binaryRead(filein, &size1)) == NULL) |
702 | 0 | return (char *)ERROR_PTR("bindata not returned", __func__, NULL); |
703 | 0 | data2 = zlibCompress(data1, size1, &size2); |
704 | 0 | cdata1 = encodeBase64(data2, size2, &csize1); |
705 | 0 | cdata2 = reformatPacked64(cdata1, csize1, 4, 72, 1, &csize2); |
706 | 0 | LEPT_FREE(data1); |
707 | 0 | LEPT_FREE(data2); |
708 | 0 | LEPT_FREE(cdata1); |
709 | | |
710 | | /* Prepend the string declaration signature and put it together */ |
711 | 0 | sa = sarrayCreate(3); |
712 | 0 | snprintf(buf, sizeof(buf), "static const char *l_strdata_%d =\n", ifunc); |
713 | 0 | sarrayAddString(sa, buf, L_COPY); |
714 | 0 | sarrayAddString(sa, cdata2, L_INSERT); |
715 | 0 | sarrayAddString(sa, ";\n", L_COPY); |
716 | 0 | cdata3 = sarrayToString(sa, 0); |
717 | 0 | sarrayDestroy(&sa); |
718 | 0 | return cdata3; |
719 | 0 | } |
720 | | |
721 | | |
722 | | /*! |
723 | | * \brief l_genCaseString() |
724 | | * |
725 | | * \param[in] ifunc index into set of functions in generated file |
726 | | * \param[in] itype index into type of function to be used |
727 | | * \return case string for this decoding function |
728 | | * |
729 | | * <pre> |
730 | | * Notes: |
731 | | * (1) %ifunc and %itype have been validated, so no error can occur |
732 | | * </pre> |
733 | | */ |
734 | | static char * |
735 | | l_genCaseString(l_int32 ifunc, |
736 | | l_int32 itype) |
737 | 0 | { |
738 | 0 | char buf[256]; |
739 | 0 | char *code = NULL; |
740 | |
|
741 | 0 | snprintf(buf, sizeof(buf), " case %d:\n", ifunc); |
742 | 0 | stringJoinIP(&code, buf); |
743 | 0 | snprintf(buf, sizeof(buf), |
744 | 0 | " data1 = decodeBase64(l_strdata_%d, strlen(l_strdata_%d), " |
745 | 0 | "&size1);\n", ifunc, ifunc); |
746 | 0 | stringJoinIP(&code, buf); |
747 | 0 | stringJoinIP(&code, |
748 | 0 | " data2 = zlibUncompress(data1, size1, &size2);\n"); |
749 | 0 | snprintf(buf, sizeof(buf), |
750 | 0 | " result = (void *)%s(data2, size2);\n", |
751 | 0 | l_assoc[itype].memreader); |
752 | 0 | stringJoinIP(&code, buf); |
753 | 0 | stringJoinIP(&code, " lept_free(data1);\n"); |
754 | 0 | stringJoinIP(&code, " lept_free(data2);\n"); |
755 | 0 | stringJoinIP(&code, " break;\n"); |
756 | 0 | return code; |
757 | 0 | } |
758 | | |
759 | | |
760 | | /*! |
761 | | * \brief l_genDescrString() |
762 | | * |
763 | | * \param[in] filein input file of serialized data |
764 | | * \param[in] ifunc index into set of functions in generated file |
765 | | * \param[in] itype index into type of function to be used |
766 | | * \return description string for this decoding function |
767 | | */ |
768 | | static char * |
769 | | l_genDescrString(const char *filein, |
770 | | l_int32 ifunc, |
771 | | l_int32 itype) |
772 | 0 | { |
773 | 0 | char buf[256]; |
774 | 0 | char *tail; |
775 | |
|
776 | 0 | if (!filein) |
777 | 0 | return (char *)ERROR_PTR("filein not defined", __func__, NULL); |
778 | | |
779 | 0 | splitPathAtDirectory(filein, NULL, &tail); |
780 | 0 | snprintf(buf, sizeof(buf), " * %-2d %-10s %-14s %s", |
781 | 0 | ifunc, l_assoc[itype].type, l_assoc[itype].reader, tail); |
782 | |
|
783 | 0 | LEPT_FREE(tail); |
784 | 0 | return stringNew(buf); |
785 | 0 | } |