File indexing completed on 2024-12-22 04:10:29
0001 /* 0002 * SPDX-FileCopyrightText: 2005-2006 Ariya Hidayat <ariya@kde.org> 0003 * SPDX-FileCopyrightText: 2010 Dmitry Kazakov <dimula73@gmail.com> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "kis_lzf_compression.h" 0009 #include "kis_debug.h" 0010 0011 0012 #define HASH_LOG 12 0013 #define HASH_SIZE (1<< HASH_LOG) 0014 #define HASH_MASK (HASH_SIZE-1) 0015 0016 #if !defined _MSC_VER 0017 #pragma GCC diagnostic ignored "-Wcast-align" 0018 #endif 0019 #define UPDATE_HASH(v,p) { v = *((quint16*)p); v ^= *((quint16*)(p+1))^(v>>(16-HASH_LOG)); } 0020 0021 #define MAX_COPY 32 0022 #define MAX_LEN 264 /* 256 + 8 */ 0023 #define MAX_DISTANCE 8192 0024 0025 0026 // Lossless compression using LZF algorithm, this is faster on modern CPU than 0027 // the original implementation in http://liblzf.plan9.de/ 0028 int lzff_compress(const void* input, int length, void* output, int /*maxout*/) 0029 { 0030 const quint8* ip = (const quint8*) input; 0031 const quint8* ip_limit = ip + length - MAX_COPY - 4; 0032 quint8* op = (quint8*) output; 0033 0034 const quint8* htab[HASH_SIZE]; 0035 const quint8** hslot; 0036 quint32 hval; 0037 0038 quint8* ref; 0039 qint32 copy; 0040 qint32 len; 0041 qint32 distance; 0042 quint8* anchor; 0043 0044 /* initializes hash table */ 0045 for (hslot = htab; hslot < htab + HASH_SIZE; hslot++) 0046 *hslot = ip; 0047 0048 /* we start with literal copy */ 0049 copy = 0; 0050 *op++ = MAX_COPY - 1; 0051 0052 /* main loop */ 0053 while (ip < ip_limit) { 0054 /* find potential match */ 0055 UPDATE_HASH(hval, ip); 0056 hslot = htab + (hval & HASH_MASK); 0057 ref = (quint8*) * hslot; 0058 0059 /* update hash table */ 0060 *hslot = ip; 0061 0062 /* find itself? then it's no match */ 0063 if (ip == ref) 0064 goto literal; 0065 0066 /* is this a match? check the first 2 bytes */ 0067 if (*((quint16*)ref) != *((quint16*)ip)) 0068 goto literal; 0069 0070 /* now check the 3rd byte */ 0071 if (ref[2] != ip[2]) 0072 goto literal; 0073 0074 /* calculate distance to the match */ 0075 distance = ip - ref; 0076 0077 /* skip if too far away */ 0078 if (distance >= MAX_DISTANCE) 0079 goto literal; 0080 0081 /* here we have 3-byte matches */ 0082 anchor = (quint8*)ip; 0083 len = 3; 0084 ref += 3; 0085 ip += 3; 0086 0087 /* now we have to check how long the match is */ 0088 if (ip < ip_limit - MAX_LEN) { 0089 while (len < MAX_LEN - 8) { 0090 /* unroll 8 times */ 0091 if (*ref++ != *ip++) break; 0092 if (*ref++ != *ip++) break; 0093 if (*ref++ != *ip++) break; 0094 if (*ref++ != *ip++) break; 0095 if (*ref++ != *ip++) break; 0096 if (*ref++ != *ip++) break; 0097 if (*ref++ != *ip++) break; 0098 if (*ref++ != *ip++) break; 0099 len += 8; 0100 } 0101 ip--; 0102 } 0103 len = ip - anchor; 0104 0105 /* just before the last non-matching byte */ 0106 ip = anchor + len; 0107 0108 /* if we have copied something, adjust the copy count */ 0109 if (copy) { 0110 /* copy is biased, '0' means 1 byte copy */ 0111 anchor = anchor - copy - 1; 0112 *(op - copy - 1) = copy - 1; 0113 copy = 0; 0114 } else 0115 /* back, to overwrite the copy count */ 0116 op--; 0117 0118 /* length is biased, '1' means a match of 3 bytes */ 0119 len -= 2; 0120 0121 /* distance is also biased */ 0122 distance--; 0123 0124 /* encode the match */ 0125 if (len < 7) 0126 *op++ = (len << 5) + (distance >> 8); 0127 else { 0128 *op++ = (7 << 5) + (distance >> 8); 0129 *op++ = len - 7; 0130 } 0131 *op++ = (distance & 255); 0132 0133 /* assuming next will be literal copy */ 0134 *op++ = MAX_COPY - 1; 0135 0136 /* update the hash at match boundary */ 0137 --ip; 0138 UPDATE_HASH(hval, ip); 0139 htab[hval & HASH_MASK] = ip; 0140 ip++; 0141 0142 continue; 0143 0144 literal: 0145 *op++ = *ip++; 0146 copy++; 0147 if (copy >= MAX_COPY) { 0148 copy = 0; 0149 *op++ = MAX_COPY - 1; 0150 } 0151 } 0152 0153 /* left-over as literal copy */ 0154 ip_limit = (const quint8*)input + length; 0155 while (ip < ip_limit) { 0156 *op++ = *ip++; 0157 copy++; 0158 if (copy == MAX_COPY) { 0159 copy = 0; 0160 *op++ = MAX_COPY - 1; 0161 } 0162 } 0163 0164 /* if we have copied something, adjust the copy length */ 0165 if (copy) 0166 *(op - copy - 1) = copy - 1; 0167 else 0168 op--; 0169 0170 return op - (quint8*)output; 0171 } 0172 0173 int lzff_decompress(const void* input, int length, void* output, int maxout) 0174 { 0175 const quint8* ip = (const quint8*) input; 0176 const quint8* ip_limit = ip + length - 1; 0177 quint8* op = (quint8*) output; 0178 quint8* op_limit = op + maxout; 0179 quint8* ref; 0180 0181 while (ip < ip_limit) { 0182 quint32 ctrl = (*ip) + 1; 0183 quint32 ofs = ((*ip) & 31) << 8; 0184 quint32 len = (*ip++) >> 5; 0185 0186 if (ctrl < 33) { 0187 /* literal copy */ 0188 if (op + ctrl > op_limit) 0189 return 0; 0190 0191 /* crazy unrolling */ 0192 if (ctrl) { 0193 *op++ = *ip++; 0194 ctrl--; 0195 0196 if (ctrl) { 0197 *op++ = *ip++; 0198 ctrl--; 0199 0200 if (ctrl) { 0201 *op++ = *ip++; 0202 ctrl--; 0203 0204 for (; ctrl; ctrl--) 0205 *op++ = *ip++; 0206 } 0207 } 0208 } 0209 } else { 0210 /* back reference */ 0211 len--; 0212 ref = op - ofs; 0213 ref--; 0214 0215 if (len == 7 - 1) 0216 len += *ip++; 0217 0218 ref -= *ip++; 0219 0220 if (op + len + 3 > op_limit) 0221 return 0; 0222 0223 if (ref < (quint8 *)output) 0224 return 0; 0225 0226 *op++ = *ref++; 0227 *op++ = *ref++; 0228 *op++ = *ref++; 0229 if (len) 0230 for (; len; --len) 0231 *op++ = *ref++; 0232 } 0233 } 0234 0235 return op - (quint8*)output; 0236 } 0237 0238 0239 0240 KisLzfCompression::KisLzfCompression() 0241 { 0242 /** 0243 * TODO: make working memory htab[HASH_SIZE] be in heap 0244 * and shared inside the class 0245 */ 0246 0247 } 0248 0249 KisLzfCompression::~KisLzfCompression() 0250 { 0251 } 0252 0253 qint32 KisLzfCompression::compress(const quint8* input, qint32 inputLength, quint8* output, qint32 outputLength) 0254 { 0255 return lzff_compress(input, inputLength, output, outputLength); 0256 } 0257 0258 qint32 KisLzfCompression::decompress(const quint8* input, qint32 inputLength, quint8* output, qint32 outputLength) 0259 { 0260 return lzff_decompress(input, inputLength, output, outputLength); 0261 } 0262 0263 qint32 KisLzfCompression::outputBufferSize(qint32 dataSize) 0264 { 0265 // WARNING: Copy-pasted from LZO samples, do not know how to prove it 0266 return dataSize + dataSize / 16 + 64 + 3; 0267 }