├── README.md ├── pithy.c └── pithy.h /README.md: -------------------------------------------------------------------------------- 1 | # pithy 2 | 3 | pithy is licensed under the terms of the BSD license.
4 | Copyright © 2011, John Engelhart. 5 | 6 | ### Notes 7 | 8 | pithys roots can be traced back to Googles [snappy][] compression library, but has diverged quite a bit. 9 | 10 | pithy can not read or write the [snappy][] compression format, and vice versa. 11 | 12 | ### TODO 13 | 14 | Write the `README.md` :). 15 | 16 | ### Benchmarks 17 | 18 | - Benchmarks were performed on an iPhone 4 (not S) and compiled with `clang` version 3.0 using `-Os`. 19 | - Compression and decompression speed numbers are in megabytes per second. 20 | - The files used are from the [snappy][] distributions test suite. 21 | - `zlib 1` and `zlib 6` correspond to the `zlib` tunable compression effort, with `1` being fastest, and `6` being the default. 22 | - `pithy 0` and `pithy 2` correspond to the `pithy` tunable compression effort. 23 | 24 | #### Size 25 | 26 | Name | Uncompressed | pithy 0 | pithy 2 | snappy | lzf | lzo | zlib 1 | zlib 6 27 | ---------------------|-------------:|--------:|--------:|-------:|-------:|-------:|--------:|-------: 28 | alice29.txt | 152089 | 93270 | 87767 | 90965 | 82985 | 83568 | 65148 | 54416 29 | asyoulik.txt | 125179 | 83039 | 78170 | 80207 | 72081 | 73849 | 56809 | 48909 30 | baddata1.snappy | 27512 | 27521 | 27521 | 26675 | 26228 | 26491 | 23151 | 22953 31 | baddata2.snappy | 27483 | 27495 | 27495 | 26724 | 26272 | 26531 | 23214 | 23035 32 | baddata3.snappy | 28384 | 28346 | 28346 | 27476 | 27105 | 27388 | 23927 | 23730 33 | cp.html | 24603 | 12398 | 11990 | 11838 | 11869 | 11677 | 9046 | 7973 34 | fields.c | 11150 | 4960 | 4790 | 4728 | 4667 | 4680 | 3665 | 3134 35 | geo.protodata | 118588 | 19984 | 19524 | 27459 | 27748 | 20170 | 18845 | 15143 36 | grammar.lsp | 3721 | 1859 | 1839 | 1800 | 1768 | 1783 | 1344 | 1234 37 | house.jpg | 126958 | 126846 | 126843 | 126803 | 130003 | 127169 | 126488 | 126525 38 | html | 102400 | 21725 | 21077 | 24140 | 22534 | 21059 | 17049 | 13711 39 | html_x_4 | 409600 | 21753 | 21105 | 96472 | 89962 | 82730 | 67470 | 53379 40 | kennedy.xls | 1029744 | 427736 | 425992 | 425735 | 402525 | 359590 | 242311 | 204004 41 | kppkn.gtb | 184320 | 73768 | 68287 | 70535 | 75486 | 72623 | 49877 | 38763 42 | lcet10.txt | 426754 | 247515 | 230250 | 243710 | 225007 | 223028 | 174142 | 144916 43 | mapreduce-osdi-1.pdf | 94330 | 79281 | 79331 | 77477 | 79684 | 77241 | 76414 | 74940 44 | plrabn12.txt | 481861 | 340325 | 317582 | 329339 | 290030 | 297444 | 228901 | 195273 45 | ptt5 | 513216 | 87776 | 83676 | 93455 | 80756 | 86277 | 65571 | 56477 46 | sum | 38240 | 19018 | 18728 | 19837 | 20274 | 17609 | 14130 | 13002 47 | urls.10K | 702087 | 343976 | 316942 | 357267 | 350981 | 312748 | 253275 | 222625 48 | xargs.1 | 4227 | 2603 | 2545 | 2509 | 2441 | 2451 | 1864 | 1748 49 | 50 | #### Compression Speed in MB/s 51 | 52 | Name | pithy 0 | pithy 2 | snappy | lzf | lzo | zlib 1 | zlib 6 53 | ---------------------|--------:|--------:|-------:|-------:|-------:|-------:|------: 54 | alice29.txt | 34.31 | 30.48 | 26.92 | 18.46 | 27.91 | 5.01 | 1.36 55 | asyoulik.txt | 32.67 | 29.09 | 25.63 | 17.52 | 25.91 | 4.60 | 1.24 56 | baddata1.snappy | 332.22 | 236.41 | 37.59 | 12.61 | 13.20 | 3.33 | 2.79 57 | baddata2.snappy | 287.97 | 213.15 | 40.39 | 12.58 | 13.12 | 3.45 | 2.87 58 | baddata3.snappy | 287.98 | 213.21 | 34.57 | 12.57 | 13.03 | 3.38 | 2.82 59 | cp.html | 44.02 | 37.66 | 29.81 | 22.54 | 29.00 | 6.32 | 3.36 60 | fields.c | 43.06 | 35.57 | 26.45 | 25.68 | 39.53 | 6.03 | 3.56 61 | geo.protodata | 90.33 | 82.31 | 63.04 | 37.91 | 60.77 | 12.47 | 5.42 62 | grammar.lsp | 35.15 | 33.17 | 23.19 | 24.99 | 36.98 | 3.73 | 2.93 63 | house.jpg | 712.25 | 450.11 | 209.48 | 11.53 | 10.41 | 2.67 | 2.46 64 | html | 77.14 | 69.61 | 58.09 | 41.57 | 57.68 | 11.02 | 4.65 65 | html_x_4 | 171.03 | 131.75 | 56.00 | 37.63 | 51.18 | 11.18 | 4.36 66 | kennedy.xls | 45.41 | 37.88 | 36.68 | 34.34 | 40.08 | 7.97 | 1.76 67 | kppkn.gtb | 47.66 | 42.32 | 37.38 | 31.08 | 40.32 | 6.80 | 1.23 68 | lcet10.txt | 33.98 | 29.69 | 27.15 | 17.84 | 23.87 | 4.92 | 1.37 69 | mapreduce-osdi-1.pdf | 165.96 | 147.48 | 90.87 | 14.25 | 13.31 | 3.47 | 2.91 70 | plrabn12.txt | 29.28 | 24.61 | 22.84 | 15.80 | 20.64 | 4.14 | 1.03 71 | ptt5 | 80.67 | 70.76 | 68.08 | 41.08 | 49.54 | 11.46 | 3.53 72 | sum | 44.91 | 38.59 | 30.31 | 22.71 | 32.02 | 5.93 | 1.97 73 | urls.10K | 39.15 | 31.68 | 31.11 | 20.44 | 23.31 | 5.71 | 2.47 74 | xargs.1 | 33.60 | 25.68 | 19.67 | 20.26 | 30.31 | 3.77 | 2.99 75 | 76 | 77 | #### Decompression Speed in MB/s 78 | 79 | Name | pithy 0 | pithy 2 | snappy | lzf | lzo | zlib 1 | zlib 6 80 | ---------------------|--------:|--------:|--------:|-------:|-------:|-------:|------: 81 | alice29.txt | 115.76 | 114.84 | 48.92 | 69.53 | 68.74 | 28.71 | 33.94 82 | asyoulik.txt | 112.63 | 110.95 | 47.17 | 66.92 | 65.92 | 27.99 | 31.66 83 | baddata1.snappy | 1544.53 | 1642.51 | 228.20 | 114.57 | 143.39 | 20.58 | 21.42 84 | baddata2.snappy | 1093.85 | 1049.47 | 251.99 | 115.96 | 144.84 | 20.60 | 20.77 85 | baddata3.snappy | 1355.65 | 1355.65 | 213.21 | 114.71 | 143.99 | 20.19 | 21.67 86 | cp.html | 172.58 | 172.58 | 76.19 | 74.72 | 100.70 | 30.83 | 32.77 87 | fields.c | 154.19 | 156.49 | 67.73 | 81.80 | 100.34 | 33.76 | 37.18 88 | geo.protodata | 340.65 | 343.80 | 135.29 | 150.60 | 156.64 | 54.24 | 58.54 89 | grammar.lsp | 154.64 | 154.24 | 72.43 | 78.96 | 104.45 | 22.32 | 23.19 90 | house.jpg | 1985.66 | 897.23 | 1017.70 | 230.62 | 229.32 | 27.60 | 36.28 91 | html | 279.83 | 280.60 | 115.30 | 130.03 | 136.39 | 50.97 | 55.11 92 | html_x_4 | 832.94 | 847.38 | 108.72 | 122.60 | 129.39 | 46.41 | 50.31 93 | kennedy.xls | 147.34 | 147.61 | 52.08 | 56.72 | 95.54 | 32.96 | 36.43 94 | kppkn.gtb | 132.47 | 115.95 | 56.02 | 85.62 | 84.27 | 34.07 | 41.23 95 | lcet10.txt | 109.02 | 107.61 | 45.58 | 60.34 | 61.26 | 27.58 | 30.86 96 | mapreduce-osdi-1.pdf | 478.53 | 463.82 | 257.78 | 182.13 | 185.10 | 29.29 | 30.33 97 | plrabn12.txt | 94.13 | 90.73 | 37.60 | 55.46 | 52.74 | 23.68 | 26.55 98 | ptt5 | 241.46 | 182.63 | 116.23 | 131.32 | 133.36 | 49.16 | 52.61 99 | sum | 162.12 | 147.64 | 67.91 | 74.89 | 98.04 | 29.18 | 33.77 100 | urls.10K | 156.08 | 153.04 | 68.10 | 62.93 | 77.84 | 29.52 | 31.03 101 | xargs.1 | 149.63 | 144.20 | 63.98 | 72.03 | 93.80 | 22.03 | 23.30 102 | 103 | 104 | [snappy]: http://code.google.com/p/snappy/ 105 | -------------------------------------------------------------------------------- /pithy.c: -------------------------------------------------------------------------------- 1 | // 2 | // pithy.c 3 | // http://github.com/johnezang/pithy 4 | // Licensed under the terms of the BSD License, as specified below. 5 | // 6 | 7 | /* 8 | Copyright (c) 2011, John Engelhart 9 | 10 | All rights reserved. 11 | 12 | Redistribution and use in source and binary forms, with or without 13 | modification, are permitted provided that the following conditions are met: 14 | 15 | * Redistributions of source code must retain the above copyright 16 | notice, this list of conditions and the following disclaimer. 17 | 18 | * Redistributions in binary form must reproduce the above copyright 19 | notice, this list of conditions and the following disclaimer in the 20 | documentation and/or other materials provided with the distribution. 21 | 22 | * Neither the name of the Zang Industries nor the names of its 23 | contributors may be used to endorse or promote products derived from 24 | this software without specific prior written permission. 25 | 26 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 27 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 28 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 29 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 30 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 31 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 32 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 33 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 34 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 35 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 36 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 37 | */ 38 | 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | #if defined(__arm__) && defined(__ARM_NEON__) 45 | #include 46 | #endif 47 | 48 | #include "pithy.h" 49 | 50 | #define kBlockLog 20ul 51 | #define kBlockSize ((size_t)(1 << kBlockLog)) 52 | 53 | // The maximum size that can be compressed while still allowing for the worst case compression expansion. 54 | #define PITHY_UNCOMPRESSED_MAX_LENGTH 0xdb6db6bfu // 0xdb6db6bf == 3681400511, or 3510.857 Mbytes. 55 | 56 | #if defined(__i386__) || defined(__x86_64__) || defined(__powerpc__) || defined(__arm__) 57 | #define PITHY_UNALIGNED_LOADS_AND_STORES 58 | #endif 59 | 60 | typedef const char * pithy_hashOffset_t; 61 | 62 | enum { 63 | PITHY_LITERAL = 0, 64 | PITHY_COPY_1_BYTE_OFFSET = 1, 65 | PITHY_COPY_2_BYTE_OFFSET = 2, 66 | PITHY_COPY_3_BYTE_OFFSET = 3 67 | }; 68 | 69 | 70 | #if defined (__GNUC__) && (__GNUC__ >= 3) 71 | #define PITHY_ATTRIBUTES(attr, ...) __attribute__((attr, ##__VA_ARGS__)) 72 | #define PITHY_EXPECTED(cond, expect) __builtin_expect((long)(cond), (expect)) 73 | #define PITHY_EXPECT_T(cond) PITHY_EXPECTED(cond, 1u) 74 | #define PITHY_EXPECT_F(cond) PITHY_EXPECTED(cond, 0u) 75 | #define PITHY_PREFETCH(ptr) __builtin_prefetch(ptr) 76 | #else // defined (__GNUC__) && (__GNUC__ >= 3) 77 | #define PITHY_ATTRIBUTES(attr, ...) 78 | #define PITHY_EXPECTED(cond, expect) (cond) 79 | #define PITHY_EXPECT_T(cond) (cond) 80 | #define PITHY_EXPECT_F(cond) (cond) 81 | #define PITHY_PREFETCH(ptr) 82 | #endif // defined (__GNUC__) && (__GNUC__ >= 3) 83 | 84 | #define PITHY_STATIC_INLINE static __inline__ PITHY_ATTRIBUTES(always_inline) 85 | #define PITHY_ALIGNED(x) PITHY_ATTRIBUTES(aligned(x)) 86 | 87 | #if defined(NS_BLOCK_ASSERTIONS) && !defined(NDEBUG) 88 | #define NDEBUG 89 | #endif 90 | 91 | #ifdef NDEBUG 92 | #define DCHECK(condition) 93 | #else 94 | #define DCHECK(condition) do { if(PITHY_EXPECT_F(!(condition))) { fprintf(stderr, "%s / %s @ %ld: Invalid parameter not satisfying: %s", __FILE__, __PRETTY_FUNCTION__, (long)__LINE__, #condition); fflush(stderr); abort(); } } while(0) 95 | #endif 96 | 97 | 98 | PITHY_STATIC_INLINE const char *pithy_Parse32WithLimit(const char *p, const char *l, size_t *OUTPUT); 99 | PITHY_STATIC_INLINE char *pithy_Encode32(char *ptr, uint32_t v); 100 | 101 | PITHY_STATIC_INLINE uint32_t pithy_GetUint32AtOffset(uint64_t v, uint32_t offset); 102 | PITHY_STATIC_INLINE uint32_t pithy_HashBytes(uint32_t bytes, uint32_t shift); 103 | PITHY_STATIC_INLINE size_t pithy_FindMatchLength(const char *s1, const char *s2, const char *s2_limit); 104 | PITHY_STATIC_INLINE char *pithy_EmitLiteral(char *op, const char *literal, size_t len, int allow_fast_path); 105 | PITHY_STATIC_INLINE char *pithy_EmitCopyGreaterThan63(char *op, size_t offset, size_t len); 106 | PITHY_STATIC_INLINE char *pithy_EmitCopyLessThan63(char *op, size_t offset, size_t len); 107 | PITHY_STATIC_INLINE char *pithy_EmitCopy(char *op, size_t offset, size_t len); 108 | 109 | 110 | #ifdef PITHY_UNALIGNED_LOADS_AND_STORES 111 | 112 | #define pithy_Load16(_p) (*((uint16_t *)(_p))) 113 | #define pithy_Load32(_p) (*((uint32_t *)(_p))) 114 | #define pithy_Store16(_p, _val) (*((uint16_t *)(_p)) = (_val)) 115 | #define pithy_Store32(_p, _val) (*((uint32_t *)(_p)) = (_val)) 116 | 117 | #if defined(__arm__) 118 | 119 | #if defined(__ARM_NEON__) 120 | #define pithy_Load64(_p) ((uint64_t)vld1_u64(_p)) 121 | #define pithy_Store64(_p, _val) vst1_u64(_p, (uint64x1_t)_val) 122 | #else 123 | #define PITHY_32BIT_MOVE64 124 | PITHY_STATIC_INLINE uint64_t pithy_Load64(const void *p) { uint64_t t; memcpy(&t, p, sizeof(t)); return(t); } 125 | PITHY_STATIC_INLINE void pithy_Store64(void *p, uint64_t v) { memcpy(p, &v, sizeof(v)); } 126 | #endif 127 | 128 | #else // not __arm__ 129 | #define pithy_Load64(_p) (*((uint64_t *)(_p))) 130 | #define pithy_Store64(_p, _val) (*((uint64_t *)(_p)) = (_val)) 131 | #endif // __arm__ 132 | 133 | #else 134 | 135 | PITHY_STATIC_INLINE uint16_t pithy_Load16(const void *p) { uint16_t t; memcpy(&t, p, sizeof(t)); return(t); } 136 | PITHY_STATIC_INLINE uint32_t pithy_Load32(const void *p) { uint32_t t; memcpy(&t, p, sizeof(t)); return(t); } 137 | PITHY_STATIC_INLINE uint64_t pithy_Load64(const void *p) { uint64_t t; memcpy(&t, p, sizeof(t)); return(t); } 138 | PITHY_STATIC_INLINE void pithy_Store16(void *p, uint16_t v) { memcpy(p, &v, sizeof(v)); } 139 | PITHY_STATIC_INLINE void pithy_Store32(void *p, uint32_t v) { memcpy(p, &v, sizeof(v)); } 140 | PITHY_STATIC_INLINE void pithy_Store64(void *p, uint64_t v) { memcpy(p, &v, sizeof(v)); } 141 | 142 | #endif 143 | 144 | 145 | #ifdef PITHY_32BIT_MOVE64 146 | #define pithy_Move64(dst,src) pithy_Store32(dst, pithy_Load32(src)); pithy_Store32(dst + 4ul, pithy_Load32(src + 4ul)); 147 | #else 148 | #define pithy_Move64(dst,src) pithy_Store64(dst, pithy_Load64(src)); 149 | #endif 150 | 151 | 152 | #if defined(__arm__) && defined(__ARM_NEON__) && defined(PITHY_UNALIGNED_LOADS_AND_STORES) 153 | #define pithy_Move128(_dst,_src) ({ asm ("vld1.64 {d0-d1}, [%[src]]\n\tvst1.64 {d0-d1}, [%[dst]]" : : [src]"r"(_src), [dst]"r"(_dst) : "d0", "d1", "memory"); }) 154 | #else 155 | #define pithy_Move128(dst,src) pithy_Move64(dst, src); pithy_Move64(dst + 8ul, src + 8ul); 156 | #endif 157 | 158 | #ifdef __BIG_ENDIAN__ 159 | 160 | #ifdef _MSC_VER 161 | #include 162 | #define pithy_bswap_16(x) _byteswap_ushort(x) 163 | #define pithy_bswap_32(x) _byteswap_ulong(x) 164 | #define pithy_bswap_64(x) _byteswap_uint64(x) 165 | 166 | #elif defined(__APPLE__) 167 | 168 | // Mac OS X / Darwin features 169 | #include 170 | #define pithy_bswap_16(x) OSSwapInt16(x) 171 | #define pithy_bswap_32(x) OSSwapInt32(x) 172 | #define pithy_bswap_64(x) OSSwapInt64(x) 173 | #else 174 | #include 175 | #endif 176 | 177 | #endif // __BIG_ENDIAN__ 178 | 179 | // Conversion functions. 180 | #ifdef __BIG_ENDIAN__ 181 | #define pithy_FromHost16(x) ((uint16_t)pithy_bswap_16(x)) 182 | #define pithy_ToHost16(x) ((uint16_t)pithy_bswap_16(x)) 183 | #define pithy_FromHost32(x) ((uint32_t)pithy_bswap_32(x)) 184 | #define pithy_ToHost32(x) ((uint32_t)pithy_bswap_32(x)) 185 | #define pithy_IsLittleEndian() (0) 186 | 187 | #else // !defined(__BIG_ENDIAN__) 188 | #define pithy_FromHost16(x) ((uint16_t)(x)) 189 | #define pithy_ToHost16(x) ((uint16_t)(x)) 190 | #define pithy_FromHost32(x) ((uint32_t)(x)) 191 | #define pithy_ToHost32(x) ((uint32_t)(x)) 192 | #define pithy_IsLittleEndian() (1) 193 | 194 | #endif // !defined(__BIG_ENDIAN__) 195 | 196 | #define pithy_LoadHost16(p) pithy_ToHost16(pithy_Load16((const void *)(p))) 197 | #define pithy_StoreHost16(p, v) pithy_Store16((void *)(p), pithy_FromHost16(v)) 198 | #define pithy_LoadHost32(p) pithy_ToHost32(pithy_Load32((p))) 199 | #define pithy_StoreHost32(p, v) pithy_Store32((p), pithy_FromHost32(v)) 200 | 201 | PITHY_STATIC_INLINE void pithy_StoreHost24(char *p, uint32_t v) { *p++ = (v & 0xffu); *p++ = ((v >> 8) & 0xffu); *p++ = ((v >> 16) & 0xffu); } 202 | 203 | #if defined (__GNUC__) && (__GNUC__ >= 3) 204 | 205 | #define pithy_Log2Floor(n) ({typeof(n) _n = (n); _n == 0 ? (int)-1 : (int)(31 ^ __builtin_clz(_n));}) 206 | #define pithy_FindLSBSetNonZero32(n) ((int)__builtin_ctz((uint32_t)(n))) 207 | #define pithy_FindLSBSetNonZero64(n) ((int)__builtin_ctzll((uint64_t)(n))) 208 | #define pithy_FindMSBSetNonZero32(n) ((int)__builtin_clz((uint32_t)(n))) 209 | #define pithy_FindMSBSetNonZero64(n) ((int)__builtin_clzll((uint64_t)(n))) 210 | 211 | #else // Portable versions, !GNUC || GNUC < 3 212 | 213 | PITHY_STATIC_INLINE int pithy_Log2Floor(uint32_t n) { 214 | if(n == 0u) { return(-1); } 215 | int i = 0, log = 0; 216 | uint32_t value = n; 217 | for(i = 4; i >= 0; --i) { int shift = (1 << i); uint32_t x = value >> shift; if(x != 0u) { value = x; log += shift; } } 218 | DCHECK(value == 1); 219 | return(log); 220 | } 221 | 222 | PITHY_STATIC_INLINE int pithy_FindLSBSetNonZero32(uint32_t n) { 223 | int i = 0, rc = 31; 224 | for(i = 4, shift = 1 << 4; i >= 0; --i) { const uint32_t x = n << shift; if(x != 0u) { n = x; rc -= shift; } shift >>= 1; } 225 | return(rc); 226 | } 227 | 228 | PITHY_STATIC_INLINE int pithy_FindLSBSetNonZero64(uint64_t n) { 229 | const uint32_t bottomBits = n; 230 | if(bottomBits == 0u) { return(32 + pithy_FindLSBSetNonZero32((n >> 32))); } else { return(pithy_FindLSBSetNonZero32(bottomBits)); } 231 | } 232 | 233 | PITHY_STATIC_INLINE int pithy_FindMSBSetNonZero32(uint32_t n) { 234 | int i; 235 | uint32_t x, rc = 32, shift = 1 << 4; 236 | for(i = 3; i >= 0; --i) { x = n >> shift; if(x != 0) { rc -= shift; n = x; } shift >>= 1; } 237 | x = n >> shift; 238 | return(rc - ((x != 0) ? 2 : n)); 239 | } 240 | 241 | PITHY_STATIC_INLINE int pithy_FindMSBSetNonZero64(uint64_t n) { 242 | const uint32_t upperBits = n >> 32; 243 | if(upperBits == 0u) { return(32 + pithy_FindMSBSetNonZero32((n))); } else { return(pithy_FindMSBSetNonZero32(upperBits)); } 244 | } 245 | 246 | #endif // End portable versions. 247 | 248 | 249 | 250 | 251 | 252 | 253 | PITHY_STATIC_INLINE const char *pithy_Parse32WithLimit(const char *p, const char *l, size_t *resultOut) { 254 | const unsigned char *ptr = (const unsigned char *)p, *limit = (const unsigned char *)l; 255 | size_t resultShift = 0ul, result = 0ul; 256 | uint32_t encodedByte = 0u; 257 | 258 | for(resultShift = 0ul; resultShift <= 28ul; resultShift += 7ul) { if(ptr >= limit) { return(NULL); } encodedByte = *(ptr++); result |= (encodedByte & 127u) << resultShift; if(encodedByte < ((resultShift == 28ul) ? 16u : 128u)) { goto done; } } 259 | return(NULL); 260 | done: 261 | *resultOut = result; 262 | return((const char *)ptr); 263 | } 264 | 265 | PITHY_STATIC_INLINE char *pithy_Encode32(char *sptr, uint32_t v) { 266 | unsigned char *ptr = (unsigned char *)sptr; 267 | if(v < (1u << 7)) { *(ptr++) = v; } 268 | else if(v < (1u << 14)) { *(ptr++) = v | 0x80u; *(ptr++) = (v>>7); } 269 | else if(v < (1u << 21)) { *(ptr++) = v | 0x80u; *(ptr++) = (v>>7) | 0x80u; *(ptr++) = (v>>14); } 270 | else if(v < (1u << 28)) { *(ptr++) = v | 0x80u; *(ptr++) = (v>>7) | 0x80u; *(ptr++) = (v>>14) | 0x80u; *(ptr++) = (v>>21); } 271 | else { *(ptr++) = v | 0x80u; *(ptr++) = (v>>7) | 0x80u; *(ptr++) = (v>>14) | 0x80u; *(ptr++) = (v>>21) | 0x80u; *(ptr++) = (v>>28); } 272 | return((char *)ptr); 273 | } 274 | 275 | 276 | 277 | 278 | PITHY_STATIC_INLINE uint32_t pithy_GetUint32AtOffset(uint64_t v, uint32_t offset) { 279 | DCHECK(offset <= 4); 280 | return(v >> (pithy_IsLittleEndian() ? (8u * offset) : (32u - (8u * offset)))); 281 | } 282 | 283 | 284 | PITHY_STATIC_INLINE uint32_t pithy_HashBytes(uint32_t bytes, uint32_t shift) { uint32_t kMul = 0x1e35a7bdU; return((bytes * kMul) >> shift); } 285 | 286 | 287 | PITHY_STATIC_INLINE size_t pithy_FindMatchLength(const char *s1, const char *s2, const char *s2_limit) { 288 | DCHECK(s2_limit >= s2); 289 | const char *ms1 = s1, *ms2 = s2; 290 | 291 | #if defined(__LP64__) 292 | while(PITHY_EXPECT_T(ms2 < (s2_limit - 8ul))) { uint64_t x = pithy_Load64(ms1) ^ pithy_Load64(ms2); if(PITHY_EXPECT_F(x == 0ul)) { ms1 += 8ul; ms2 += 8ul; } else { return((ms1 - s1) + ((unsigned int)(pithy_IsLittleEndian() ? (pithy_FindLSBSetNonZero64(x) >> 3) : (pithy_FindMSBSetNonZero64(x) >> 3)))); } } 293 | #else 294 | while(PITHY_EXPECT_T(ms2 < (s2_limit - 4u ))) { uint32_t x = pithy_Load32(ms1) ^ pithy_Load32(ms2); if(PITHY_EXPECT_F(x == 0u )) { ms1 += 4u; ms2 += 4u; } else { return((ms1 - s1) + ((unsigned int)(pithy_IsLittleEndian() ? (pithy_FindLSBSetNonZero32(x) >> 3) : (pithy_FindMSBSetNonZero32(x) >> 3)))); } } 295 | #endif 296 | while(PITHY_EXPECT_T(ms2 < s2_limit)) { if(PITHY_EXPECT_T(*ms1 == *ms2)) { ms1++; ms2++; } else { return( ms1 - s1); } } 297 | return(ms1 - s1); 298 | } 299 | 300 | 301 | 302 | PITHY_STATIC_INLINE char *pithy_EmitLiteral(char *op, const char *literal, size_t len, int allow_fast_path) { 303 | ssize_t n = len - 1l; 304 | if(PITHY_EXPECT_T(n < 60l)) { *op++ = PITHY_LITERAL | (n << 2); if(PITHY_EXPECT_T(allow_fast_path) && PITHY_EXPECT_T(len <= 16ul)) { pithy_Move128(op, literal); return(op + len); } } 305 | else { char *base = op; int count = 0; op++; while(n > 0l) { *op++ = n & 0xff; n >>= 8; count++; } DCHECK((count >= 1) && (count <= 4)); *base = PITHY_LITERAL | ((59 + count) << 2); } 306 | memcpy(op, literal, len); 307 | return(op + len); 308 | } 309 | 310 | PITHY_STATIC_INLINE char *pithy_EmitCopyGreaterThan63(char *op, size_t offset, size_t len) { 311 | DCHECK((len < 65536ul) && (len >= 63ul) && (offset < kBlockSize)); 312 | if(PITHY_EXPECT_T(offset < 65536ul)) { if(PITHY_EXPECT_T(len < (256ul + 63ul))) { *op++ = PITHY_COPY_2_BYTE_OFFSET | (62 << 2); pithy_StoreHost16(op, offset); op += 2ul; *op++ = (len - 63ul); } 313 | else { *op++ = PITHY_COPY_2_BYTE_OFFSET | (63 << 2); pithy_StoreHost16(op, offset); op += 2ul; pithy_StoreHost16(op, len); op += 2ul; } } 314 | else { if(PITHY_EXPECT_T(len < (256ul + 63ul))) { *op++ = PITHY_COPY_3_BYTE_OFFSET | (62 << 2); pithy_StoreHost24(op, offset); op += 3ul; *op++ = (len - 63ul); } 315 | else { *op++ = PITHY_COPY_3_BYTE_OFFSET | (63 << 2); pithy_StoreHost24(op, offset); op += 3ul; pithy_StoreHost16(op, len); op += 2ul; } } 316 | return(op); 317 | } 318 | 319 | PITHY_STATIC_INLINE char *pithy_EmitCopyLessThan63(char *op, size_t offset, size_t len) { 320 | DCHECK((len < 63ul) && (len >= 4ul) && (offset < kBlockSize)); 321 | if(PITHY_EXPECT_T(len < 12ul) && PITHY_EXPECT_T(offset < 2048ul)) { ssize_t lenMinus4 = len - 4l; DCHECK(lenMinus4 < 8l); *op++ = PITHY_COPY_1_BYTE_OFFSET | (lenMinus4 << 2) | ((offset >> 8) << 5); *op++ = offset & 0xff; } 322 | else { if(PITHY_EXPECT_T(offset < 65536ul)) { *op++ = PITHY_COPY_2_BYTE_OFFSET | ((len - 1ul) << 2); pithy_StoreHost16(op, offset); op += 2ul; } 323 | else { *op++ = PITHY_COPY_3_BYTE_OFFSET | ((len - 1ul) << 2); pithy_StoreHost24(op, offset); op += 3ul; } } 324 | return(op); 325 | } 326 | 327 | PITHY_STATIC_INLINE char *pithy_EmitCopy(char *op, size_t offset, size_t len) { 328 | while(PITHY_EXPECT_F(len >= 63ul)) { op = pithy_EmitCopyGreaterThan63(op, offset, (len >= 65539ul) ? 65535ul : len); len -= (len >= 65539ul) ? 65535ul : len; } 329 | DCHECK((len > 0ul) ? ((len >= 4ul) && (len < 63ul)) : 1); 330 | if( PITHY_EXPECT_T(len > 0ul)) { op = pithy_EmitCopyLessThan63( op, offset, len); len -= len; } 331 | return(op); 332 | } 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | size_t pithy_MaxCompressedLength(size_t inputLength) { 343 | return((inputLength >= PITHY_UNCOMPRESSED_MAX_LENGTH) ? 0ul : 32ul + inputLength + (inputLength / 6ul)); 344 | } 345 | 346 | size_t pithy_Compress(const char *uncompressed, size_t uncompressedLength, char *compressedOut, size_t compressedOutLength, int compressionLevel) { 347 | if((pithy_MaxCompressedLength(uncompressedLength) > compressedOutLength) || (uncompressedLength >= PITHY_UNCOMPRESSED_MAX_LENGTH) || (uncompressedLength == 0ul)) { return(0ul); } 348 | char *compressedPtr = compressedOut; 349 | 350 | size_t hashTableSize = 0x100ul, maxHashTableSize = 1 << (12ul + (((compressionLevel < 0) ? 0 : (compressionLevel > 9) ? 9 : compressionLevel) / 2ul)); 351 | while((hashTableSize < maxHashTableSize) && (hashTableSize < uncompressedLength)) { hashTableSize <<= 1; } 352 | pithy_hashOffset_t stackHashTable[hashTableSize], *heapHashTable = NULL, *hashTable = stackHashTable; 353 | if((sizeof(pithy_hashOffset_t) * hashTableSize) >= (1024ul * 128ul)) { if((heapHashTable = malloc(sizeof(pithy_hashOffset_t) * hashTableSize)) == NULL) { return(0ul); } hashTable = heapHashTable; } 354 | size_t x = 0ul; 355 | for(x = 0ul; x < hashTableSize; x++) { hashTable[x] = uncompressed; } 356 | 357 | #ifndef NDEBUG 358 | char * const compressedOutEnd = compressedOut + compressedOutLength; 359 | #endif 360 | compressedPtr = pithy_Encode32(compressedPtr, uncompressedLength); 361 | DCHECK(compressedPtr <= compressedOutEnd); 362 | 363 | { 364 | const char *uncompressedPtr = uncompressed, *uncompressedEnd = uncompressed + uncompressedLength, *nextEmitUncompressedPtr = uncompressedPtr; 365 | DCHECK((hashTableSize & (hashTableSize - 1l)) == 0); 366 | const int shift = 32 - pithy_Log2Floor(hashTableSize); 367 | DCHECK((UINT32_MAX >> shift) == (hashTableSize - 1l)); 368 | size_t skip = 32ul; 369 | 370 | if(PITHY_EXPECT_T(uncompressedLength >= 15ul)) { 371 | const char *uncompressedEndLimit = uncompressed + uncompressedLength - 15ul; 372 | uint32_t uncompressedBytes, nextUncompressedBytes = pithy_Load32(++uncompressedPtr), nextUncompressedBytesHash = pithy_HashBytes(nextUncompressedBytes, shift); 373 | 374 | while(1) { 375 | DCHECK(nextEmitUncompressedPtr < uncompressedPtr); 376 | const char *nextUncompressedPtr = uncompressedPtr, *matchCandidatePtr = NULL; 377 | 378 | skip = (((skip - 32ul) * 184ul) >> 8) + 32ul; 379 | 380 | do { 381 | uncompressedPtr = nextUncompressedPtr; 382 | uncompressedBytes = nextUncompressedBytes; 383 | uint32_t uncompressedBytesHash = nextUncompressedBytesHash; 384 | DCHECK(uncompressedBytesHash == pithy_HashBytes(uncompressedBytes, shift)); 385 | size_t skipBytesBetweenHashLookups = skip >> 5; 386 | skip += ((skip * 7ul) >> 11) + 1ul; 387 | nextUncompressedPtr = uncompressedPtr + skipBytesBetweenHashLookups; 388 | if(PITHY_EXPECT_F(nextUncompressedPtr > uncompressedEndLimit)) { goto emit_remainder; } 389 | nextUncompressedBytes = pithy_Load32(nextUncompressedPtr); 390 | nextUncompressedBytesHash = pithy_HashBytes(nextUncompressedBytes, shift); 391 | matchCandidatePtr = hashTable[uncompressedBytesHash]; 392 | DCHECK((matchCandidatePtr >= uncompressed) && (matchCandidatePtr < uncompressedPtr)); 393 | hashTable[uncompressedBytesHash] = uncompressedPtr; 394 | } while((PITHY_EXPECT_T(uncompressedBytes != pithy_Load32(matchCandidatePtr))) || PITHY_EXPECT_F((uncompressedPtr - matchCandidatePtr) >= ((ssize_t)(kBlockSize - 2ul)))); 395 | 396 | DCHECK((nextEmitUncompressedPtr + 16ul) <= uncompressedEnd); 397 | compressedPtr = pithy_EmitLiteral(compressedPtr, nextEmitUncompressedPtr, uncompressedPtr - nextEmitUncompressedPtr, 1); 398 | DCHECK(compressedPtr <= compressedOutEnd); 399 | uint64_t uncompressedBytes64 = 0ul; 400 | 401 | do { 402 | if(compressionLevel > 2) { 403 | DCHECK((uncompressedPtr + 5ul) <= uncompressedEnd); 404 | uncompressedBytes64 = pithy_Load64(uncompressedPtr + 1ul); 405 | hashTable[pithy_HashBytes(pithy_GetUint32AtOffset(uncompressedBytes64, 0u), shift)] = uncompressedPtr + 1ul; 406 | if(compressionLevel > 4) { hashTable[pithy_HashBytes(pithy_GetUint32AtOffset(uncompressedBytes64, 1u), shift)] = uncompressedPtr + 2ul; } 407 | } 408 | 409 | DCHECK((matchCandidatePtr >= uncompressed) && (matchCandidatePtr <= uncompressedPtr) && ((matchCandidatePtr + 4ul) <= uncompressedEnd) && ((uncompressedPtr + 4ul) <= uncompressedEnd)); 410 | size_t matchCandidateLength = 4ul + pithy_FindMatchLength(matchCandidatePtr + 4ul, uncompressedPtr + 4ul, uncompressedEnd); 411 | DCHECK(((matchCandidatePtr + matchCandidateLength) >= uncompressed) && ((matchCandidatePtr + matchCandidateLength) <= uncompressedEnd)); 412 | DCHECK(0 == memcmp(uncompressedPtr, matchCandidatePtr, matchCandidateLength)); 413 | compressedPtr = pithy_EmitCopy(compressedPtr, uncompressedPtr - matchCandidatePtr, matchCandidateLength); 414 | DCHECK(compressedPtr <= compressedOutEnd); 415 | uncompressedPtr += matchCandidateLength; 416 | DCHECK(uncompressedPtr <= uncompressedEnd); 417 | nextEmitUncompressedPtr = uncompressedPtr; 418 | if(PITHY_EXPECT_F(uncompressedPtr >= uncompressedEndLimit)) { goto emit_remainder; } 419 | 420 | DCHECK(((uncompressedPtr - 3ul) >= uncompressed) && (uncompressedPtr <= uncompressedEnd)); 421 | 422 | uncompressedBytes64 = pithy_Load64(uncompressedPtr - 3ul); 423 | 424 | if(compressionLevel > 0) { 425 | if(compressionLevel > 8) { hashTable[pithy_HashBytes(pithy_GetUint32AtOffset(uncompressedBytes64, 0u), shift)] = uncompressedPtr - 3ul; } 426 | if(compressionLevel > 6) { hashTable[pithy_HashBytes(pithy_GetUint32AtOffset(uncompressedBytes64, 1u), shift)] = uncompressedPtr - 2ul; } 427 | hashTable[pithy_HashBytes(pithy_GetUint32AtOffset(uncompressedBytes64, 2u), shift)] = uncompressedPtr - 1ul; 428 | } 429 | 430 | uncompressedBytes = pithy_GetUint32AtOffset(uncompressedBytes64, 3u); 431 | uint32_t uncompressedBytesHash = pithy_HashBytes(uncompressedBytes, shift); 432 | matchCandidatePtr = hashTable[uncompressedBytesHash]; 433 | DCHECK((matchCandidatePtr >= uncompressed) && (matchCandidatePtr < uncompressedPtr)); 434 | hashTable[uncompressedBytesHash] = uncompressedPtr; 435 | } while(PITHY_EXPECT_F(uncompressedBytes == pithy_Load32(matchCandidatePtr)) && PITHY_EXPECT_T((uncompressedPtr - matchCandidatePtr) < ((ssize_t)(kBlockSize - 2ul)))); 436 | 437 | nextUncompressedBytes = pithy_GetUint32AtOffset(uncompressedBytes64, 4u); 438 | nextUncompressedBytesHash = pithy_HashBytes(nextUncompressedBytes, shift); 439 | uncompressedPtr++; 440 | } 441 | } 442 | 443 | emit_remainder: 444 | if(nextEmitUncompressedPtr < uncompressedEnd) { compressedPtr = pithy_EmitLiteral(compressedPtr, nextEmitUncompressedPtr, uncompressedEnd - nextEmitUncompressedPtr, 0); } 445 | } 446 | 447 | pithy_Store32(compressedPtr, 0); compressedPtr += 4; 448 | 449 | DCHECK((size_t)(compressedPtr - compressedOut) <= compressedOutLength); 450 | if(heapHashTable != NULL) { free(heapHashTable); heapHashTable = NULL; hashTable = NULL; } 451 | return(compressedPtr - compressedOut); 452 | } 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | static const uint32_t pithy_wordmask[] = { 461 | 0u, 0xffu, 0xffffu, 0xffffffu, 0xffffffffu 462 | }; 463 | 464 | static const uint16_t pithy_charTable[256] = { 465 | 0x0001, 0x0804, 0x1001, 0x1801, 0x0002, 0x0805, 0x1002, 0x1802, 466 | 0x0003, 0x0806, 0x1003, 0x1803, 0x0004, 0x0807, 0x1004, 0x1804, 467 | 0x0005, 0x0808, 0x1005, 0x1805, 0x0006, 0x0809, 0x1006, 0x1806, 468 | 0x0007, 0x080a, 0x1007, 0x1807, 0x0008, 0x080b, 0x1008, 0x1808, 469 | 0x0009, 0x0904, 0x1009, 0x1809, 0x000a, 0x0905, 0x100a, 0x180a, 470 | 0x000b, 0x0906, 0x100b, 0x180b, 0x000c, 0x0907, 0x100c, 0x180c, 471 | 0x000d, 0x0908, 0x100d, 0x180d, 0x000e, 0x0909, 0x100e, 0x180e, 472 | 0x000f, 0x090a, 0x100f, 0x180f, 0x0010, 0x090b, 0x1010, 0x1810, 473 | 0x0011, 0x0a04, 0x1011, 0x1811, 0x0012, 0x0a05, 0x1012, 0x1812, 474 | 0x0013, 0x0a06, 0x1013, 0x1813, 0x0014, 0x0a07, 0x1014, 0x1814, 475 | 0x0015, 0x0a08, 0x1015, 0x1815, 0x0016, 0x0a09, 0x1016, 0x1816, 476 | 0x0017, 0x0a0a, 0x1017, 0x1817, 0x0018, 0x0a0b, 0x1018, 0x1818, 477 | 0x0019, 0x0b04, 0x1019, 0x1819, 0x001a, 0x0b05, 0x101a, 0x181a, 478 | 0x001b, 0x0b06, 0x101b, 0x181b, 0x001c, 0x0b07, 0x101c, 0x181c, 479 | 0x001d, 0x0b08, 0x101d, 0x181d, 0x001e, 0x0b09, 0x101e, 0x181e, 480 | 0x001f, 0x0b0a, 0x101f, 0x181f, 0x0020, 0x0b0b, 0x1020, 0x1820, 481 | 0x0021, 0x0c04, 0x1021, 0x1821, 0x0022, 0x0c05, 0x1022, 0x1822, 482 | 0x0023, 0x0c06, 0x1023, 0x1823, 0x0024, 0x0c07, 0x1024, 0x1824, 483 | 0x0025, 0x0c08, 0x1025, 0x1825, 0x0026, 0x0c09, 0x1026, 0x1826, 484 | 0x0027, 0x0c0a, 0x1027, 0x1827, 0x0028, 0x0c0b, 0x1028, 0x1828, 485 | 0x0029, 0x0d04, 0x1029, 0x1829, 0x002a, 0x0d05, 0x102a, 0x182a, 486 | 0x002b, 0x0d06, 0x102b, 0x182b, 0x002c, 0x0d07, 0x102c, 0x182c, 487 | 0x002d, 0x0d08, 0x102d, 0x182d, 0x002e, 0x0d09, 0x102e, 0x182e, 488 | 0x002f, 0x0d0a, 0x102f, 0x182f, 0x0030, 0x0d0b, 0x1030, 0x1830, 489 | 0x0031, 0x0e04, 0x1031, 0x1831, 0x0032, 0x0e05, 0x1032, 0x1832, 490 | 0x0033, 0x0e06, 0x1033, 0x1833, 0x0034, 0x0e07, 0x1034, 0x1834, 491 | 0x0035, 0x0e08, 0x1035, 0x1835, 0x0036, 0x0e09, 0x1036, 0x1836, 492 | 0x0037, 0x0e0a, 0x1037, 0x1837, 0x0038, 0x0e0b, 0x1038, 0x1838, 493 | 0x0039, 0x0f04, 0x1039, 0x1839, 0x003a, 0x0f05, 0x103a, 0x183a, 494 | 0x003b, 0x0f06, 0x103b, 0x183b, 0x003c, 0x0f07, 0x103c, 0x183c, 495 | 0x0801, 0x0f08, 0x103d, 0x183d, 0x1001, 0x0f09, 0x103e, 0x183e, 496 | 0x1801, 0x0f0a, 0x103f, 0x183f, 0x2001, 0x0f0b, 0x1040, 0x1840 497 | }; 498 | 499 | 500 | int pithy_GetDecompressedLength(const char *compressed, size_t compressedLength, size_t *decompressedOutLengthResult) { 501 | const char *compressedEnd = compressed + compressedLength; 502 | size_t decompressedLength = 0ul; 503 | if(pithy_Parse32WithLimit(compressed, compressedEnd, &decompressedLength) != NULL) { *decompressedOutLengthResult = decompressedLength; return(1); } else { return(0); } 504 | } 505 | 506 | 507 | int pithy_Decompress(const char *compressed, size_t compressedLength, char *decompressedOut, size_t decompressedOutLength) { 508 | const char *nextCompressedPtr = NULL, *compressedEnd = (compressed + compressedLength); 509 | size_t parsedDecompressedLength = 0ul; 510 | if(((nextCompressedPtr = pithy_Parse32WithLimit(compressed, compressedEnd, &parsedDecompressedLength)) == NULL) || (parsedDecompressedLength > decompressedOutLength)) { return(0); } 511 | 512 | char *decompressedPtr = decompressedOut, *decompressedEnd = decompressedOut + parsedDecompressedLength; 513 | 514 | while(1) { 515 | const char *compressedPtr = nextCompressedPtr; 516 | DCHECK(compressedPtr <= compressedEnd); 517 | if(PITHY_EXPECT_F(compressedPtr >= compressedEnd)) { break; } 518 | 519 | const unsigned char c = *((const unsigned char *)(compressedPtr++)); 520 | const unsigned char cLowerBits = (c & 0x3u); 521 | const ssize_t spaceLeft = (decompressedEnd - decompressedPtr); 522 | 523 | if((cLowerBits == PITHY_LITERAL)) { 524 | size_t literalLength = (c >> 2) + 1; 525 | if(PITHY_EXPECT_T(literalLength <= 16ul) && PITHY_EXPECT_T((compressedEnd - compressedPtr) >= 16l) && PITHY_EXPECT_T(spaceLeft >= 16l)) { pithy_Move128(decompressedPtr, compressedPtr); } 526 | else { 527 | if(PITHY_EXPECT_F(literalLength > 60)) { 528 | if(PITHY_EXPECT_F((compressedPtr + 4) > compressedEnd)) { break; } 529 | size_t literalLengthBytes = literalLength - 60; 530 | literalLength = (pithy_LoadHost32(compressedPtr) & pithy_wordmask[literalLengthBytes]) + 1; 531 | compressedPtr += literalLengthBytes; 532 | } 533 | if(PITHY_EXPECT_F(spaceLeft < (ssize_t)literalLength) || PITHY_EXPECT_F((compressedPtr + literalLength) > compressedEnd)) { break; } 534 | memcpy(decompressedPtr, compressedPtr, literalLength); 535 | } 536 | nextCompressedPtr = compressedPtr + literalLength; 537 | decompressedPtr += literalLength; 538 | } else { 539 | const uint32_t entry = pithy_charTable[c]; 540 | const size_t trailer = pithy_LoadHost32(compressedPtr) & pithy_wordmask[cLowerBits]; 541 | size_t length = entry & 0xffu; 542 | const size_t copyOffset = ((entry & 0x700u) + trailer); 543 | 544 | compressedPtr += cLowerBits; 545 | 546 | DCHECK((compressedPtr <= compressedEnd) && (copyOffset > 0ul) && (spaceLeft > 0l) && (length > 0ul)); 547 | 548 | if(PITHY_EXPECT_F((decompressedPtr - decompressedOut) <= ((ssize_t)copyOffset - 1l))) { break; } 549 | if(PITHY_EXPECT_T(length <= 16ul) && PITHY_EXPECT_T(copyOffset >= 16ul) && PITHY_EXPECT_T(spaceLeft >= 16l)) { pithy_Move128(decompressedPtr, decompressedPtr - copyOffset); } 550 | else { 551 | if(PITHY_EXPECT_F(length >= 63ul)) { if(PITHY_EXPECT_T(length == 63ul)) { if(PITHY_EXPECT_F((compressedPtr + 1) > compressedEnd)) { break; } length = (*((unsigned char *)compressedPtr++)) + 63ul; } 552 | else { if(PITHY_EXPECT_F((compressedPtr + 2) > compressedEnd)) { break; } length = pithy_LoadHost16(compressedPtr); compressedPtr += 2ul; } } 553 | 554 | char *copyFrom = decompressedPtr - copyOffset, *copyTo = decompressedPtr; 555 | ssize_t copyLength = (ssize_t)length; 556 | 557 | if(PITHY_EXPECT_F(copyLength > 256l) && PITHY_EXPECT_T(copyOffset > (size_t)copyLength)) { if(PITHY_EXPECT_F(spaceLeft < copyLength)) { break; } memcpy(copyTo, copyFrom, copyLength); } 558 | else { 559 | if(PITHY_EXPECT_T(spaceLeft >= (copyLength + 24)) && PITHY_EXPECT_T(copyLength > 0l)) { 560 | while((copyTo - copyFrom) < 16l) { pithy_Move128(copyTo, copyFrom); copyLength -= copyTo - copyFrom; copyTo += copyTo - copyFrom; } 561 | while(copyLength > 0l) { pithy_Move128(copyTo, copyFrom); copyFrom += 16l; copyTo += 16l; copyLength -= 16l; } 562 | } else { if(PITHY_EXPECT_F(spaceLeft < copyLength) || PITHY_EXPECT_F(copyLength <= 0l)) { break; } do { *copyTo++ = *copyFrom++; } while(--copyLength > 0l); } 563 | } 564 | } 565 | nextCompressedPtr = compressedPtr; 566 | decompressedPtr += length; 567 | } 568 | } 569 | 570 | return(decompressedPtr == decompressedEnd); 571 | } 572 | -------------------------------------------------------------------------------- /pithy.h: -------------------------------------------------------------------------------- 1 | // 2 | // pithy.h 3 | // http://github.com/johnezang/pithy 4 | // Licensed under the terms of the BSD License, as specified below. 5 | // 6 | 7 | /* 8 | Copyright (c) 2011, John Engelhart 9 | 10 | All rights reserved. 11 | 12 | Redistribution and use in source and binary forms, with or without 13 | modification, are permitted provided that the following conditions are met: 14 | 15 | * Redistributions of source code must retain the above copyright 16 | notice, this list of conditions and the following disclaimer. 17 | 18 | * Redistributions in binary form must reproduce the above copyright 19 | notice, this list of conditions and the following disclaimer in the 20 | documentation and/or other materials provided with the distribution. 21 | 22 | * Neither the name of the Zang Industries nor the names of its 23 | contributors may be used to endorse or promote products derived from 24 | this software without specific prior written permission. 25 | 26 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 27 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 28 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 29 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 30 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 31 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 32 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 33 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 34 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 35 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 36 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 37 | */ 38 | 39 | #include 40 | #include 41 | 42 | #ifdef __cplusplus 43 | extern "C" { 44 | #endif 45 | 46 | #ifndef _PITHY_H_ 47 | #define _PITHY_H_ 48 | 49 | // compressionLevel >= 0 && compressionLevel <= 9. Values out side this range will be clamped to this range. 50 | size_t pithy_Compress (const char *uncompressed, size_t uncompressedLength, char *compressedOut, size_t compressedOutLength, int compressionLevel); 51 | int pithy_Decompress(const char *compressed, size_t compressedLength, char *decompressedOut, size_t decompressedOutLength); 52 | 53 | size_t pithy_MaxCompressedLength(size_t inputLength); 54 | int pithy_GetDecompressedLength(const char *compressed, size_t compressedLength, size_t *decompressedOutLengthResult); 55 | 56 | #endif // _PITHY_H_ 57 | 58 | #ifdef __cplusplus 59 | } // extern "C" 60 | #endif 61 | --------------------------------------------------------------------------------