VirtualBox

source: vbox/trunk/include/iprt/bldprog-strtab-template.cpp.h@ 97969

Last change on this file since 97969 was 96577, checked in by vboxsync, 2 years ago

iprt/bldprog-strtab-template.cpp.h: Sparc build fix. bugref:9726

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 42.3 KB
Line 
1/* $Id: bldprog-strtab-template.cpp.h 96577 2022-09-02 11:47:43Z vboxsync $ */
2/** @file
3 * IPRT - Build Program - String Table Generator.
4 */
5
6/*
7 * Copyright (C) 2006-2022 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*
39 * (Avoid include sanity checks as this is ring-3 only, C++ code.)
40 */
41#if defined(__cplusplus) && defined(IN_RING3)
42
43
44/*********************************************************************************************************************************
45* Defined Constants And Macros *
46*********************************************************************************************************************************/
47/** @def BLDPROG_STRTAB_MAX_STRLEN
48 * The max length of strings in the table. */
49#if !defined(BLDPROG_STRTAB_MAX_STRLEN) || defined(DOXYGEN_RUNNING)
50# define BLDPROG_STRTAB_MAX_STRLEN 256
51#endif
52
53/** @def BLDPROG_STRTAB_WITH_COMPRESSION
54 * Enables very simple string compression.
55 */
56#if defined(DOXYGEN_RUNNING)
57# define BLDPROG_STRTAB_WITH_COMPRESSION
58#endif
59
60/** @def BLDPROG_STRTAB_WITH_CAMEL_WORDS
61 * Modifies the string compression to look for camel case words.
62 */
63#if defined(DOXYGEN_RUNNING)
64# define BLDPROG_STRTAB_WITH_CAMEL_WORDS
65#endif
66
67/** @def BLDPROG_STRTAB_PURE_ASCII
68 * String compression assumes pure 7-bit ASCII and will fail on UTF-8 when this
69 * is defined. Otherwise, the compression code will require IPRT to link.
70 */
71#if defined(DOXYGEN_RUNNING)
72# define BLDPROG_STRTAB_PURE_ASCII
73#endif
74
75
76
77/*********************************************************************************************************************************
78* Header Files *
79*********************************************************************************************************************************/
80#include <iprt/stdarg.h>
81#include <iprt/ctype.h>
82#include <stdlib.h>
83#include <stdio.h>
84#include <string.h>
85
86#ifdef BLDPROG_STRTAB_WITH_COMPRESSION
87# include <map>
88# include <iprt/sanitized/string>
89
90# define BLDPROG_STRTAB_WITH_WORD_SEP_ALTERNATIVE
91typedef struct BLDPROGWORDFREQSTATS
92{
93 uint32_t cWithoutSep; /**< Number of occurances without a separator. */
94# ifdef BLDPROG_STRTAB_WITH_WORD_SEP_ALTERNATIVE
95 uint32_t cWithSep; /**< Number of occurance with a separator. */
96 char chSep; /**< The separator. First come basis. */
97# endif
98} BLDPROGWORDFREQSTATS;
99
100typedef std::map<std::string, BLDPROGWORDFREQSTATS> BLDPROGWORDFREQMAP;
101
102#endif
103
104#include "../../src/VBox/Runtime/include/internal/strhash.h" /** @todo make this one public */
105
106
107/*********************************************************************************************************************************
108* Structures and Typedefs *
109*********************************************************************************************************************************/
110/**
111 * Build table string.
112 */
113typedef struct BLDPROGSTRING
114{
115 /** The string.
116 * @note This may be modified or replaced (allocated from heap) when
117 * compressing the string table. */
118 char *pszString;
119 /** The string hash value. */
120 uint32_t uHash;
121 /** The strint table offset. */
122 uint32_t offStrTab;
123 /** The string length. */
124 size_t cchString;
125 /** Pointer to the next string reference (same string table entry). */
126 struct BLDPROGSTRING *pNextRef;
127 /** Pointer to the next string with the same hash value (collision). */
128 struct BLDPROGSTRING *pNextCollision;
129
130} BLDPROGSTRING;
131/** Pointer to a string table string. */
132typedef BLDPROGSTRING *PBLDPROGSTRING;
133
134
135/** String table data. */
136typedef struct BLDPROGSTRTAB
137{
138 /** The size of g_papStrHash. */
139 size_t cStrHash;
140 /** String hash table. */
141 PBLDPROGSTRING *papStrHash;
142 /** Duplicate strings found by AddString. */
143 size_t cDuplicateStrings;
144 /** Total length of the unique strings (no terminators). */
145 size_t cchUniqueStrings;
146 /** Number of unique strings after AddString. */
147 size_t cUniqueStrings;
148 /** Number of collisions. */
149 size_t cCollisions;
150
151 /** Number of entries in apSortedStrings. */
152 size_t cSortedStrings;
153 /** The sorted string table. */
154 PBLDPROGSTRING *papSortedStrings;
155
156#ifdef BLDPROG_STRTAB_WITH_COMPRESSION
157 /** The 256 words we've picked to be indexed by reference. */
158 BLDPROGSTRING aCompDict[256];
159 /** The frequency of the 256 dictionary entries. */
160 size_t auCompDictFreq[256];
161 /** Incoming strings pending compression. */
162 PBLDPROGSTRING *papPendingStrings;
163 /** Current number of entries in papStrPending. */
164 size_t cPendingStrings;
165 /** The allocated size of papPendingStrings. */
166 size_t cMaxPendingStrings;
167 /** Work frequency map.
168 * @todo rewrite in plain C. */
169 BLDPROGWORDFREQMAP Frequencies;
170 /** Map of characters used by input strings. */
171 uint64_t bmUsedChars[256/64];
172#endif
173
174 /** The string table. */
175 char *pachStrTab;
176 /** The actual string table size. */
177 size_t cchStrTab;
178} BLDPROGSTRTAB;
179typedef BLDPROGSTRTAB *PBLDPROGSTRTAB;
180
181#if RT_CLANG_PREREQ(4, 0) || RT_GNUC_PREREQ(4, 6)
182# pragma GCC diagnostic push
183# pragma GCC diagnostic ignored "-Wunused-function"
184#endif
185
186
187#ifdef BLDPROG_STRTAB_WITH_COMPRESSION
188
189/**
190 * Same as ASMBitTest.
191 *
192 * We cannot safely use ASMBitTest here because it must be inline, as this code
193 * is used to build RuntimeBldProg. */
194DECLINLINE(bool) BldProgBitIsSet(uint64_t const *pbmBitmap, size_t iBit)
195{
196 return RT_BOOL(pbmBitmap[iBit / 64] & RT_BIT_64(iBit % 64));
197}
198
199
200/**
201 * Same as ASMBitSet.
202 *
203 * We cannot safely use ASMBitSet here because it must be inline, as this code
204 * is used to build RuntimeBldProg.
205 */
206DECLINLINE(void) BldProgBitSet(uint64_t *pbmBitmap, size_t iBit)
207{
208 pbmBitmap[iBit / 64] |= RT_BIT_64(iBit % 64);
209}
210
211#endif
212
213
214/**
215 * Initializes the strint table compiler.
216 *
217 * @returns success indicator (out of memory if false).
218 * @param pThis The strint table compiler instance.
219 * @param cMaxStrings The max number of strings we'll be adding.
220 */
221static bool BldProgStrTab_Init(PBLDPROGSTRTAB pThis, size_t cMaxStrings)
222{
223 pThis->cStrHash = 0;
224 pThis->papStrHash = NULL;
225 pThis->cDuplicateStrings = 0;
226 pThis->cchUniqueStrings = 0;
227 pThis->cUniqueStrings = 0;
228 pThis->cCollisions = 0;
229 pThis->cSortedStrings = 0;
230 pThis->papSortedStrings = NULL;
231 pThis->pachStrTab = NULL;
232 pThis->cchStrTab = 0;
233#ifdef BLDPROG_STRTAB_WITH_COMPRESSION
234 memset(pThis->aCompDict, 0, sizeof(pThis->aCompDict));
235 pThis->papPendingStrings = NULL;
236 pThis->cPendingStrings = 0;
237 pThis->cMaxPendingStrings = cMaxStrings;
238 memset(pThis->bmUsedChars, 0, sizeof(pThis->bmUsedChars));
239 BldProgBitSet(pThis->bmUsedChars, 0); /* Some parts of the code still thinks zero is a terminator, so don't use it for now. */
240# ifndef BLDPROG_STRTAB_PURE_ASCII
241 BldProgBitSet(pThis->bmUsedChars, 0xff); /* Reserve escape byte for codepoints above 127. */
242# endif
243#endif
244
245 /*
246 * Allocate a hash table double the size of all strings (to avoid too
247 * many collisions). Add all strings to it, finding duplicates in the
248 * process.
249 */
250#ifdef BLDPROG_STRTAB_WITH_COMPRESSION
251 cMaxStrings += RT_ELEMENTS(pThis->aCompDict);
252#endif
253 cMaxStrings *= 2;
254 pThis->papStrHash = (PBLDPROGSTRING *)calloc(sizeof(pThis->papStrHash[0]), cMaxStrings);
255 if (pThis->papStrHash)
256 {
257 pThis->cStrHash = cMaxStrings;
258#ifdef BLDPROG_STRTAB_WITH_COMPRESSION
259 pThis->papPendingStrings = (PBLDPROGSTRING *)calloc(sizeof(pThis->papPendingStrings[0]), pThis->cMaxPendingStrings);
260 if (pThis->papPendingStrings)
261#endif
262 return true;
263
264#ifdef BLDPROG_STRTAB_WITH_COMPRESSION
265 free(pThis->papStrHash);
266 pThis->papStrHash = NULL;
267#endif
268 }
269 return false;
270}
271
272
273#if 0 /* unused */
274static void BldProgStrTab_Delete(PBLDPROGSTRTAB pThis)
275{
276 free(pThis->papStrHash);
277 free(pThis->papSortedStrings);
278 free(pThis->pachStrTab);
279# ifdef BLDPROG_STRTAB_WITH_COMPRESSION
280 free(pThis->papPendingStrings);
281# endif
282 memset(pThis, 0, sizeof(*pThis));
283}
284#endif
285
286#ifdef BLDPROG_STRTAB_WITH_COMPRESSION
287
288DECLINLINE(size_t) bldProgStrTab_compressorFindNextWord(const char *pszSrc, char ch, const char **ppszSrc)
289{
290 /*
291 * Skip leading word separators.
292 */
293# ifdef BLDPROG_STRTAB_WITH_CAMEL_WORDS
294 while ( ch == ' '
295 || ch == '-'
296 || ch == '+'
297 || ch == '_')
298# else
299 while (ch == ' ')
300# endif
301 ch = *++pszSrc;
302 if (ch)
303 {
304 /*
305 * Find end of word.
306 */
307 size_t cchWord = 1;
308# ifdef BLDPROG_STRTAB_WITH_CAMEL_WORDS
309 char chPrev = ch;
310 while ( (ch = pszSrc[cchWord]) != ' '
311 && ch != '\0'
312 && ch != '-'
313 && ch != '+'
314 && ch != '_'
315 && ( ch == chPrev
316 || !RT_C_IS_UPPER(ch)
317 || RT_C_IS_UPPER(chPrev)) )
318 {
319 chPrev = ch;
320 cchWord++;
321 }
322# else
323 while ((ch = pszSrc[cchWord]) != ' ' && ch != '\0')
324 cchWord++;
325# endif
326 *ppszSrc = pszSrc;
327 return cchWord;
328 }
329
330 *ppszSrc = pszSrc;
331 return 0;
332}
333
334
335/**
336 * Analyzes a string.
337 *
338 * @param pThis The strint table compiler instance.
339 * @param pStr The string to analyze.
340 */
341static void bldProgStrTab_compressorAnalyzeString(PBLDPROGSTRTAB pThis, PBLDPROGSTRING pStr)
342{
343 /*
344 * Mark all the string characters as used.
345 */
346 const char *psz = pStr->pszString;
347 char ch;
348 while ((ch = *psz++) != '\0')
349 BldProgBitSet(pThis->bmUsedChars, (uint8_t)ch);
350
351 /*
352 * For now we just consider words.
353 */
354 psz = pStr->pszString;
355 while ((ch = *psz) != '\0')
356 {
357 size_t cchWord = bldProgStrTab_compressorFindNextWord(psz, ch, &psz);
358 if (cchWord > 1)
359 {
360 std::string strWord(psz, cchWord);
361 BLDPROGWORDFREQMAP::iterator it = pThis->Frequencies.find(strWord);
362 if (it != pThis->Frequencies.end())
363 {
364# ifdef BLDPROG_STRTAB_WITH_WORD_SEP_ALTERNATIVE
365 char const chSep = psz[cchWord];
366 if (chSep != '\0' && (it->second.chSep == chSep || it->second.chSep == '\0'))
367 {
368 it->second.chSep = chSep;
369 it->second.cWithSep++;
370 }
371 else
372# endif
373 it->second.cWithoutSep++;
374 }
375 else
376 {
377# ifdef BLDPROG_STRTAB_WITH_WORD_SEP_ALTERNATIVE
378 char const chSep = psz[cchWord];
379 if (chSep != '\0')
380 {
381 BLDPROGWORDFREQSTATS NewWord = { 0, 0, chSep };
382 pThis->Frequencies[strWord] = NewWord;
383 }
384 else
385# endif
386 {
387 static BLDPROGWORDFREQSTATS s_NewWord = { 0 };
388 pThis->Frequencies[strWord] = s_NewWord;
389 }
390 }
391
392# if 0 /** @todo need better accounting for overlapping alternatives before this can be enabled. */
393 /* Two words - immediate yields calc may lie when this enabled and we may pick the wrong words. */
394 if (ch == ' ')
395 {
396 ch = psz[++cchWord];
397 if (ch != ' ' && ch != '\0')
398 {
399 size_t const cchSaved = cchWord;
400
401 do
402 cchWord++;
403 while ((ch = psz[cchWord]) != ' ' && ch != '\0');
404
405 strWord = std::string(psz, cchWord);
406 BLDPROGWORDFREQMAP::iterator it = pThis->Frequencies.find(strWord);
407 if (it != pThis->Frequencies.end())
408 it->second += cchWord - 1;
409 else
410 pThis->Frequencies[strWord] = 0;
411
412 cchWord = cchSaved;
413 }
414 }
415# endif
416 }
417 else if (!cchWord)
418 break;
419
420 /* Advance. */
421 psz += cchWord;
422 }
423 pStr->cchString = psz - pStr->pszString;
424 if (pStr->cchString > BLDPROG_STRTAB_MAX_STRLEN)
425 {
426 fprintf(stderr, "error: String to long (%u)\n", (unsigned)pStr->cchString);
427 abort();
428 }
429}
430
431#endif /* BLDPROG_STRTAB_WITH_COMPRESSION */
432
433/**
434 * Adds a string to the hash table.
435 * @param pThis The strint table compiler instance.
436 * @param pStr The string.
437 */
438static void bldProgStrTab_AddStringToHashTab(PBLDPROGSTRTAB pThis, PBLDPROGSTRING pStr)
439{
440 pStr->pNextRef = NULL;
441 pStr->pNextCollision = NULL;
442 pStr->offStrTab = 0;
443 pStr->uHash = sdbm(pStr->pszString, &pStr->cchString);
444 if (pStr->cchString > BLDPROG_STRTAB_MAX_STRLEN)
445 {
446 fprintf(stderr, "error: String to long (%u)\n", (unsigned)pStr->cchString);
447 exit(RTEXITCODE_FAILURE);
448 }
449
450 size_t idxHash = pStr->uHash % pThis->cStrHash;
451 PBLDPROGSTRING pCur = pThis->papStrHash[idxHash];
452 if (!pCur)
453 pThis->papStrHash[idxHash] = pStr;
454 else
455 {
456 /* Look for matching string. */
457 do
458 {
459 if ( pCur->uHash == pStr->uHash
460 && pCur->cchString == pStr->cchString
461 && memcmp(pCur->pszString, pStr->pszString, pStr->cchString) == 0)
462 {
463 pStr->pNextRef = pCur->pNextRef;
464 pCur->pNextRef = pStr;
465 pThis->cDuplicateStrings++;
466 return;
467 }
468 pCur = pCur->pNextCollision;
469 } while (pCur != NULL);
470
471 /* No matching string, insert. */
472 pThis->cCollisions++;
473 pStr->pNextCollision = pThis->papStrHash[idxHash];
474 pThis->papStrHash[idxHash] = pStr;
475 }
476
477 pThis->cUniqueStrings++;
478 pThis->cchUniqueStrings += pStr->cchString;
479}
480
481
482/**
483 * Adds a string to the string table.
484 *
485 * @param pThis The strint table compiler instance.
486 * @param pStr The string.
487 */
488static void BldProgStrTab_AddString(PBLDPROGSTRTAB pThis, PBLDPROGSTRING pStr)
489{
490#ifdef BLDPROG_STRTAB_WITH_COMPRESSION
491 bldProgStrTab_compressorAnalyzeString(pThis, pStr);
492 if (pThis->cPendingStrings < pThis->cMaxPendingStrings)
493 pThis->papPendingStrings[pThis->cPendingStrings++] = pStr;
494 else
495 abort();
496#else
497 bldProgStrTab_AddStringToHashTab(pThis, pStr);
498#endif
499}
500
501
502/**
503 * Adds a string to the string table.
504 *
505 * @param pThis The strint table compiler instance.
506 * @param pStr The string entry (uninitialized).
507 * @param psz The string, will be duplicated if compression is enabled.
508 */
509DECLINLINE(void) BldProgStrTab_AddStringDup(PBLDPROGSTRTAB pThis, PBLDPROGSTRING pStr, const char *psz)
510{
511#ifdef BLDPROG_STRTAB_WITH_COMPRESSION
512 pStr->pszString = strdup(psz);
513 if (!pStr->pszString)
514 abort();
515#else
516 pStr->pszString = (char *)psz;
517#endif
518 BldProgStrTab_AddString(pThis, pStr);
519}
520
521#ifdef BLDPROG_STRTAB_WITH_COMPRESSION
522
523/**
524 * Copies @a cchSrc chars from @a pchSrc to @a pszDst, escaping special
525 * sequences.
526 *
527 * @returns New @a pszDst position, NULL if invalid source encoding.
528 * @param pszDst The destination buffer.
529 * @param pszSrc The source buffer.
530 * @param cchSrc How much to copy.
531 */
532static char *bldProgStrTab_compressorCopyAndEscape(char *pszDst, const char *pszSrc, size_t cchSrc)
533{
534 while (cchSrc-- > 0)
535 {
536 char ch = *pszSrc;
537 if (!((unsigned char)ch & 0x80))
538 {
539 *pszDst++ = ch;
540 pszSrc++;
541 }
542 else
543 {
544# ifdef BLDPROG_STRTAB_PURE_ASCII
545 fprintf(stderr, "error: unexpected char value %#x\n", ch);
546 return NULL;
547# else
548 RTUNICP uc;
549 int rc = RTStrGetCpEx(&pszSrc, &uc);
550 if (RT_SUCCESS(rc))
551 {
552 *pszDst++ = (unsigned char)0xff; /* escape single code point. */
553 pszDst = RTStrPutCp(pszDst, uc);
554 }
555 else
556 {
557 fprintf(stderr, "Error: RTStrGetCpEx failed with rc=%d\n", rc);
558 return NULL;
559 }
560# endif
561 }
562 }
563 return pszDst;
564}
565
566
567/**
568 * Replaces the dictionary words and escapes non-ascii chars in a string.
569 *
570 * @param pThis The strint table compiler instance.
571 * @param pString The string to fixup.
572 */
573static bool bldProgStrTab_compressorFixupString(PBLDPROGSTRTAB pThis, PBLDPROGSTRING pStr)
574{
575 char szNew[BLDPROG_STRTAB_MAX_STRLEN * 2];
576 char *pszDst = szNew;
577 const char *pszSrc = pStr->pszString;
578 const char *pszSrcEnd = pszSrc + pStr->cchString;
579
580 char ch;
581 while ((ch = *pszSrc) != '\0')
582 {
583 const char * const pszSrcUncompressed = pszSrc;
584 size_t cchWord = bldProgStrTab_compressorFindNextWord(pszSrc, ch, &pszSrc);
585 size_t cchSrcUncompressed = pszSrc - pszSrcUncompressed;
586 if (cchSrcUncompressed > 0)
587 {
588 pszDst = bldProgStrTab_compressorCopyAndEscape(pszDst, pszSrcUncompressed, cchSrcUncompressed);
589 if (!pszDst)
590 return false;
591 }
592 if (!cchWord)
593 break;
594
595 /* Check for g_aWord matches. */
596 if (cchWord > 1)
597 {
598 size_t cchMax = pszSrcEnd - pszSrc;
599 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aCompDict); i++)
600 {
601 size_t cchLen = pThis->aCompDict[i].cchString;
602 if ( cchLen >= cchWord
603 && cchLen <= cchMax
604 && memcmp(pThis->aCompDict[i].pszString, pszSrc, cchLen) == 0)
605 {
606 *pszDst++ = (unsigned char)i;
607 pszSrc += cchLen;
608 cchWord = 0;
609 break;
610 }
611 }
612 }
613
614 if (cchWord > 0)
615 {
616 /* Copy the current word. */
617 pszDst = bldProgStrTab_compressorCopyAndEscape(pszDst, pszSrc, cchWord);
618 if (!pszDst)
619 return false;
620 pszSrc += cchWord;
621 }
622 }
623
624 /* Just terminate it now. */
625 *pszDst = '\0';
626
627 /*
628 * Update the string.
629 */
630 size_t cchNew = pszDst - &szNew[0];
631 if (cchNew > pStr->cchString)
632 {
633 pStr->pszString = (char *)malloc(cchNew + 1);
634 if (!pStr->pszString)
635 {
636 fprintf(stderr, "Out of memory!\n");
637 return false;
638 }
639 }
640 memcpy(pStr->pszString, szNew, cchNew + 1);
641 pStr->cchString = cchNew;
642
643 return true;
644}
645
646
647/**
648 * Entry in SortedDictionary.
649 *
650 * Uses variable length string member, so not class and allocated via malloc.
651 */
652struct SortedDictionaryEntry
653{
654 size_t m_cchGain;
655 size_t m_cchString;
656 RT_FLEXIBLE_ARRAY_EXTENSION
657 char m_szString[RT_FLEXIBLE_ARRAY];
658
659 /** Allocates and initializes a new entry. */
660 static SortedDictionaryEntry *allocate(const char *a_pch, size_t a_cch, size_t a_cchGain, char a_chSep)
661 {
662 size_t cbString = a_cch + !!a_chSep + 1;
663 SortedDictionaryEntry *pNew = (SortedDictionaryEntry *)malloc(RT_UOFFSETOF(SortedDictionaryEntry, m_szString) + cbString);
664 if (pNew)
665 {
666 pNew->m_cchGain = a_cchGain;
667 memcpy(pNew->m_szString, a_pch, a_cch);
668 if (a_chSep)
669 pNew->m_szString[a_cch++] = a_chSep;
670 pNew->m_szString[a_cch] = '\0';
671 pNew->m_cchString = a_cch;
672 }
673 return pNew;
674 }
675
676
677 /** Compares this dictionary entry with an incoming one.
678 * @retval -1 if this entry is of less worth than the new one.
679 * @retval 0 if this entry is of equal worth to the new one.
680 * @retval +1 if this entry is of more worth than the new one.
681 */
682 int compare(size_t a_cchGain, size_t a_cchString)
683 {
684 /* Higher gain is preferred of course: */
685 if (m_cchGain < a_cchGain)
686 return -1;
687 if (m_cchGain > a_cchGain)
688 return 1;
689
690 /* Gain is the same. Prefer the shorter string, as it will result in a shorter string table: */
691 if (m_cchString > a_cchString)
692 return -1;
693 if (m_cchString < a_cchString)
694 return 1;
695 return 0;
696 }
697};
698
699
700/**
701 * Insertion sort dictionary that keeps the 256 best words.
702 *
703 * Used by bldProgStrTab_compressorDoStringCompression to pick the dictionary
704 * words.
705 */
706class SortedDictionary
707{
708public:
709 size_t m_cEntries;
710 SortedDictionaryEntry *m_apEntries[256];
711
712 SortedDictionary()
713 : m_cEntries(0)
714 {
715 for (size_t i = 0; i < RT_ELEMENTS(m_apEntries); i++)
716 m_apEntries[i] = NULL;
717 }
718
719 ~SortedDictionary()
720 {
721 while (m_cEntries > 0)
722 {
723 free(m_apEntries[--m_cEntries]);
724 m_apEntries[m_cEntries] = NULL;
725 }
726 }
727
728
729 /**
730 * Inserts a new entry, if it's worth it.
731 * @returns true on succes, false if out of memory.
732 */
733 bool insert(const char *a_pchString, size_t a_cchStringBase, size_t a_cchGain, char a_chSep = 0)
734 {
735 size_t const cchString = a_cchStringBase + (a_chSep + 1);
736
737 /*
738 * Drop the insert if the symbol table is full and the insert is less worth the last entry:
739 */
740 if ( m_cEntries >= RT_ELEMENTS(m_apEntries)
741 && m_apEntries[RT_ELEMENTS(m_apEntries) - 1]->compare(a_cchGain, cchString) >= 0)
742 return true;
743
744 /*
745 * Create a new entry to insert.
746 */
747 SortedDictionaryEntry *pNewEntry = SortedDictionaryEntry::allocate(a_pchString, a_cchStringBase, a_cchGain, a_chSep);
748 if (!pNewEntry)
749 return false;
750
751 /*
752 * Find the insert point.
753 */
754 if (m_cEntries == 0)
755 {
756 m_apEntries[0] = pNewEntry;
757 m_cEntries = 1;
758 }
759 else
760 {
761 /* If table is full, drop the last entry before we start (already made
762 sure the incoming entry is preferable to the one were dropping): */
763 if (m_cEntries >= RT_ELEMENTS(m_apEntries))
764 {
765 free(m_apEntries[RT_ELEMENTS(m_apEntries) - 1]);
766 m_apEntries[RT_ELEMENTS(m_apEntries) - 1] = NULL;
767 m_cEntries = RT_ELEMENTS(m_apEntries) - 1;
768 }
769
770 /* Find where to insert the new entry: */
771 /** @todo use binary search. */
772 size_t i = m_cEntries;
773 while (i > 0 && m_apEntries[i - 1]->compare(a_cchGain, cchString) < 0)
774 i--;
775
776 /* Shift entries to make room and insert the new entry. */
777 if (i < m_cEntries)
778 memmove(&m_apEntries[i + 1], &m_apEntries[i], (m_cEntries - i) * sizeof(m_apEntries[0]));
779 m_apEntries[i] = pNewEntry;
780 m_cEntries++;
781 }
782 return true;
783 }
784};
785
786
787/**
788 * Compresses the vendor and product strings.
789 *
790 * This is very very simple (a lot less work than the string table for
791 * instance).
792 */
793static bool bldProgStrTab_compressorDoStringCompression(PBLDPROGSTRTAB pThis, bool fVerbose)
794{
795 /*
796 * Sort the frequency analyzis result and pick the top entries for any
797 * available dictionary slots.
798 */
799 SortedDictionary SortedDict;
800 for (BLDPROGWORDFREQMAP::iterator it = pThis->Frequencies.begin(); it != pThis->Frequencies.end(); ++it)
801 {
802 bool fInsert;
803 size_t const cchString = it->first.length();
804# ifndef BLDPROG_STRTAB_WITH_WORD_SEP_ALTERNATIVE
805 size_t const cchGainWithout = it->second.cWithoutSep * cchString;
806# else
807 size_t const cchGainWithout = (it->second.cWithoutSep + it->second.cWithSep) * cchString;
808 size_t const cchGainWith = it->second.cWithSep * (cchString + 1);
809 if (cchGainWith > cchGainWithout)
810 fInsert = SortedDict.insert(it->first.c_str(), cchString, cchGainWith, it->second.chSep);
811 else
812# endif
813 fInsert = SortedDict.insert(it->first.c_str(), cchString, cchGainWithout);
814 if (!fInsert)
815 return false;
816 }
817
818 size_t cb = 0;
819 size_t cWords = 0;
820 size_t iDict = 0;
821 for (size_t i = 0; i < RT_ELEMENTS(pThis->aCompDict); i++)
822 {
823 char szTmp[2] = { (char)i, '\0' };
824 const char *psz = szTmp;
825 if ( BldProgBitIsSet(pThis->bmUsedChars, i)
826 || iDict >= SortedDict.m_cEntries)
827 {
828 /* character entry */
829 pThis->auCompDictFreq[i] = 0;
830 pThis->aCompDict[i].cchString = 1;
831 }
832 else
833 {
834 /* word entry */
835 cb += SortedDict.m_apEntries[iDict]->m_cchGain;
836 pThis->auCompDictFreq[i] = SortedDict.m_apEntries[iDict]->m_cchGain;
837 pThis->aCompDict[i].cchString = SortedDict.m_apEntries[iDict]->m_cchString;
838 psz = SortedDict.m_apEntries[iDict]->m_szString;
839 cWords++;
840 iDict++;
841 }
842 pThis->aCompDict[i].pszString = (char *)malloc(pThis->aCompDict[i].cchString + 1);
843 if (pThis->aCompDict[i].pszString)
844 memcpy(pThis->aCompDict[i].pszString, psz, pThis->aCompDict[i].cchString + 1);
845 else
846 return false;
847 }
848
849 if (fVerbose)
850 printf("debug: Estimated string compression saving: %u bytes\n"
851 "debug: %u words, %u characters\n"
852 , (unsigned)cb, (unsigned)cWords, (unsigned)(RT_ELEMENTS(pThis->aCompDict) - cWords));
853
854 /*
855 * Rework the strings.
856 */
857 size_t cchOld = 0;
858 size_t cchOldMax = 0;
859 size_t cchOldMin = BLDPROG_STRTAB_MAX_STRLEN;
860 size_t cchNew = 0;
861 size_t cchNewMax = 0;
862 size_t cchNewMin = BLDPROG_STRTAB_MAX_STRLEN;
863 size_t i = pThis->cPendingStrings;
864 while (i-- > 0)
865 {
866 PBLDPROGSTRING pCurStr = pThis->papPendingStrings[i];
867 cchOld += pCurStr->cchString;
868 if (pCurStr->cchString > cchOldMax)
869 cchOldMax = pCurStr->cchString;
870 if (pCurStr->cchString < cchOldMin)
871 cchOldMin = pCurStr->cchString;
872
873 if (!bldProgStrTab_compressorFixupString(pThis, pCurStr))
874 return false;
875
876 cchNew += pCurStr->cchString;
877 if (pCurStr->cchString > cchNewMax)
878 cchNewMax = pCurStr->cchString;
879 if (pCurStr->cchString < cchNewMin)
880 cchNewMin = pCurStr->cchString;
881
882 bldProgStrTab_AddStringToHashTab(pThis, pCurStr);
883 }
884
885 /*
886 * Do debug stats.
887 */
888 if (fVerbose)
889 {
890 for (i = 0; i < RT_ELEMENTS(pThis->aCompDict); i++)
891 cchNew += pThis->aCompDict[i].cchString + 1;
892
893 printf("debug: Strings: original: %u bytes; compressed: %u bytes;", (unsigned)cchOld, (unsigned)cchNew);
894 if (cchNew < cchOld)
895 printf(" saving %u bytes (%u%%)\n", (unsigned)(cchOld - cchNew), (unsigned)((cchOld - cchNew) * 100 / cchOld));
896 else
897 printf(" wasting %u bytes!\n", (unsigned)(cchOld - cchNew));
898 printf("debug: Original string lengths: average %u; min %u; max %u\n",
899 (unsigned)(cchOld / pThis->cPendingStrings), (unsigned)cchOldMin, (unsigned)cchOldMax);
900 printf("debug: Compressed string lengths: average %u; min %u; max %u\n",
901 (unsigned)(cchNew / pThis->cPendingStrings), (unsigned)cchNewMin, (unsigned)cchNewMax);
902 }
903
904 return true;
905}
906
907#endif /* BLDPROG_STRTAB_WITH_COMPRESSION */
908
909/**
910 * Inserts a string into g_apUniqueStrings.
911 * @param pThis The strint table compiler instance.
912 * @param pStr The string.
913 */
914static void bldProgStrTab_InsertUniqueString(PBLDPROGSTRTAB pThis, PBLDPROGSTRING pStr)
915{
916 size_t iIdx;
917 size_t iEnd = pThis->cSortedStrings;
918 if (iEnd)
919 {
920 size_t iStart = 0;
921 for (;;)
922 {
923 iIdx = iStart + (iEnd - iStart) / 2;
924 if (pThis->papSortedStrings[iIdx]->cchString < pStr->cchString)
925 {
926 if (iIdx <= iStart)
927 break;
928 iEnd = iIdx;
929 }
930 else if (pThis->papSortedStrings[iIdx]->cchString > pStr->cchString)
931 {
932 if (++iIdx >= iEnd)
933 break;
934 iStart = iIdx;
935 }
936 else
937 break;
938 }
939
940 if (iIdx != pThis->cSortedStrings)
941 memmove(&pThis->papSortedStrings[iIdx + 1], &pThis->papSortedStrings[iIdx],
942 (pThis->cSortedStrings - iIdx) * sizeof(pThis->papSortedStrings[iIdx]));
943 }
944 else
945 iIdx = 0;
946
947 pThis->papSortedStrings[iIdx] = pStr;
948 pThis->cSortedStrings++;
949}
950
951
952/**
953 * Compiles the string table after the string has been added.
954 *
955 * This will save space by dropping string terminators, eliminating duplicates
956 * and try find strings that are sub-strings of others.
957 *
958 * Will initialize the StrRef of all StrTabString instances.
959 *
960 * @returns success indicator (error flagged).
961 * @param pThis The strint table compiler instance.
962 */
963static bool BldProgStrTab_CompileIt(PBLDPROGSTRTAB pThis, bool fVerbose)
964{
965#ifdef BLDPROG_STRTAB_WITH_COMPRESSION
966 /*
967 * Do the compression and add all the compressed strings to the table.
968 */
969 if (!bldProgStrTab_compressorDoStringCompression(pThis, fVerbose))
970 return false;
971
972 /*
973 * Add the dictionary strings.
974 */
975 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aCompDict); i++)
976 if (pThis->aCompDict[i].cchString > 1)
977 bldProgStrTab_AddStringToHashTab(pThis, &pThis->aCompDict[i]);
978# ifdef RT_STRICT
979 else if (pThis->aCompDict[i].cchString != 1)
980 abort();
981# endif
982#endif
983 if (fVerbose)
984 printf("debug: %u unique strings (%u bytes), %u duplicates, %u collisions\n",
985 (unsigned)pThis->cUniqueStrings, (unsigned)pThis->cchUniqueStrings,
986 (unsigned)pThis->cDuplicateStrings, (unsigned)pThis->cCollisions);
987
988 /*
989 * Create papSortedStrings from the hash table. The table is sorted by
990 * string length, with the longer strings first, so that we increase our
991 * chances of locating duplicate substrings.
992 */
993 pThis->papSortedStrings = (PBLDPROGSTRING *)malloc(sizeof(pThis->papSortedStrings[0]) * pThis->cUniqueStrings);
994 if (!pThis->papSortedStrings)
995 return false;
996 pThis->cSortedStrings = 0;
997 size_t idxHash = pThis->cStrHash;
998 while (idxHash-- > 0)
999 {
1000 PBLDPROGSTRING pCur = pThis->papStrHash[idxHash];
1001 if (pCur)
1002 {
1003 do
1004 {
1005 bldProgStrTab_InsertUniqueString(pThis, pCur);
1006 pCur = pCur->pNextCollision;
1007 } while (pCur);
1008 }
1009 }
1010
1011 /*
1012 * Create the actual string table.
1013 */
1014 pThis->pachStrTab = (char *)malloc(pThis->cchUniqueStrings + 1);
1015 if (!pThis->pachStrTab)
1016 return false;
1017 pThis->cchStrTab = 0;
1018 for (size_t i = 0; i < pThis->cSortedStrings; i++)
1019 {
1020 PBLDPROGSTRING pCur = pThis->papSortedStrings[i];
1021 const char * const pszCur = pCur->pszString;
1022 size_t const cchCur = pCur->cchString;
1023 size_t offStrTab = pThis->cchStrTab;
1024
1025 /*
1026 * See if the string is a substring already found in the string table.
1027 * Excluding the zero terminator increases the chances for this.
1028 */
1029 size_t cchLeft = pThis->cchStrTab >= cchCur ? pThis->cchStrTab - cchCur : 0;
1030 const char *pchLeft = pThis->pachStrTab;
1031 char const chFirst = *pszCur;
1032 while (cchLeft > 0)
1033 {
1034 const char *pchCandidate = (const char *)memchr(pchLeft, chFirst, cchLeft);
1035 if (!pchCandidate)
1036 break;
1037 if (memcmp(pchCandidate, pszCur, cchCur) == 0)
1038 {
1039 offStrTab = pchCandidate - pThis->pachStrTab;
1040 break;
1041 }
1042
1043 cchLeft -= pchCandidate + 1 - pchLeft;
1044 pchLeft = pchCandidate + 1;
1045 }
1046
1047 if (offStrTab == pThis->cchStrTab)
1048 {
1049 /*
1050 * See if the start of the string overlaps the end of the string table.
1051 */
1052 if (pThis->cchStrTab && cchCur > 1)
1053 {
1054 cchLeft = RT_MIN(pThis->cchStrTab, cchCur - 1);
1055 pchLeft = &pThis->pachStrTab[pThis->cchStrTab - cchLeft];
1056 while (cchLeft > 0)
1057 {
1058 const char *pchCandidate = (const char *)memchr(pchLeft, chFirst, cchLeft);
1059 if (!pchCandidate)
1060 break;
1061 cchLeft -= pchCandidate - pchLeft;
1062 pchLeft = pchCandidate;
1063 if (memcmp(pchLeft, pszCur, cchLeft) == 0)
1064 {
1065 size_t cchToCopy = cchCur - cchLeft;
1066 memcpy(&pThis->pachStrTab[offStrTab], &pszCur[cchLeft], cchToCopy);
1067 pThis->cchStrTab += cchToCopy;
1068 offStrTab = pchCandidate - pThis->pachStrTab;
1069 break;
1070 }
1071 cchLeft--;
1072 pchLeft++;
1073 }
1074 }
1075
1076 /*
1077 * If we didn't have any luck above, just append the string.
1078 */
1079 if (offStrTab == pThis->cchStrTab)
1080 {
1081 memcpy(&pThis->pachStrTab[offStrTab], pszCur, cchCur);
1082 pThis->cchStrTab += cchCur;
1083 }
1084 }
1085
1086 /*
1087 * Set the string table offset for all the references to this string.
1088 */
1089 do
1090 {
1091 pCur->offStrTab = (uint32_t)offStrTab;
1092 pCur = pCur->pNextRef;
1093 } while (pCur != NULL);
1094 }
1095
1096 if (fVerbose)
1097 printf("debug: String table: %u bytes\n", (unsigned)pThis->cchStrTab);
1098 return true;
1099}
1100
1101
1102#ifdef RT_STRICT
1103/**
1104 * Sanity checks a string table string.
1105 * @param pThis The strint table compiler instance.
1106 * @param pStr The string to check.
1107 */
1108static void BldProgStrTab_CheckStrTabString(PBLDPROGSTRTAB pThis, PBLDPROGSTRING pStr)
1109{
1110 if (pStr->cchString != strlen(pStr->pszString))
1111 abort();
1112 if (pStr->offStrTab >= pThis->cchStrTab)
1113 abort();
1114 if (pStr->offStrTab + pStr->cchString > pThis->cchStrTab)
1115 abort();
1116 if (memcmp(pStr->pszString, &pThis->pachStrTab[pStr->offStrTab], pStr->cchString) != 0)
1117 abort();
1118}
1119#endif
1120
1121
1122/**
1123 * Output the string table string in C string litteral fashion.
1124 *
1125 * @param pThis The strint table instance.
1126 * @param pString The string to print.
1127 * @param pOut The output stream.
1128 */
1129static void BldProgStrTab_PrintCStringLitteral(PBLDPROGSTRTAB pThis, PBLDPROGSTRING pString, FILE *pOut)
1130{
1131 const unsigned char *psz = (const unsigned char *)pString->pszString;
1132 unsigned char uch;
1133 while ((uch = *psz++) != '\0')
1134 {
1135#ifdef BLDPROG_STRTAB_WITH_COMPRESSION
1136 if (pThis->aCompDict[uch].cchString == 1)
1137#else
1138 if (!(uch & 0x80))
1139#endif
1140 {
1141 if (uch != '\'' && uch != '\\')
1142 fputc((char)uch, pOut);
1143 else
1144 {
1145 fputc('\\', pOut);
1146 fputc((char)uch, pOut);
1147 }
1148 }
1149#ifdef BLDPROG_STRTAB_WITH_COMPRESSION
1150# ifndef BLDPROG_STRTAB_PURE_ASCII
1151 else if (uch == 0xff)
1152 {
1153 RTUNICP uc = RTStrGetCp((const char *)psz);
1154 psz += RTStrCpSize(uc);
1155 fprintf(pOut, "\\u%04x", uc);
1156 }
1157# else
1158 else
1159 fputs(pThis->aCompDict[uch].pszString, pOut);
1160# endif
1161#else
1162 else
1163 fprintf(pOut, "\\x%02x", (unsigned)uch);
1164 NOREF(pThis);
1165#endif
1166 }
1167}
1168
1169
1170/**
1171 * Writes the string table code to the output stream.
1172 *
1173 * @param pThis The strint table compiler instance.
1174 * @param pOut The output stream.
1175 * @param pszScope The scoping ("static " or empty string),
1176 * including trailing space.
1177 * @param pszPrefix The variable prefix. Typically "g_" for C programs,
1178 * whereas C++ classes normally use "class::s_g".
1179 * @param pszBaseName The base name for the variables. The user
1180 * accessible variable will end up as just the
1181 * prefix and basename concatenated.
1182 */
1183static void BldProgStrTab_WriteStringTable(PBLDPROGSTRTAB pThis, FILE *pOut,
1184 const char *pszScope, const char *pszPrefix, const char *pszBaseName)
1185{
1186#ifdef RT_STRICT
1187 /*
1188 * Do some quick sanity checks while we're here.
1189 */
1190# ifdef BLDPROG_STRTAB_WITH_COMPRESSION
1191 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aCompDict); i++)
1192 {
1193 if (BldProgBitIsSet(pThis->bmUsedChars, i)
1194 ? pThis->aCompDict[i].cchString != 1 : pThis->aCompDict[i].cchString < 1)
1195 abort();
1196 if (pThis->aCompDict[i].cchString > 1)
1197 BldProgStrTab_CheckStrTabString(pThis, &pThis->aCompDict[i]);
1198 }
1199# endif
1200#endif
1201
1202 /*
1203 * Create a table for speeding up the character categorization.
1204 */
1205 uint8_t abCharCat[256];
1206 memset(abCharCat, 0, sizeof(abCharCat));
1207 abCharCat[(unsigned char)'\\'] = 1;
1208 abCharCat[(unsigned char)'\''] = 1;
1209 for (unsigned i = 0; i < 0x20; i++)
1210 abCharCat[i] = 2;
1211 for (unsigned i = 0x7f; i < 0x100; i++)
1212 abCharCat[i] = 2;
1213#ifdef BLDPROG_STRTAB_WITH_COMPRESSION
1214 for (unsigned i = 0; i < 0x100; i++)
1215 if (!BldProgBitIsSet(pThis->bmUsedChars, i)) /* Encode table references using '\xYY'. */
1216 abCharCat[i] = 2;
1217#endif
1218
1219 /*
1220 * We follow the sorted string table, one string per line.
1221 */
1222 fprintf(pOut,
1223 "#include <iprt/bldprog-strtab.h>\n"
1224 "\n"
1225 "static const char g_achStrTab%s[] =\n"
1226 "{\n",
1227 pszBaseName);
1228
1229 uint32_t off = 0;
1230 for (uint32_t i = 0; i < pThis->cSortedStrings; i++)
1231 {
1232 PBLDPROGSTRING pCur = pThis->papSortedStrings[i];
1233 uint32_t offEnd = pCur->offStrTab + (uint32_t)pCur->cchString;
1234 if (offEnd > off)
1235 {
1236 /* Comment with an uncompressed and more readable version of the string. */
1237 if (off == pCur->offStrTab)
1238 fprintf(pOut, "/* 0x%05x = \"", off);
1239 else
1240 fprintf(pOut, "/* 0X%05x = \"", off);
1241 BldProgStrTab_PrintCStringLitteral(pThis, pCur, pOut);
1242 fputs("\" */\n", pOut);
1243
1244 /* Must use char by char here or we may trigger the max string
1245 length limit in the compiler, */
1246 fputs(" ", pOut);
1247 for (; off < offEnd; off++)
1248 {
1249 unsigned char uch = pThis->pachStrTab[off];
1250 fputc('\'', pOut);
1251 if (abCharCat[uch] == 0)
1252 fputc(uch, pOut);
1253 else if (abCharCat[uch] != 1)
1254 fprintf(pOut, "\\x%02x", (unsigned)uch);
1255 else
1256 {
1257 fputc('\\', pOut);
1258 fputc(uch, pOut);
1259 }
1260 fputc('\'', pOut);
1261 fputc(',', pOut);
1262 }
1263 fputc('\n', pOut);
1264 }
1265 }
1266
1267 fprintf(pOut,
1268 "};\n"
1269 "AssertCompile(sizeof(g_achStrTab%s) == %#x);\n\n",
1270 pszBaseName, (unsigned)pThis->cchStrTab);
1271
1272#ifdef BLDPROG_STRTAB_WITH_COMPRESSION
1273 /*
1274 * Write the compression dictionary.
1275 */
1276 fprintf(pOut,
1277 "static const RTBLDPROGSTRREF g_aCompDict%s[%u] = \n"
1278 "{\n",
1279 pszBaseName, (unsigned)RT_ELEMENTS(pThis->aCompDict));
1280 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aCompDict); i++)
1281 if (pThis->aCompDict[i].cchString > 1)
1282 fprintf(pOut, " /*[%3u]=*/ { %#08x, %#04x }, // %6lu - %s\n", i,
1283 pThis->aCompDict[i].offStrTab, (unsigned)pThis->aCompDict[i].cchString,
1284 (unsigned long)pThis->auCompDictFreq[i], pThis->aCompDict[i].pszString);
1285# ifndef BLDPROG_STRTAB_PURE_ASCII
1286 else if (i == 0xff)
1287 fprintf(pOut, " /*[%3u]=*/ { 0x000000, 0x00 }, // UTF-8 escape\n", i);
1288# endif
1289 else if (i == 0)
1290 fprintf(pOut, " /*[%3u]=*/ { 0x000000, 0x00 }, // unused, because zero terminator\n", i);
1291 else if (i < 0x20)
1292 fprintf(pOut, " /*[%3u]=*/ { 0x000000, 0x00 }, // %02x\n", i, i);
1293 else
1294 fprintf(pOut, " /*[%3u]=*/ { 0x000000, 0x00 }, // '%c'\n", i, (char)i);
1295 fprintf(pOut, "};\n\n");
1296#endif
1297
1298
1299 /*
1300 * Write the string table data structure.
1301 */
1302 fprintf(pOut,
1303 "%sconst RTBLDPROGSTRTAB %s%s = \n"
1304 "{\n"
1305 " /*.pchStrTab = */ &g_achStrTab%s[0],\n"
1306 " /*.cchStrTab = */ sizeof(g_achStrTab%s),\n"
1307 ,
1308 pszScope, pszPrefix, pszBaseName, pszBaseName, pszBaseName);
1309#ifdef BLDPROG_STRTAB_WITH_COMPRESSION
1310 fprintf(pOut,
1311 " /*.cCompDict = */ %u,\n"
1312 " /*.paCompDict = */ &g_aCompDict%s[0]\n"
1313 "};\n"
1314# ifndef BLDPROG_STRTAB_PURE_ASCII /* 255 or 256 entries is how the decoder knows */
1315 , (unsigned)RT_ELEMENTS(pThis->aCompDict) - 1,
1316# else
1317 , (unsigned)RT_ELEMENTS(pThis->aCompDict),
1318# endif
1319 pszBaseName);
1320#else
1321 fprintf(pOut,
1322 " /*.cCompDict = */ 0,\n"
1323 " /*.paCompDict = */ NULL\n"
1324 "};\n");
1325#endif
1326}
1327
1328#if RT_CLANG_PREREQ(4, 0) || RT_GNUC_PREREQ(4, 6)
1329# pragma GCC diagnostic pop
1330#endif
1331
1332#endif /* __cplusplus && IN_RING3 */
1333
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette