• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 12706066811

10 Jan 2025 08:38AM UTC coverage: 70.084% (-2.5%) from 72.549%
12706066811

Pull #11629

github

web-flow
Merge 9418dc48f into 0df468c56
Pull Request #11629: add uv documentation for python package

563296 of 803749 relevant lines covered (70.08%)

223434.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.08
/frmts/rmf/rmflzw.cpp
1
/******************************************************************************
2
 *
3
 * Project:  Raster Matrix Format
4
 * Purpose:  Implementation of the LZW compression algorithm as used in
5
 *           GIS "Panorama"/"Integratsia" raster files. Based on implementation
6
 *           of Kent Williams, but heavily modified over it. The key point
7
 *           in the initial implementation is a hashing algorithm.
8
 * Author:   Andrey Kiselev, dron@ak4719.spb.edu
9
 *
10
 ******************************************************************************
11
 * Copyright (c) 2007, Andrey Kiselev <dron@ak4719.spb.edu>
12
 *
13
 * SPDX-License-Identifier: MIT
14
 ******************************************************************************
15
 * COPYRIGHT NOTICE FROM THE INITIAL IMPLEMENTATION:
16
 *
17
 * The programs LZWCOM and LZWUNC, both in binary executable and source forms,
18
 * are in the public domain.  No warranty is given or implied, and no
19
 * liability will be assumed by the author.
20
 *
21
 * Everyone on earth is hereby given permission to use, copy, distribute,
22
 * change, mangle, destroy or otherwise employ these programs, provided they
23
 * hurt no one but themselves in the process.
24
 *
25
 * Kent Williams
26
 * Norand Inc.
27
 * 550 2nd St S.E.
28
 * Cedar Rapids, Iowa 52401
29
 * (319) 369-3131
30
 ****************************************************************************/
31

32
#include "cpl_conv.h"
33

34
#include "rmfdataset.h"
35

36
// Code marks that there is no predecessor in the string
37
constexpr GUInt32 NO_PRED = 0xFFFF;
38

39
// We are using 12-bit codes in this particular implementation
40
constexpr GUInt32 TABSIZE = 4096U;
41
constexpr GUInt32 STACKSIZE = TABSIZE;
42

43
constexpr GUInt32 NOT_FND = 0xFFFF;
44

45
/************************************************************************/
46
/*                           LZWStringTab                               */
47
/************************************************************************/
48

49
typedef struct
50
{
51
    bool bUsed;
52
    GUInt32 iNext;         // hi bit is 'used' flag
53
    GUInt32 iPredecessor;  // 12 bit code
54
    GByte iFollower;
55
} LZWStringTab;
56

57
/************************************************************************/
58
/*                           LZWUpdateTab()                             */
59
/************************************************************************/
60

61
CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
62
static GUInt32 UnsanitizedMul(GUInt32 a, GUInt32 b)
17,680,400✔
63
{
64
    return a * b;
17,680,400✔
65
}
66

67
static int UnsignedByteToSignedByte(GByte byVal)
18,336,000✔
68
{
69
    return byVal >= 128 ? byVal - 256 : byVal;
18,336,000✔
70
}
71

72
static void LZWUpdateTab(LZWStringTab *poCodeTab, GUInt32 iPred, GByte bFollow)
981,443✔
73
{
74
    /* -------------------------------------------------------------------- */
75
    /* Hash uses the 'mid-square' algorithm. I.E. for a hash val of n bits  */
76
    /* hash = middle binary digits of (key * key).  Upon collision, hash    */
77
    /* searches down linked list of keys that hashed to that key already.   */
78
    /* It will NOT notice if the table is full. This must be handled        */
79
    /* elsewhere                                                            */
80
    /* -------------------------------------------------------------------- */
81
    const int iFollow = UnsignedByteToSignedByte(bFollow);
981,443✔
82
    GUInt32 nLocal = CPLUnsanitizedAdd<GUInt32>(iPred, iFollow) | 0x0800;
981,599✔
83
    nLocal = (UnsanitizedMul(nLocal, nLocal) >> 6) &
981,412✔
84
             0x0FFF;  // middle 12 bits of result
85

86
    // If string is not used
87
    GUInt32 nNext = nLocal;
981,315✔
88
    if (poCodeTab[nLocal].bUsed)
981,315✔
89
    {
90
        // If collision has occurred
91
        while ((nNext = poCodeTab[nLocal].iNext) != 0)
1,919,130✔
92
            nLocal = nNext;
1,256,720✔
93

94
        // Search for free entry from nLocal + 101
95
        nNext = (nLocal + 101) & 0x0FFF;
662,413✔
96
        while (poCodeTab[nNext].bUsed)
32,385,100✔
97
        {
98
            if (++nNext >= TABSIZE)
31,722,700✔
99
                nNext = 0;
2,788✔
100
        }
101

102
        // Put new tempnext into last element in collision list
103
        poCodeTab[nLocal].iNext = nNext;
662,413✔
104
    }
105

106
    poCodeTab[nNext].bUsed = true;
981,315✔
107
    poCodeTab[nNext].iNext = 0;
981,315✔
108
    poCodeTab[nNext].iPredecessor = iPred;
981,315✔
109
    poCodeTab[nNext].iFollower = bFollow;
981,315✔
110
}
981,315✔
111

