1 /**
2  * Identifier compression for 32 bits OMF builds
3  *
4  * On 32 bits OMF builds, we have a limit of 128 characters for identifiers.
5  * When this limit is reached, this code is called to attempt to compress the identifier.
6  *
7  * Copyright:   Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
8  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
9  * License:     $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
10  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/backend/compress.d, backend/compress.d)
11  */
12 module dmd.backend.compress;
13 
14 import core.stdc.stdio;
15 import core.stdc.stdlib;
16 import core.stdc.string;
17 
18 nothrow:
19 @safe:
20 
21 /****************************************
22  * Find longest match of pattern[0..plen] in dict[0..dlen].
23  * Returns:
24  *      true if match found
25  */
26 
27 @trusted
28 private bool longest_match(const(char) *dict, int dlen, const(char) *pattern, int plen,
29         int *pmatchoff, int *pmatchlen)
30 {
31     int matchlen = 0;
32     int matchoff;
33 
34     char c = pattern[0];
35     for (int i = 0; i < dlen; i++)
36     {
37         if (dict[i] == c)
38         {
39             int len = dlen - i;
40             if (plen < len)
41                 len = plen;
42             int j;
43             for (j = 1; j < len; j++)
44             {
45                 if (dict[i + j] != pattern[j])
46                     break;
47             }
48             if (j >= matchlen)
49             {
50                 matchlen = j;
51                 matchoff = i;
52             }
53         }
54     }
55 
56     if (matchlen > 1)
57     {
58         *pmatchlen = matchlen;
59         *pmatchoff = matchoff;
60         return true;                    // found a match
61     }
62     return false;                       // no match
63 }
64 
65 /******************************************
66  * Compress an identifier for name mangling purposes.
67  * Format is if ASCII, then it's just the char.
68  * If high bit set, then it's a length/offset pair
69  *
70  * Params:
71  *      id = string to compress
72  *      idlen = length of id
73  *      plen = where to store length of compressed result
74  * Returns:
75  *      malloc'd compressed 0-terminated identifier
76  */
77 @trusted
78 extern(C) char *id_compress(const(char) *id, int idlen, size_t *plen)
79 {
80     int count = 0;
81     char *p = cast(char *)malloc(idlen + 1);
82     for (int i = 0; i < idlen; i++)
83     {
84         int matchoff;
85         int matchlen;
86 
87         int j = 0;
88         if (i > 1023)
89             j = i - 1023;
90 
91         if (longest_match(id + j, i - j, id + i, idlen - i, &matchoff, &matchlen))
92         {   int off;
93 
94             matchoff += j;
95             off = i - matchoff;
96             //printf("matchoff = %3d, matchlen = %2d, off = %d\n", matchoff, matchlen, off);
97             assert(off >= matchlen);
98 
99             if (off <= 8 && matchlen <= 8)
100             {
101                 p[count] = cast(char) (0xC0 | ((off - 1) << 3) | (matchlen - 1));
102                 count++;
103                 i += matchlen - 1;
104                 continue;
105             }
106             else if (matchlen > 2 && off < 1024)
107             {
108                 if (matchlen >= 1024)
109                     matchlen = 1023;    // longest representable match
110                 p[count + 0] = cast(char) (0x80 | ((matchlen >> 4) & 0x38) | ((off >> 7) & 7));
111                 p[count + 1] = cast(char) (0x80 | matchlen);
112                 p[count + 2] = cast(char) (0x80 | off);
113                 count += 3;
114                 i += matchlen - 1;
115                 continue;
116             }
117         }
118         p[count] = id[i];
119         count++;
120     }
121     p[count] = 0;
122     //printf("old size = %d, new size = %d\n", idlen, count);
123     assert(count <= idlen);
124     *plen = count;
125     return p;
126 }