Coverage Report

Created: 2024-02-28 06:46

/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
}