112
/************************************************************************/
113
/*                           LZWCreateTab()                             */
114
/************************************************************************/
115

116
static LZWStringTab *LZWCreateTab()
261✔
117
{
118
    // Allocate space for the new table and pre-fill it
119
    LZWStringTab *poCodeTab =
120
        (LZWStringTab *)CPLMalloc(TABSIZE * sizeof(LZWStringTab));
261✔
121

122
    memset(poCodeTab, 0, TABSIZE * sizeof(LZWStringTab));
261✔
123

124
    for (GUInt32 iCode = 0; iCode < 256; ++iCode)
67,077✔
125
        LZWUpdateTab(poCodeTab, NO_PRED, static_cast<GByte>(iCode));
66,816✔
126

127
    return poCodeTab;
261✔
128
}
129

130
/************************************************************************/
131
/*                            LZWFindIndex()                            */
132
/************************************************************************/
133

134
static GUInt32 LZWFindIndex(const LZWStringTab *poCodeTab, GUInt32 iPred,
17,143,600✔
135
                            GByte bFollow)
136
{
137
    const int iFollow = UnsignedByteToSignedByte(bFollow);
17,143,600✔
138
    GUInt32 nLocal = CPLUnsanitizedAdd<GUInt32>(iPred, iFollow) | 0x0800;
16,944,100✔
139
    nLocal = (UnsanitizedMul(nLocal, nLocal) >> 6) &
17,024,400✔
140
             0x0FFF;  // middle 12 bits of result
141

142
    do
24,457,800✔
143
    {
144
        CPLAssert(nLocal < TABSIZE);
37,298,400✔
145
        if (poCodeTab[nLocal].iPredecessor == iPred &&
37,298,400✔
146
            poCodeTab[nLocal].iFollower == bFollow)
14,143,500✔
147
        {
148
            return nLocal;
14,116,200✔
149
        }
150
        nLocal = poCodeTab[nLocal].iNext;
23,182,200✔
151
    } while (nLocal > 0);
23,182,200✔
152

153
    return NOT_FND;
×
154
}
155

156
/************************************************************************/
157
/*                             LZWPutCode()                             */
158
/************************************************************************/
159

160
static bool LZWPutCode(GUInt32 iCode, GUInt32 &iTmp, bool &bBitsleft,
5,168,910✔
161
                       GByte *&pabyCurrent, const GByte *const pabyOutEnd)
162
{
163
    if (bBitsleft)
5,168,910✔
164
    {
165
        if (pabyCurrent >= pabyOutEnd)
2,628,710✔
166
        {
167
            return false;
×
168
        }
169
        *(pabyCurrent++) = static_cast<GByte>((iCode >> 4) & 0xFF);
2,628,710✔
170
        iTmp = iCode & 0x000F;
2,628,710✔
171
        bBitsleft = false;
2,628,710✔
172
    }
173
    else
174
    {
175
        if (pabyCurrent + 1 >= pabyOutEnd)
2,540,210✔
176
        {
177
            return false;
28✔
178
        }
179
        *(pabyCurrent++) =
2,540,180✔
180
            static_cast<GByte>(((iTmp << 4) & 0xFF0) + ((iCode >> 8) & 0x00F));
2,540,180✔
181
        *(pabyCurrent++) = static_cast<GByte>(iCode & 0xFF);
2,540,180✔
182
        bBitsleft = true;
2,540,180✔
183
    }
184
    return true;
5,168,880✔
185
}
186

187
/************************************************************************/
188
/*                           LZWReadStream()                            */
189
/************************************************************************/
190

191
static size_t LZWReadStream(const GByte *pabyIn, GUInt32 nSizeIn,
137✔
192
                            GByte *pabyOut, GUInt32 nSizeOut,
193
                            LZWStringTab *poCodeTab)
194
{
195
    GByte *const pabyOutBegin = pabyOut;
137✔
196

197
    // The first code is always known
198
    GUInt32 iCode = (*pabyIn++ << 4) & 0xFF0;
137✔
199
    nSizeIn--;
137✔
200
    iCode += (*pabyIn >> 4) & 0x00F;
137✔
201
    GUInt32 iOldCode = iCode;
137✔
202
    bool bBitsleft = true;
137✔
203

204
    GByte iFinChar = poCodeTab[iCode].iFollower;
137✔
205
    nSizeOut--;
137✔
206
    *pabyOut++ = iFinChar;
137✔
207

208
    GUInt32 nCount = TABSIZE - 256;
137✔
209

210
    // Decompress the input buffer
211
    while (nSizeIn > 0)
4,783,380✔
212
    {
213
        // Fetch 12-bit code from input stream
214
        if (bBitsleft)
4,783,300✔
215
        {
216
            iCode = ((*pabyIn++ & 0x0F) << 8) & 0xF00;
2,391,720✔
217
            nSizeIn--;
2,391,720✔
218
            if (nSizeIn == 0)
2,391,720✔
219
                break;
57✔
220
            iCode += *pabyIn++;
2,391,660✔
221
            nSizeIn--;
2,391,660✔
222
            bBitsleft = FALSE;
2,391,660✔
223
        }
224
        else
225
        {
226
            iCode = (*pabyIn++ << 4) & 0xFF0;
2,391,580✔
227
            nSizeIn--;
2,391,580✔
228
            if (nSizeIn == 0)
2,391,580✔
229
                break;
×
230
            iCode += (*pabyIn >> 4) & 0x00F;
2,391,580✔
231
            bBitsleft = TRUE;
2,391,580✔
232
        }
233

234
        const GUInt32 iInCode = iCode;
4,783,250✔
235
        GByte bLastChar = 0;  // TODO(schwehr): Why not nLastChar?
4,783,250✔
236

237
        // Do we have unknown code?
238
        bool bNewCode = false;
4,783,250✔
239
        if (!poCodeTab[iCode].bUsed)
4,783,250✔
240
        {
241
            iCode = iOldCode;
40,101✔
242
            bLastChar = iFinChar;
40,101✔
243
            bNewCode = true;
40,101✔
244
        }
245

246
        GByte abyStack[STACKSIZE] = {};
4,783,250✔
247
        GByte *pabyTail = abyStack + STACKSIZE;
4,783,250✔
248
        GUInt32 nStackCount = 0;
4,783,250✔
249

250
        while (poCodeTab[iCode].iPredecessor != NO_PRED)
16,371,000✔
251
        {
252
            // Stack overrun
253
            if (nStackCount >= STACKSIZE)
11,587,700✔
254
                return 0;
×
255
            // Put the decoded character into stack
256
            *(--pabyTail) = poCodeTab[iCode].iFollower;
11,587,700✔
257
            nStackCount++;
11,587,700✔
258
            iCode = poCodeTab[iCode].iPredecessor;
11,587,700✔
259
        }
260

261
        if (!nSizeOut)
4,783,250✔
262
            return 0;
×
263
        // The first character
264
        iFinChar = poCodeTab[iCode].iFollower;
4,783,250✔
265
        nSizeOut--;
4,783,250✔
266
        *pabyOut++ = iFinChar;
4,783,250✔
267

268
        // Output buffer overrun
269
        if (nStackCount > nSizeOut)
4,783,250✔
270
            return 0;
×
271

272
        // Now copy the stack contents into output buffer. Our stack was
273
        // filled in reverse order, so no need in character reordering
274
        memcpy(pabyOut, pabyTail, nStackCount);
4,783,250✔
275
        nSizeOut -= nStackCount;
4,783,250✔
276
        pabyOut += nStackCount;
4,783,250✔
277

278
        // If code isn't known
279
        if (bNewCode)
4,783,250✔
280
        {
281
            // Output buffer overrun
282
            if (!nSizeOut)
40,101✔
283
                return 0;
×
284
            iFinChar = bLastChar;  // the follower char of last
40,101✔
285
            *pabyOut++ = iFinChar;
40,101✔
286
            nSizeOut--;
40,101✔
287
        }
288

289
        if (nCount > 0)
4,783,250✔
290
        {
291
            nCount--;
463,213✔
292
            // Add code to the table
293
            LZWUpdateTab(poCodeTab, iOldCode, iFinChar);
463,213✔
294
        }
295

296
        iOldCode = iInCode;
4,783,250✔
297
    }
298

299
    return static_cast<size_t>(pabyOut - pabyOutBegin);
137✔
300
}
301

302
/************************************************************************/
303
/*                           LZWDecompress()                            */
304
/************************************************************************/
305

306
size_t RMFDataset::LZWDecompress(const GByte *pabyIn, GUInt32 nSizeIn,
137✔
307
                                 GByte *pabyOut, GUInt32 nSizeOut, GUInt32,
308
                                 GUInt32)
309
{
310
    if (pabyIn == nullptr || pabyOut == nullptr || nSizeIn < 2)
137✔
311
        return 0;
×
312
    LZWStringTab *poCodeTab = LZWCreateTab();
137✔
313

314
    size_t nRet = LZWReadStream(pabyIn, nSizeIn, pabyOut, nSizeOut, poCodeTab);
137✔
315

316
    CPLFree(poCodeTab);
137✔
317

318
    return nRet;
137✔
319
}
320

321
/************************************************************************/
322
/*                             LZWWriteStream()                         */
323
/************************************************************************/
324

325
static size_t LZWWriteStream(const GByte *pabyIn, GUInt32 nSizeIn,
124✔
326
                             GByte *pabyOut, GUInt32 nSizeOut,
327
                             LZWStringTab *poCodeTab)
328
{
329
    GUInt32 iCode;
330
    iCode = LZWFindIndex(poCodeTab, NO_PRED, *pabyIn++);
124✔
331
    nSizeIn--;
124✔
332

333
    GUInt32 nCount = TABSIZE - 256;
124✔
334
    GUInt32 iTmp = 0;
124✔
335
    bool bBitsleft = true;
124✔
336
    GByte *pabyCurrent = pabyOut;
124✔
337
    GByte *pabyOutEnd = pabyOut + nSizeOut;
124✔
338

339
    while (nSizeIn > 0)
13,734,600✔
340
    {
341
        const GByte bCurrentCode = *pabyIn++;
13,734,500✔
342
        nSizeIn--;
13,734,500✔
343

344
        GUInt32 iNextCode = LZWFindIndex(poCodeTab, iCode, bCurrentCode);
13,734,500✔
345
        if (iNextCode != NOT_FND)
13,664,400✔
346
        {
347
            iCode = iNextCode;
9,598,600✔
348
            continue;
9,598,600✔
349
        }
350

351
        if (!LZWPutCode(iCode, iTmp, bBitsleft, pabyCurrent, pabyOutEnd))
4,065,770✔
352
        {
353
            return 0;
28✔
354
        }
355

356
        if (nCount > 0)
5,189,250✔
357
        {
358
            nCount--;
451,528✔
359
            LZWUpdateTab(poCodeTab, iCode, bCurrentCode);
451,528✔
360
        }
361

362
        iCode = LZWFindIndex(poCodeTab, NO_PRED, bCurrentCode);
5,189,480✔
363
    }
364

365
    if (!LZWPutCode(iCode, iTmp, bBitsleft, pabyCurrent, pabyOutEnd))
96✔
366
    {
367
        return 0;
×
368
    }
369

370
    if (!bBitsleft)
96✔
371
    {
372
        if (pabyCurrent >= pabyOutEnd)
52✔
373
        {
374
            return 0;
×
375
        }
376
        *(pabyCurrent++) = static_cast<GByte>((iTmp << 4) & 0xFF0);
52✔
377
    }
378

379
    return static_cast<size_t>(pabyCurrent - pabyOut);
96✔
380
}
381

382
/************************************************************************/
383
/*                             LZWCompress()                            */
384
/************************************************************************/
385

386
size_t RMFDataset::LZWCompress(const GByte *pabyIn, GUInt32 nSizeIn,
124✔
387
                               GByte *pabyOut, GUInt32 nSizeOut, GUInt32,
388
                               GUInt32, const RMFDataset *)
389
{
390
    if (pabyIn == nullptr || pabyOut == nullptr || nSizeIn == 0)
124✔
391
        return 0;
×
392

393
    // Allocate space for the new table and pre-fill it
394
    LZWStringTab *poCodeTab = LZWCreateTab();
124✔
395

396
    size_t nWritten =
397
        LZWWriteStream(pabyIn, nSizeIn, pabyOut, nSizeOut, poCodeTab);
124✔
398

399
    CPLFree(poCodeTab);
124✔
400

401
    return nWritten;
124✔
402
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc