├── .appveyor.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── nimsnappyc.nim ├── nimsnappyc.nimble ├── nimsnappyc ├── private │ ├── LICENSE │ ├── README.md │ ├── compat.h │ ├── snappy.c │ └── snappy.h └── snappyc.nim └── tests ├── filecompress.nim ├── filecompress.nim.cfg ├── test.nim ├── test.nim.cfg ├── test2.nim └── test2.nim.cfg /.appveyor.yml: -------------------------------------------------------------------------------- 1 | version: "{build}" 2 | 3 | deploy: off 4 | 5 | matrix: 6 | fast_finish: true 7 | 8 | environment: 9 | matrix: 10 | # - MINGW_PATH: C:\mingw-w64\i686-6.3.0-posix-dwarf-rt_v5-rev1\mingw32 11 | # platform: x32 12 | 13 | - MINGW_PATH: C:\mingw-w64\x86_64-7.3.0-posix-seh-rt_v5-rev0\mingw64 14 | platform: x64 15 | 16 | install: 17 | - setlocal EnableExtensions EnableDelayedExpansion 18 | - set path=%MINGW_PATH%\bin;%CD%\Nim\bin;%PATH% 19 | - gcc --version 20 | - git clone --depth 1 https://github.com/nim-lang/Nim.git 21 | - cd Nim 22 | - git clone --depth 1 https://github.com/nim-lang/csources 23 | - cd csources 24 | - if %PLATFORM% == x64 ( build64.bat ) else ( build.bat ) 25 | - cd .. 26 | - bin\nim c koch 27 | - koch boot -d:release 28 | 29 | build_script: 30 | - cd C:\projects\%APPVEYOR_PROJECT_SLUG% 31 | - nim c tests\test 32 | 33 | test_script: 34 | - cd C:\projects\%APPVEYOR_PROJECT_SLUG% 35 | - tests\test 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | nimcache/ 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | notifications: 2 | email: false 3 | 4 | sudo: required 5 | language: c 6 | 7 | git: 8 | depth: 1 9 | 10 | addons: 11 | apt: 12 | config: 13 | retries: true 14 | sources: 15 | - ubuntu-toolchain-r-test 16 | - llvm-toolchain-trusty-6.0 17 | packages: 18 | - gcc-8 19 | - clang-6.0 20 | - g++-8 21 | - libstdc++-8-dev 22 | - gcc-multilib 23 | - g++-multilib 24 | - gcc-8-multilib 25 | - g++-8-multilib 26 | matrix: 27 | include: 28 | - os: linux 29 | compiler: gcc 30 | dist: trusty 31 | 32 | - os: linux 33 | compiler: clang 34 | dist: trusty 35 | 36 | - os: osx 37 | compiler: gcc 38 | 39 | - os: osx 40 | compiler: clang 41 | 42 | install: 43 | - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew update > /dev/null; fi 44 | 45 | before_script: 46 | - eval "${TOOLCHAIN_EVAL}" 47 | - git clone -b devel --depth 1 git://github.com/nim-lang/Nim.git 48 | - cd Nim 49 | - git clone --depth 1 git://github.com/nim-lang/csources 50 | - cd csources && sh build.sh 51 | - cd .. 52 | - bin/nim c koch 53 | - ./koch boot -d:release 54 | - export PATH=$PWD/bin:$PATH 55 | - cd .. 56 | 57 | script: 58 | - nim c --run tests/test 59 | - nim --cpu:i386 --passC:-m32 --passL:-m32 c --run tests/test 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Dmitry Atamanov 4 | Copyright (c) 2018 NimCompression's members and contributors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/NimCompression/nimsnappyc.svg?branch=master)](https://travis-ci.org/NimCompression/nimsnappyc) 2 | [![Build status](https://ci.appveyor.com/api/projects/status/8veiod4jdp1fa81a?svg=true)](https://ci.appveyor.com/project/data-man/nimsnappyc) 3 | 4 | # NimSnappyC 5 | 6 | **NimSnappyC** provides a Snappy compression/decompression for [Nim](https://nim-lang.org) through wrapping to [snappy-c](https://github.com/andikleen/snappy-c). 7 | 8 | **[GitHub Repo](https://github.com/NimCompression/nimsnappyc)** 9 | 10 | ## Installation 11 | 12 | Add this to your application's .nimble file: 13 | 14 | ```nim 15 | requires "nimsnappyc" 16 | ``` 17 | 18 | or to install globally run: 19 | 20 | ``` 21 | nimble install nimsnappyc 22 | ``` 23 | 24 | ## Usage 25 | 26 | ```nim 27 | import nimsnappyc 28 | 29 | # Compress a string: 30 | let data = "0123456789abcdef" 31 | let compressed = snappyCompress(data) 32 | 33 | # Get the uncompressed length of compressed data: 34 | let uncompressedLen = snappyUncompressedLength(compressed) 35 | 36 | # Uncompress a compressed data: 37 | let uncompressed = snappyUncompress(compressed) 38 | ``` 39 | 40 | Raises the ```SnappyError``` exception on errors. 41 | 42 | ## Contributing 43 | 44 | 1. Fork it (https://github.com/NimCompression/nimsnappyc/fork) 45 | 2. Create your feature branch (`git checkout -b my-new-feature`) 46 | 3. Commit your changes (`git commit -am 'Add some feature'`) 47 | 4. Push to the branch (`git push origin my-new-feature`) 48 | 5. Create a new Pull Request 49 | 50 | ## License 51 | MIT 52 | 53 | Authors: [NimCompression](https://github.com/NimCompression) 54 | 55 | ## Snappy-C License 56 | BSD 3-Clause License 57 | 58 | Author: [Andi Kleen](https://github.com/andikleen) -------------------------------------------------------------------------------- /nimsnappyc.nim: -------------------------------------------------------------------------------- 1 | import nimsnappyc/snappyc 2 | 3 | type 4 | SnappyError* = object of Exception 5 | 6 | proc snappyMaxCompressedLength*(inputLen: int): int = 7 | snappyc.snappy_max_compressed_length(inputLen).int 8 | 9 | proc snappyUncompressedLength*(input: string | seq[byte] | openarray[byte]): uint = 10 | var 11 | inputAddr = input[0].unsafeAddr 12 | inputLen = input.len 13 | uncompressedLength: csize 14 | 15 | let res = snappy_uncompressed_length(inputAddr, inputLen, uncompressedLength.addr) 16 | if not res: raise newException(SnappyError, "Uncompressed length error") 17 | 18 | result = uncompressedLength.uint 19 | 20 | template snappyCompressImpl = 21 | var 22 | env: SnappyEnv 23 | compressedLength: csize 24 | 25 | if snappy_init_env(env.addr) != 0: raise newException(SnappyError, "Env's init error") 26 | 27 | let res = snappy_compress(env.addr, inputAddr, inputLen, 28 | result[0].addr, compressedLength.addr) 29 | if res != 0: raise newException(SnappyError, "Compress error") 30 | snappy_free_env(env.addr) 31 | result.setLen(compressedLength) 32 | 33 | proc snappyCompress*(inputAddr: pointer, inputLen: int): seq[byte] = 34 | if inputLen == 0: 35 | return newSeq[byte]() 36 | else: 37 | let maxLen = snappyc.snappy_max_compressed_length(inputLen).int 38 | 39 | result = newSeqUninitialized[byte](maxLen) 40 | snappyCompressImpl 41 | 42 | proc snappyCompress*(input: string): string = 43 | let inputLen = input.len 44 | 45 | if inputLen == 0: 46 | return "" 47 | else: 48 | let inputAddr = input[0].unsafeAddr 49 | let maxLen = snappyc.snappy_max_compressed_length(inputLen).int 50 | 51 | result = newString(maxLen) 52 | snappyCompressImpl 53 | 54 | proc snappyCompress*[T](input: T): seq[byte] = 55 | var 56 | inputAddr: pointer 57 | inputLen: int 58 | 59 | when T is string | seq | openarray: 60 | if input.len == 0: 61 | return newSeq[byte]() 62 | 63 | inputAddr = input[0].unsafeAddr 64 | inputLen = input[0].sizeof * input.len 65 | else: 66 | inputAddr = input.unsafeAddr 67 | inputLen = input.sizeof 68 | 69 | let maxLen = snappyc.snappy_max_compressed_length(inputLen).int 70 | 71 | result = newSeqUninitialized[byte](maxLen) 72 | snappyCompressImpl 73 | 74 | template snappyUncompressImpl = 75 | var 76 | uncompressedLength: csize 77 | let res = snappy_uncompressed_length(inputAddr, inputLen, uncompressedLength.addr) 78 | if not res: raise newException(SnappyError, "Uncompress error") 79 | 80 | let uncompressRes = snappy_uncompress(inputAddr, inputLen, result[0].addr) 81 | if uncompressRes != 0: raise newException(SnappyError, "Uncompress error") 82 | 83 | proc snappyUncompress*(input: seq[byte] | openarray[byte]): seq[byte] = 84 | if input.len == 0: 85 | return newSeq[byte]() 86 | 87 | let 88 | inputAddr = input[0].unsafeAddr 89 | inputLen = input.len 90 | 91 | result = newSeqUninitialized[byte](snappyUncompressedLength(input)) 92 | snappyUncompressImpl 93 | 94 | proc snappyUncompress*(input: string): string = 95 | if input.len == 0: 96 | return "" 97 | 98 | let 99 | inputAddr = input[0].unsafeAddr 100 | inputLen = input.len 101 | 102 | result = newString(snappyUncompressedLength(input)) 103 | snappyUncompressImpl 104 | -------------------------------------------------------------------------------- /nimsnappyc.nimble: -------------------------------------------------------------------------------- 1 | packageName = "nimsnappyc" 2 | version = "0.0.4" 3 | author = "NimCompression's members and conributors" 4 | description = "Snappy-C lib wrapper for Nim" 5 | license = "MIT" 6 | skipDirs = @["tests"] 7 | 8 | requires "nim >= 0.18.0" 9 | -------------------------------------------------------------------------------- /nimsnappyc/private/LICENSE: -------------------------------------------------------------------------------- 1 | The snappy-c code is under the same license as the original snappy source 2 | 3 | Copyright 2011 Intel Corporation All Rights Reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following disclaimer 13 | in the documentation and/or other materials provided with the 14 | distribution. 15 | * Neither the name of Intel Corporation nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | -------------------------------------------------------------------------------- /nimsnappyc/private/README.md: -------------------------------------------------------------------------------- 1 | ![snappy-c](http://halobates.de/snappy-c.png) 2 | 3 | This is a C port of the google snappy compressor (http://code.google.com/p/snappy/) 4 | The compressor is very fast with reasonable compression ratio. 5 | It is mainly useful for projects that cannot integrate C++ code, but want snappy. 6 | Also contains a command line tool, a benchmark, random test code and a fuzz tester. 7 | 8 | The compression code supports scather-gather and linear buffers. The scather 9 | gather code is ifdefed (-DSG) and can be removed with unifdef. 10 | 11 | API documentation: http://halobates.de/snappy.html 12 | 13 | To generate the documentation run 14 | 15 | make html 16 | 17 | Requires the kerneldoc script from a Linux kernel source (may need to point 18 | the makefile to it). If you don't have the kernel source lying around 19 | just download it from 20 | https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/plain/scripts/kernel-doc 21 | and point the Makefile to it. 22 | 23 | 24 | 25 | 26 | Andi Kleen 27 | -------------------------------------------------------------------------------- /nimsnappyc/private/compat.h: -------------------------------------------------------------------------------- 1 | #ifdef __FreeBSD__ 2 | # include 3 | #elif defined(__APPLE_CC_) || defined(__MACH__) /* MacOS/X support */ 4 | # include 5 | 6 | #if __DARWIN_BYTE_ORDER == __DARWIN_LITTLE_ENDIAN 7 | # define htole16(x) (x) 8 | # define le32toh(x) (x) 9 | #elif __DARWIN_BYTE_ORDER == __DARWIN_BIG_ENDIAN 10 | # define htole16(x) __DARWIN_OSSwapInt16(x) 11 | # define le32toh(x) __DARWIN_OSSwapInt32(x) 12 | #else 13 | # error "Endianness is undefined" 14 | #endif 15 | 16 | 17 | #elif !defined(__WIN32__) 18 | # include 19 | #endif 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #ifndef __WIN32__ 28 | #include 29 | #endif 30 | 31 | #ifdef __ANDROID__ 32 | #define le32toh letoh32 33 | #endif 34 | 35 | #if defined(__WIN32__) && defined(SG) 36 | struct iovec { 37 | void *iov_base; /* Pointer to data. */ 38 | size_t iov_len; /* Length of data. */ 39 | }; 40 | #endif 41 | 42 | #define get_unaligned_memcpy(x) ({ \ 43 | typeof(*(x)) _ret; \ 44 | memcpy(&_ret, (x), sizeof(*(x))); \ 45 | _ret; }) 46 | #define put_unaligned_memcpy(v,x) ({ \ 47 | typeof((v)) _v = (v); \ 48 | memcpy((x), &_v, sizeof(*(x))); }) 49 | 50 | #define get_unaligned_direct(x) (*(x)) 51 | #define put_unaligned_direct(v,x) (*(x) = (v)) 52 | 53 | // Potentially unaligned loads and stores. 54 | // x86 and PowerPC can simply do these loads and stores native. 55 | #if defined(__i386__) || defined(__x86_64__) || defined(__powerpc__) 56 | 57 | #define get_unaligned get_unaligned_direct 58 | #define put_unaligned put_unaligned_direct 59 | #define get_unaligned64 get_unaligned_direct 60 | #define put_unaligned64 put_unaligned_direct 61 | 62 | // ARMv7 and newer support native unaligned accesses, but only of 16-bit 63 | // and 32-bit values (not 64-bit); older versions either raise a fatal signal, 64 | // do an unaligned read and rotate the words around a bit, or do the reads very 65 | // slowly (trip through kernel mode). There's no simple #define that says just 66 | // “ARMv7 or higher”, so we have to filter away all ARMv5 and ARMv6 67 | // sub-architectures. 68 | // 69 | // This is a mess, but there's not much we can do about it. 70 | #elif defined(__arm__) && \ 71 | !defined(__ARM_ARCH_4__) && \ 72 | !defined(__ARM_ARCH_4T__) && \ 73 | !defined(__ARM_ARCH_5__) && \ 74 | !defined(__ARM_ARCH_5T__) && \ 75 | !defined(__ARM_ARCH_5TE__) && \ 76 | !defined(__ARM_ARCH_5TEJ__) && \ 77 | !defined(__ARM_ARCH_6__) && \ 78 | !defined(__ARM_ARCH_6J__) && \ 79 | !defined(__ARM_ARCH_6K__) && \ 80 | !defined(__ARM_ARCH_6Z__) && \ 81 | !defined(__ARM_ARCH_6ZK__) && \ 82 | !defined(__ARM_ARCH_6T2__) 83 | 84 | #define get_unaligned get_unaligned_direct 85 | #define put_unaligned put_unaligned_direct 86 | #define get_unaligned64 get_unaligned_memcpy 87 | #define put_unaligned64 put_unaligned_memcpy 88 | 89 | // These macroses are provided for architectures that don't support 90 | // unaligned loads and stores. 91 | #else 92 | 93 | #define get_unaligned get_unaligned_memcpy 94 | #define put_unaligned put_unaligned_memcpy 95 | #define get_unaligned64 get_unaligned_memcpy 96 | #define put_unaligned64 put_unaligned_memcpy 97 | 98 | #endif 99 | 100 | #define get_unaligned_le32(x) (le32toh(get_unaligned((u32 *)(x)))) 101 | #define put_unaligned_le16(v,x) (put_unaligned(htole16(v), (u16 *)(x))) 102 | 103 | typedef unsigned char u8; 104 | typedef unsigned short u16; 105 | typedef unsigned u32; 106 | typedef unsigned long long u64; 107 | 108 | #define BUG_ON(x) assert(!(x)) 109 | 110 | #define vmalloc(x) malloc(x) 111 | #define vfree(x) free(x) 112 | 113 | #define EXPORT_SYMBOL(x) 114 | 115 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) 116 | 117 | #define likely(x) __builtin_expect((x), 1) 118 | #define unlikely(x) __builtin_expect((x), 0) 119 | 120 | #define min_t(t,x,y) ((x) < (y) ? (x) : (y)) 121 | #define max_t(t,x,y) ((x) > (y) ? (x) : (y)) 122 | 123 | #if __BYTE_ORDER == __LITTLE_ENDIAN 124 | #define __LITTLE_ENDIAN__ 1 125 | #endif 126 | 127 | #if __LITTLE_ENDIAN__ == 1 && (defined(__LSB_VERSION__) || defined(__WIN32__)) 128 | #define htole16(x) (x) 129 | #define le32toh(x) (x) 130 | #endif 131 | 132 | #define BITS_PER_LONG (__SIZEOF_LONG__ * 8) 133 | -------------------------------------------------------------------------------- /nimsnappyc/private/snappy.c: -------------------------------------------------------------------------------- 1 | /* 2 | * C port of the snappy compressor from Google. 3 | * This is a very fast compressor with comparable compression to lzo. 4 | * Works best on 64bit little-endian, but should be good on others too. 5 | * Ported by Andi Kleen. 6 | * Uptodate with snappy 1.1.0 7 | */ 8 | 9 | /* 10 | * Copyright 2005 Google Inc. 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 14 | * met: 15 | * 16 | * * Redistributions of source code must retain the above copyright 17 | * notice, this list of conditions and the following disclaimer. 18 | * * Redistributions in binary form must reproduce the above 19 | * copyright notice, this list of conditions and the following disclaimer 20 | * in the documentation and/or other materials provided with the 21 | * distribution. 22 | * * Neither the name of Google Inc. 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 32 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 33 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 34 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 35 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 36 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 37 | */ 38 | 39 | #ifdef __KERNEL__ 40 | #include 41 | #ifdef SG 42 | #include 43 | #endif 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #else 51 | #include "snappy.h" 52 | #include "compat.h" 53 | #endif 54 | 55 | #define CRASH_UNLESS(x) BUG_ON(!(x)) 56 | #define CHECK(cond) CRASH_UNLESS(cond) 57 | #define CHECK_LE(a, b) CRASH_UNLESS((a) <= (b)) 58 | #define CHECK_GE(a, b) CRASH_UNLESS((a) >= (b)) 59 | #define CHECK_EQ(a, b) CRASH_UNLESS((a) == (b)) 60 | #define CHECK_NE(a, b) CRASH_UNLESS((a) != (b)) 61 | #define CHECK_LT(a, b) CRASH_UNLESS((a) < (b)) 62 | #define CHECK_GT(a, b) CRASH_UNLESS((a) > (b)) 63 | 64 | #define UNALIGNED_LOAD16(_p) get_unaligned((u16 *)(_p)) 65 | #define UNALIGNED_LOAD32(_p) get_unaligned((u32 *)(_p)) 66 | #define UNALIGNED_LOAD64(_p) get_unaligned64((u64 *)(_p)) 67 | 68 | #define UNALIGNED_STORE16(_p, _val) put_unaligned(_val, (u16 *)(_p)) 69 | #define UNALIGNED_STORE32(_p, _val) put_unaligned(_val, (u32 *)(_p)) 70 | #define UNALIGNED_STORE64(_p, _val) put_unaligned64(_val, (u64 *)(_p)) 71 | 72 | /* 73 | * This can be more efficient than UNALIGNED_LOAD64 + UNALIGNED_STORE64 74 | * on some platforms, in particular ARM. 75 | */ 76 | static inline void unaligned_copy64(const void *src, void *dst) 77 | { 78 | if (sizeof(void *) == 8) { 79 | UNALIGNED_STORE64(dst, UNALIGNED_LOAD64(src)); 80 | } else { 81 | const char *src_char = (const char *)(src); 82 | char *dst_char = (char *)(dst); 83 | 84 | UNALIGNED_STORE32(dst_char, UNALIGNED_LOAD32(src_char)); 85 | UNALIGNED_STORE32(dst_char + 4, UNALIGNED_LOAD32(src_char + 4)); 86 | } 87 | } 88 | 89 | #ifdef NDEBUG 90 | 91 | #define DCHECK(cond) do {} while(0) 92 | #define DCHECK_LE(a, b) do {} while(0) 93 | #define DCHECK_GE(a, b) do {} while(0) 94 | #define DCHECK_EQ(a, b) do {} while(0) 95 | #define DCHECK_NE(a, b) do {} while(0) 96 | #define DCHECK_LT(a, b) do {} while(0) 97 | #define DCHECK_GT(a, b) do {} while(0) 98 | 99 | #else 100 | 101 | #define DCHECK(cond) CHECK(cond) 102 | #define DCHECK_LE(a, b) CHECK_LE(a, b) 103 | #define DCHECK_GE(a, b) CHECK_GE(a, b) 104 | #define DCHECK_EQ(a, b) CHECK_EQ(a, b) 105 | #define DCHECK_NE(a, b) CHECK_NE(a, b) 106 | #define DCHECK_LT(a, b) CHECK_LT(a, b) 107 | #define DCHECK_GT(a, b) CHECK_GT(a, b) 108 | 109 | #endif 110 | 111 | static inline bool is_little_endian(void) 112 | { 113 | #ifdef __LITTLE_ENDIAN__ 114 | return true; 115 | #endif 116 | return false; 117 | } 118 | 119 | static inline int log2_floor(u32 n) 120 | { 121 | return n == 0 ? -1 : 31 ^ __builtin_clz(n); 122 | } 123 | 124 | static inline int find_lsb_set_non_zero(u32 n) 125 | { 126 | return __builtin_ctz(n); 127 | } 128 | 129 | static inline int find_lsb_set_non_zero64(u64 n) 130 | { 131 | return __builtin_ctzll(n); 132 | } 133 | 134 | #define kmax32 5 135 | 136 | /* 137 | * Attempts to parse a varint32 from a prefix of the bytes in [ptr,limit-1]. 138 | * Never reads a character at or beyond limit. If a valid/terminated varint32 139 | * was found in the range, stores it in *OUTPUT and returns a pointer just 140 | * past the last byte of the varint32. Else returns NULL. On success, 141 | * "result <= limit". 142 | */ 143 | static inline const char *varint_parse32_with_limit(const char *p, 144 | const char *l, 145 | u32 * OUTPUT) 146 | { 147 | const unsigned char *ptr = (const unsigned char *)(p); 148 | const unsigned char *limit = (const unsigned char *)(l); 149 | u32 b, result; 150 | 151 | if (ptr >= limit) 152 | return NULL; 153 | b = *(ptr++); 154 | result = b & 127; 155 | if (b < 128) 156 | goto done; 157 | if (ptr >= limit) 158 | return NULL; 159 | b = *(ptr++); 160 | result |= (b & 127) << 7; 161 | if (b < 128) 162 | goto done; 163 | if (ptr >= limit) 164 | return NULL; 165 | b = *(ptr++); 166 | result |= (b & 127) << 14; 167 | if (b < 128) 168 | goto done; 169 | if (ptr >= limit) 170 | return NULL; 171 | b = *(ptr++); 172 | result |= (b & 127) << 21; 173 | if (b < 128) 174 | goto done; 175 | if (ptr >= limit) 176 | return NULL; 177 | b = *(ptr++); 178 | result |= (b & 127) << 28; 179 | if (b < 16) 180 | goto done; 181 | return NULL; /* Value is too long to be a varint32 */ 182 | done: 183 | *OUTPUT = result; 184 | return (const char *)(ptr); 185 | } 186 | 187 | /* 188 | * REQUIRES "ptr" points to a buffer of length sufficient to hold "v". 189 | * EFFECTS Encodes "v" into "ptr" and returns a pointer to the 190 | * byte just past the last encoded byte. 191 | */ 192 | static inline char *varint_encode32(char *sptr, u32 v) 193 | { 194 | /* Operate on characters as unsigneds */ 195 | unsigned char *ptr = (unsigned char *)(sptr); 196 | static const int B = 128; 197 | 198 | if (v < (1 << 7)) { 199 | *(ptr++) = v; 200 | } else if (v < (1 << 14)) { 201 | *(ptr++) = v | B; 202 | *(ptr++) = v >> 7; 203 | } else if (v < (1 << 21)) { 204 | *(ptr++) = v | B; 205 | *(ptr++) = (v >> 7) | B; 206 | *(ptr++) = v >> 14; 207 | } else if (v < (1 << 28)) { 208 | *(ptr++) = v | B; 209 | *(ptr++) = (v >> 7) | B; 210 | *(ptr++) = (v >> 14) | B; 211 | *(ptr++) = v >> 21; 212 | } else { 213 | *(ptr++) = v | B; 214 | *(ptr++) = (v >> 7) | B; 215 | *(ptr++) = (v >> 14) | B; 216 | *(ptr++) = (v >> 21) | B; 217 | *(ptr++) = v >> 28; 218 | } 219 | return (char *)(ptr); 220 | } 221 | 222 | #ifdef SG 223 | 224 | static inline void *n_bytes_after_addr(void *addr, size_t n_bytes) 225 | { 226 | return (void *) ((char *)addr + n_bytes); 227 | } 228 | 229 | struct source { 230 | struct iovec *iov; 231 | int iovlen; 232 | int curvec; 233 | int curoff; 234 | size_t total; 235 | }; 236 | 237 | /* Only valid at beginning when nothing is consumed */ 238 | static inline int available(struct source *s) 239 | { 240 | return s->total; 241 | } 242 | 243 | static inline const char *peek(struct source *s, size_t *len) 244 | { 245 | if (likely(s->curvec < s->iovlen)) { 246 | struct iovec *iv = &s->iov[s->curvec]; 247 | if (s->curoff < iv->iov_len) { 248 | *len = iv->iov_len - s->curoff; 249 | return n_bytes_after_addr(iv->iov_base, s->curoff); 250 | } 251 | } 252 | *len = 0; 253 | return NULL; 254 | } 255 | 256 | static inline void skip(struct source *s, size_t n) 257 | { 258 | struct iovec *iv = &s->iov[s->curvec]; 259 | s->curoff += n; 260 | DCHECK_LE(s->curoff, iv->iov_len); 261 | if (s->curoff >= iv->iov_len && s->curvec + 1 < s->iovlen) { 262 | s->curoff = 0; 263 | s->curvec++; 264 | } 265 | } 266 | 267 | struct sink { 268 | struct iovec *iov; 269 | int iovlen; 270 | unsigned curvec; 271 | unsigned curoff; 272 | unsigned written; 273 | }; 274 | 275 | static inline void append(struct sink *s, const char *data, size_t n) 276 | { 277 | struct iovec *iov = &s->iov[s->curvec]; 278 | char *dst = n_bytes_after_addr(iov->iov_base, s->curoff); 279 | size_t nlen = min_t(size_t, iov->iov_len - s->curoff, n); 280 | if (data != dst) 281 | memcpy(dst, data, nlen); 282 | s->written += n; 283 | s->curoff += nlen; 284 | while ((n -= nlen) > 0) { 285 | data += nlen; 286 | s->curvec++; 287 | DCHECK_LT(s->curvec, s->iovlen); 288 | iov++; 289 | nlen = min_t(size_t, iov->iov_len, n); 290 | memcpy(iov->iov_base, data, nlen); 291 | s->curoff = nlen; 292 | } 293 | } 294 | 295 | static inline void *sink_peek(struct sink *s, size_t n) 296 | { 297 | struct iovec *iov = &s->iov[s->curvec]; 298 | if (s->curvec < iov->iov_len && iov->iov_len - s->curoff >= n) 299 | return n_bytes_after_addr(iov->iov_base, s->curoff); 300 | return NULL; 301 | } 302 | 303 | #else 304 | 305 | struct source { 306 | const char *ptr; 307 | size_t left; 308 | }; 309 | 310 | static inline int available(struct source *s) 311 | { 312 | return s->left; 313 | } 314 | 315 | static inline const char *peek(struct source *s, size_t * len) 316 | { 317 | *len = s->left; 318 | return s->ptr; 319 | } 320 | 321 | static inline void skip(struct source *s, size_t n) 322 | { 323 | s->left -= n; 324 | s->ptr += n; 325 | } 326 | 327 | struct sink { 328 | char *dest; 329 | }; 330 | 331 | static inline void append(struct sink *s, const char *data, size_t n) 332 | { 333 | if (data != s->dest) 334 | memcpy(s->dest, data, n); 335 | s->dest += n; 336 | } 337 | 338 | #define sink_peek(s, n) sink_peek_no_sg(s) 339 | 340 | static inline void *sink_peek_no_sg(const struct sink *s) 341 | { 342 | return s->dest; 343 | } 344 | 345 | #endif 346 | 347 | struct writer { 348 | char *base; 349 | char *op; 350 | char *op_limit; 351 | }; 352 | 353 | /* Called before decompression */ 354 | static inline void writer_set_expected_length(struct writer *w, size_t len) 355 | { 356 | w->op_limit = w->op + len; 357 | } 358 | 359 | /* Called after decompression */ 360 | static inline bool writer_check_length(struct writer *w) 361 | { 362 | return w->op == w->op_limit; 363 | } 364 | 365 | /* 366 | * Copy "len" bytes from "src" to "op", one byte at a time. Used for 367 | * handling COPY operations where the input and output regions may 368 | * overlap. For example, suppose: 369 | * src == "ab" 370 | * op == src + 2 371 | * len == 20 372 | * After IncrementalCopy(src, op, len), the result will have 373 | * eleven copies of "ab" 374 | * ababababababababababab 375 | * Note that this does not match the semantics of either memcpy() 376 | * or memmove(). 377 | */ 378 | static inline void incremental_copy(const char *src, char *op, ssize_t len) 379 | { 380 | DCHECK_GT(len, 0); 381 | do { 382 | *op++ = *src++; 383 | } while (--len > 0); 384 | } 385 | 386 | /* 387 | * Equivalent to IncrementalCopy except that it can write up to ten extra 388 | * bytes after the end of the copy, and that it is faster. 389 | * 390 | * The main part of this loop is a simple copy of eight bytes at a time until 391 | * we've copied (at least) the requested amount of bytes. However, if op and 392 | * src are less than eight bytes apart (indicating a repeating pattern of 393 | * length < 8), we first need to expand the pattern in order to get the correct 394 | * results. For instance, if the buffer looks like this, with the eight-byte 395 | * and patterns marked as intervals: 396 | * 397 | * abxxxxxxxxxxxx 398 | * [------] src 399 | * [------] op 400 | * 401 | * a single eight-byte copy from to will repeat the pattern once, 402 | * after which we can move two bytes without moving : 403 | * 404 | * ababxxxxxxxxxx 405 | * [------] src 406 | * [------] op 407 | * 408 | * and repeat the exercise until the two no longer overlap. 409 | * 410 | * This allows us to do very well in the special case of one single byte 411 | * repeated many times, without taking a big hit for more general cases. 412 | * 413 | * The worst case of extra writing past the end of the match occurs when 414 | * op - src == 1 and len == 1; the last copy will read from byte positions 415 | * [0..7] and write to [4..11], whereas it was only supposed to write to 416 | * position 1. Thus, ten excess bytes. 417 | */ 418 | 419 | #define kmax_increment_copy_overflow 10 420 | 421 | static inline void incremental_copy_fast_path(const char *src, char *op, 422 | ssize_t len) 423 | { 424 | while (op - src < 8) { 425 | unaligned_copy64(src, op); 426 | len -= op - src; 427 | op += op - src; 428 | } 429 | while (len > 0) { 430 | unaligned_copy64(src, op); 431 | src += 8; 432 | op += 8; 433 | len -= 8; 434 | } 435 | } 436 | 437 | static inline bool writer_append_from_self(struct writer *w, u32 offset, 438 | u32 len) 439 | { 440 | char *const op = w->op; 441 | CHECK_LE(op, w->op_limit); 442 | const u32 space_left = w->op_limit - op; 443 | 444 | if (op - w->base <= offset - 1u) /* -1u catches offset==0 */ 445 | return false; 446 | if (len <= 16 && offset >= 8 && space_left >= 16) { 447 | /* Fast path, used for the majority (70-80%) of dynamic 448 | * invocations. */ 449 | unaligned_copy64(op - offset, op); 450 | unaligned_copy64(op - offset + 8, op + 8); 451 | } else { 452 | if (space_left >= len + kmax_increment_copy_overflow) { 453 | incremental_copy_fast_path(op - offset, op, len); 454 | } else { 455 | if (space_left < len) { 456 | return false; 457 | } 458 | incremental_copy(op - offset, op, len); 459 | } 460 | } 461 | 462 | w->op = op + len; 463 | return true; 464 | } 465 | 466 | static inline bool writer_append(struct writer *w, const char *ip, u32 len) 467 | { 468 | char *const op = w->op; 469 | CHECK_LE(op, w->op_limit); 470 | const u32 space_left = w->op_limit - op; 471 | if (space_left < len) 472 | return false; 473 | memcpy(op, ip, len); 474 | w->op = op + len; 475 | return true; 476 | } 477 | 478 | static inline bool writer_try_fast_append(struct writer *w, const char *ip, 479 | u32 available_bytes, u32 len) 480 | { 481 | char *const op = w->op; 482 | const int space_left = w->op_limit - op; 483 | if (len <= 16 && available_bytes >= 16 && space_left >= 16) { 484 | /* Fast path, used for the majority (~95%) of invocations */ 485 | unaligned_copy64(ip, op); 486 | unaligned_copy64(ip + 8, op + 8); 487 | w->op = op + len; 488 | return true; 489 | } 490 | return false; 491 | } 492 | 493 | /* 494 | * Any hash function will produce a valid compressed bitstream, but a good 495 | * hash function reduces the number of collisions and thus yields better 496 | * compression for compressible input, and more speed for incompressible 497 | * input. Of course, it doesn't hurt if the hash function is reasonably fast 498 | * either, as it gets called a lot. 499 | */ 500 | static inline u32 hash_bytes(u32 bytes, int shift) 501 | { 502 | u32 kmul = 0x1e35a7bd; 503 | return (bytes * kmul) >> shift; 504 | } 505 | 506 | static inline u32 hash(const char *p, int shift) 507 | { 508 | return hash_bytes(UNALIGNED_LOAD32(p), shift); 509 | } 510 | 511 | /* 512 | * Compressed data can be defined as: 513 | * compressed := item* literal* 514 | * item := literal* copy 515 | * 516 | * The trailing literal sequence has a space blowup of at most 62/60 517 | * since a literal of length 60 needs one tag byte + one extra byte 518 | * for length information. 519 | * 520 | * Item blowup is trickier to measure. Suppose the "copy" op copies 521 | * 4 bytes of data. Because of a special check in the encoding code, 522 | * we produce a 4-byte copy only if the offset is < 65536. Therefore 523 | * the copy op takes 3 bytes to encode, and this type of item leads 524 | * to at most the 62/60 blowup for representing literals. 525 | * 526 | * Suppose the "copy" op copies 5 bytes of data. If the offset is big 527 | * enough, it will take 5 bytes to encode the copy op. Therefore the 528 | * worst case here is a one-byte literal followed by a five-byte copy. 529 | * I.e., 6 bytes of input turn into 7 bytes of "compressed" data. 530 | * 531 | * This last factor dominates the blowup, so the final estimate is: 532 | */ 533 | size_t snappy_max_compressed_length(size_t source_len) 534 | { 535 | return 32 + source_len + source_len / 6; 536 | } 537 | EXPORT_SYMBOL(snappy_max_compressed_length); 538 | 539 | enum { 540 | LITERAL = 0, 541 | COPY_1_BYTE_OFFSET = 1, /* 3 bit length + 3 bits of offset in opcode */ 542 | COPY_2_BYTE_OFFSET = 2, 543 | COPY_4_BYTE_OFFSET = 3 544 | }; 545 | 546 | static inline char *emit_literal(char *op, 547 | const char *literal, 548 | int len, bool allow_fast_path) 549 | { 550 | int n = len - 1; /* Zero-length literals are disallowed */ 551 | 552 | if (n < 60) { 553 | /* Fits in tag byte */ 554 | *op++ = LITERAL | (n << 2); 555 | 556 | /* 557 | * The vast majority of copies are below 16 bytes, for which a 558 | * call to memcpy is overkill. This fast path can sometimes 559 | * copy up to 15 bytes too much, but that is okay in the 560 | * main loop, since we have a bit to go on for both sides: 561 | * 562 | * - The input will always have kInputMarginBytes = 15 extra 563 | * available bytes, as long as we're in the main loop, and 564 | * if not, allow_fast_path = false. 565 | * - The output will always have 32 spare bytes (see 566 | * MaxCompressedLength). 567 | */ 568 | if (allow_fast_path && len <= 16) { 569 | unaligned_copy64(literal, op); 570 | unaligned_copy64(literal + 8, op + 8); 571 | return op + len; 572 | } 573 | } else { 574 | /* Encode in upcoming bytes */ 575 | char *base = op; 576 | int count = 0; 577 | op++; 578 | while (n > 0) { 579 | *op++ = n & 0xff; 580 | n >>= 8; 581 | count++; 582 | } 583 | DCHECK(count >= 1); 584 | DCHECK(count <= 4); 585 | *base = LITERAL | ((59 + count) << 2); 586 | } 587 | memcpy(op, literal, len); 588 | return op + len; 589 | } 590 | 591 | static inline char *emit_copy_less_than64(char *op, int offset, int len) 592 | { 593 | DCHECK_LE(len, 64); 594 | DCHECK_GE(len, 4); 595 | DCHECK_LT(offset, 65536); 596 | 597 | if ((len < 12) && (offset < 2048)) { 598 | int len_minus_4 = len - 4; 599 | DCHECK(len_minus_4 < 8); /* Must fit in 3 bits */ 600 | *op++ = 601 | COPY_1_BYTE_OFFSET + ((len_minus_4) << 2) + ((offset >> 8) 602 | << 5); 603 | *op++ = offset & 0xff; 604 | } else { 605 | *op++ = COPY_2_BYTE_OFFSET + ((len - 1) << 2); 606 | put_unaligned_le16(offset, op); 607 | op += 2; 608 | } 609 | return op; 610 | } 611 | 612 | static inline char *emit_copy(char *op, int offset, int len) 613 | { 614 | /* 615 | * Emit 64 byte copies but make sure to keep at least four bytes 616 | * reserved 617 | */ 618 | while (len >= 68) { 619 | op = emit_copy_less_than64(op, offset, 64); 620 | len -= 64; 621 | } 622 | 623 | /* 624 | * Emit an extra 60 byte copy if have too much data to fit in 625 | * one copy 626 | */ 627 | if (len > 64) { 628 | op = emit_copy_less_than64(op, offset, 60); 629 | len -= 60; 630 | } 631 | 632 | /* Emit remainder */ 633 | op = emit_copy_less_than64(op, offset, len); 634 | return op; 635 | } 636 | 637 | /** 638 | * snappy_uncompressed_length - return length of uncompressed output. 639 | * @start: compressed buffer 640 | * @n: length of compressed buffer. 641 | * @result: Write the length of the uncompressed output here. 642 | * 643 | * Returns true when successfull, otherwise false. 644 | */ 645 | bool snappy_uncompressed_length(const char *start, size_t n, size_t * result) 646 | { 647 | u32 v = 0; 648 | const char *limit = start + n; 649 | if (varint_parse32_with_limit(start, limit, &v) != NULL) { 650 | *result = v; 651 | return true; 652 | } else { 653 | return false; 654 | } 655 | } 656 | EXPORT_SYMBOL(snappy_uncompressed_length); 657 | 658 | /* 659 | * The size of a compression block. Note that many parts of the compression 660 | * code assumes that kBlockSize <= 65536; in particular, the hash table 661 | * can only store 16-bit offsets, and EmitCopy() also assumes the offset 662 | * is 65535 bytes or less. Note also that if you change this, it will 663 | * affect the framing format 664 | * Note that there might be older data around that is compressed with larger 665 | * block sizes, so the decompression code should not rely on the 666 | * non-existence of long backreferences. 667 | */ 668 | #define kblock_log 16 669 | #define kblock_size (1 << kblock_log) 670 | 671 | /* 672 | * This value could be halfed or quartered to save memory 673 | * at the cost of slightly worse compression. 674 | */ 675 | #define kmax_hash_table_bits 14 676 | #define kmax_hash_table_size (1U << kmax_hash_table_bits) 677 | 678 | /* 679 | * Use smaller hash table when input.size() is smaller, since we 680 | * fill the table, incurring O(hash table size) overhead for 681 | * compression, and if the input is short, we won't need that 682 | * many hash table entries anyway. 683 | */ 684 | static u16 *get_hash_table(struct snappy_env *env, size_t input_size, 685 | int *table_size) 686 | { 687 | unsigned htsize = 256; 688 | 689 | DCHECK(kmax_hash_table_size >= 256); 690 | while (htsize < kmax_hash_table_size && htsize < input_size) 691 | htsize <<= 1; 692 | CHECK_EQ(0, htsize & (htsize - 1)); 693 | CHECK_LE(htsize, kmax_hash_table_size); 694 | 695 | u16 *table; 696 | table = env->hash_table; 697 | 698 | *table_size = htsize; 699 | memset(table, 0, htsize * sizeof(*table)); 700 | return table; 701 | } 702 | 703 | /* 704 | * Return the largest n such that 705 | * 706 | * s1[0,n-1] == s2[0,n-1] 707 | * and n <= (s2_limit - s2). 708 | * 709 | * Does not read *s2_limit or beyond. 710 | * Does not read *(s1 + (s2_limit - s2)) or beyond. 711 | * Requires that s2_limit >= s2. 712 | * 713 | * Separate implementation for x86_64, for speed. Uses the fact that 714 | * x86_64 is little endian. 715 | */ 716 | #if defined(__LITTLE_ENDIAN__) && BITS_PER_LONG == 64 717 | static inline int find_match_length(const char *s1, 718 | const char *s2, const char *s2_limit) 719 | { 720 | int matched = 0; 721 | 722 | DCHECK_GE(s2_limit, s2); 723 | /* 724 | * Find out how long the match is. We loop over the data 64 bits at a 725 | * time until we find a 64-bit block that doesn't match; then we find 726 | * the first non-matching bit and use that to calculate the total 727 | * length of the match. 728 | */ 729 | while (likely(s2 <= s2_limit - 8)) { 730 | if (unlikely 731 | (UNALIGNED_LOAD64(s2) == UNALIGNED_LOAD64(s1 + matched))) { 732 | s2 += 8; 733 | matched += 8; 734 | } else { 735 | /* 736 | * On current (mid-2008) Opteron models there 737 | * is a 3% more efficient code sequence to 738 | * find the first non-matching byte. However, 739 | * what follows is ~10% better on Intel Core 2 740 | * and newer, and we expect AMD's bsf 741 | * instruction to improve. 742 | */ 743 | u64 x = 744 | UNALIGNED_LOAD64(s2) ^ UNALIGNED_LOAD64(s1 + 745 | matched); 746 | int matching_bits = find_lsb_set_non_zero64(x); 747 | matched += matching_bits >> 3; 748 | return matched; 749 | } 750 | } 751 | while (likely(s2 < s2_limit)) { 752 | if (likely(s1[matched] == *s2)) { 753 | ++s2; 754 | ++matched; 755 | } else { 756 | return matched; 757 | } 758 | } 759 | return matched; 760 | } 761 | #else 762 | static inline int find_match_length(const char *s1, 763 | const char *s2, const char *s2_limit) 764 | { 765 | /* Implementation based on the x86-64 version, above. */ 766 | DCHECK_GE(s2_limit, s2); 767 | int matched = 0; 768 | 769 | while (s2 <= s2_limit - 4 && 770 | UNALIGNED_LOAD32(s2) == UNALIGNED_LOAD32(s1 + matched)) { 771 | s2 += 4; 772 | matched += 4; 773 | } 774 | if (is_little_endian() && s2 <= s2_limit - 4) { 775 | u32 x = 776 | UNALIGNED_LOAD32(s2) ^ UNALIGNED_LOAD32(s1 + matched); 777 | int matching_bits = find_lsb_set_non_zero(x); 778 | matched += matching_bits >> 3; 779 | } else { 780 | while ((s2 < s2_limit) && (s1[matched] == *s2)) { 781 | ++s2; 782 | ++matched; 783 | } 784 | } 785 | return matched; 786 | } 787 | #endif 788 | 789 | /* 790 | * For 0 <= offset <= 4, GetU32AtOffset(GetEightBytesAt(p), offset) will 791 | * equal UNALIGNED_LOAD32(p + offset). Motivation: On x86-64 hardware we have 792 | * empirically found that overlapping loads such as 793 | * UNALIGNED_LOAD32(p) ... UNALIGNED_LOAD32(p+1) ... UNALIGNED_LOAD32(p+2) 794 | * are slower than UNALIGNED_LOAD64(p) followed by shifts and casts to u32. 795 | * 796 | * We have different versions for 64- and 32-bit; ideally we would avoid the 797 | * two functions and just inline the UNALIGNED_LOAD64 call into 798 | * GetUint32AtOffset, but GCC (at least not as of 4.6) is seemingly not clever 799 | * enough to avoid loading the value multiple times then. For 64-bit, the load 800 | * is done when GetEightBytesAt() is called, whereas for 32-bit, the load is 801 | * done at GetUint32AtOffset() time. 802 | */ 803 | 804 | #if BITS_PER_LONG == 64 805 | 806 | typedef u64 eight_bytes_reference; 807 | 808 | static inline eight_bytes_reference get_eight_bytes_at(const char* ptr) 809 | { 810 | return UNALIGNED_LOAD64(ptr); 811 | } 812 | 813 | static inline u32 get_u32_at_offset(u64 v, int offset) 814 | { 815 | DCHECK_GE(offset, 0); 816 | DCHECK_LE(offset, 4); 817 | return v >> (is_little_endian()? 8 * offset : 32 - 8 * offset); 818 | } 819 | 820 | #else 821 | 822 | typedef const char *eight_bytes_reference; 823 | 824 | static inline eight_bytes_reference get_eight_bytes_at(const char* ptr) 825 | { 826 | return ptr; 827 | } 828 | 829 | static inline u32 get_u32_at_offset(const char *v, int offset) 830 | { 831 | DCHECK_GE(offset, 0); 832 | DCHECK_LE(offset, 4); 833 | return UNALIGNED_LOAD32(v + offset); 834 | } 835 | #endif 836 | 837 | /* 838 | * Flat array compression that does not emit the "uncompressed length" 839 | * prefix. Compresses "input" string to the "*op" buffer. 840 | * 841 | * REQUIRES: "input" is at most "kBlockSize" bytes long. 842 | * REQUIRES: "op" points to an array of memory that is at least 843 | * "MaxCompressedLength(input.size())" in size. 844 | * REQUIRES: All elements in "table[0..table_size-1]" are initialized to zero. 845 | * REQUIRES: "table_size" is a power of two 846 | * 847 | * Returns an "end" pointer into "op" buffer. 848 | * "end - op" is the compressed size of "input". 849 | */ 850 | 851 | static char *compress_fragment(const char *const input, 852 | const size_t input_size, 853 | char *op, u16 * table, const unsigned table_size) 854 | { 855 | /* "ip" is the input pointer, and "op" is the output pointer. */ 856 | const char *ip = input; 857 | CHECK_LE(input_size, kblock_size); 858 | CHECK_EQ(table_size & (table_size - 1), 0); 859 | const int shift = 32 - log2_floor(table_size); 860 | DCHECK_EQ(UINT_MAX >> shift, table_size - 1); 861 | const char *ip_end = input + input_size; 862 | const char *baseip = ip; 863 | /* 864 | * Bytes in [next_emit, ip) will be emitted as literal bytes. Or 865 | * [next_emit, ip_end) after the main loop. 866 | */ 867 | const char *next_emit = ip; 868 | 869 | const unsigned kinput_margin_bytes = 15; 870 | 871 | if (likely(input_size >= kinput_margin_bytes)) { 872 | const char *const ip_limit = input + input_size - 873 | kinput_margin_bytes; 874 | 875 | u32 next_hash; 876 | for (next_hash = hash(++ip, shift);;) { 877 | DCHECK_LT(next_emit, ip); 878 | /* 879 | * The body of this loop calls EmitLiteral once and then EmitCopy one or 880 | * more times. (The exception is that when we're close to exhausting 881 | * the input we goto emit_remainder.) 882 | * 883 | * In the first iteration of this loop we're just starting, so 884 | * there's nothing to copy, so calling EmitLiteral once is 885 | * necessary. And we only start a new iteration when the 886 | * current iteration has determined that a call to EmitLiteral will 887 | * precede the next call to EmitCopy (if any). 888 | * 889 | * Step 1: Scan forward in the input looking for a 4-byte-long match. 890 | * If we get close to exhausting the input then goto emit_remainder. 891 | * 892 | * Heuristic match skipping: If 32 bytes are scanned with no matches 893 | * found, start looking only at every other byte. If 32 more bytes are 894 | * scanned, look at every third byte, etc.. When a match is found, 895 | * immediately go back to looking at every byte. This is a small loss 896 | * (~5% performance, ~0.1% density) for lcompressible data due to more 897 | * bookkeeping, but for non-compressible data (such as JPEG) it's a huge 898 | * win since the compressor quickly "realizes" the data is incompressible 899 | * and doesn't bother looking for matches everywhere. 900 | * 901 | * The "skip" variable keeps track of how many bytes there are since the 902 | * last match; dividing it by 32 (ie. right-shifting by five) gives the 903 | * number of bytes to move ahead for each iteration. 904 | */ 905 | u32 skip_bytes = 32; 906 | 907 | const char *next_ip = ip; 908 | const char *candidate; 909 | do { 910 | ip = next_ip; 911 | u32 hval = next_hash; 912 | DCHECK_EQ(hval, hash(ip, shift)); 913 | u32 bytes_between_hash_lookups = skip_bytes++ >> 5; 914 | next_ip = ip + bytes_between_hash_lookups; 915 | if (unlikely(next_ip > ip_limit)) { 916 | goto emit_remainder; 917 | } 918 | next_hash = hash(next_ip, shift); 919 | candidate = baseip + table[hval]; 920 | DCHECK_GE(candidate, baseip); 921 | DCHECK_LT(candidate, ip); 922 | 923 | table[hval] = ip - baseip; 924 | } while (likely(UNALIGNED_LOAD32(ip) != 925 | UNALIGNED_LOAD32(candidate))); 926 | 927 | /* 928 | * Step 2: A 4-byte match has been found. We'll later see if more 929 | * than 4 bytes match. But, prior to the match, input 930 | * bytes [next_emit, ip) are unmatched. Emit them as "literal bytes." 931 | */ 932 | DCHECK_LE(next_emit + 16, ip_end); 933 | op = emit_literal(op, next_emit, ip - next_emit, true); 934 | 935 | /* 936 | * Step 3: Call EmitCopy, and then see if another EmitCopy could 937 | * be our next move. Repeat until we find no match for the 938 | * input immediately after what was consumed by the last EmitCopy call. 939 | * 940 | * If we exit this loop normally then we need to call EmitLiteral next, 941 | * though we don't yet know how big the literal will be. We handle that 942 | * by proceeding to the next iteration of the main loop. We also can exit 943 | * this loop via goto if we get close to exhausting the input. 944 | */ 945 | eight_bytes_reference input_bytes; 946 | u32 candidate_bytes = 0; 947 | 948 | do { 949 | /* 950 | * We have a 4-byte match at ip, and no need to emit any 951 | * "literal bytes" prior to ip. 952 | */ 953 | const char *base = ip; 954 | int matched = 4 + 955 | find_match_length(candidate + 4, ip + 4, 956 | ip_end); 957 | ip += matched; 958 | int offset = base - candidate; 959 | DCHECK_EQ(0, memcmp(base, candidate, matched)); 960 | op = emit_copy(op, offset, matched); 961 | /* 962 | * We could immediately start working at ip now, but to improve 963 | * compression we first update table[Hash(ip - 1, ...)]. 964 | */ 965 | const char *insert_tail = ip - 1; 966 | next_emit = ip; 967 | if (unlikely(ip >= ip_limit)) { 968 | goto emit_remainder; 969 | } 970 | input_bytes = get_eight_bytes_at(insert_tail); 971 | u32 prev_hash = 972 | hash_bytes(get_u32_at_offset 973 | (input_bytes, 0), shift); 974 | table[prev_hash] = ip - baseip - 1; 975 | u32 cur_hash = 976 | hash_bytes(get_u32_at_offset 977 | (input_bytes, 1), shift); 978 | candidate = baseip + table[cur_hash]; 979 | candidate_bytes = UNALIGNED_LOAD32(candidate); 980 | table[cur_hash] = ip - baseip; 981 | } while (get_u32_at_offset(input_bytes, 1) == 982 | candidate_bytes); 983 | 984 | next_hash = 985 | hash_bytes(get_u32_at_offset(input_bytes, 2), 986 | shift); 987 | ++ip; 988 | } 989 | } 990 | 991 | emit_remainder: 992 | /* Emit the remaining bytes as a literal */ 993 | if (next_emit < ip_end) 994 | op = emit_literal(op, next_emit, ip_end - next_emit, false); 995 | 996 | return op; 997 | } 998 | 999 | /* 1000 | * ----------------------------------------------------------------------- 1001 | * Lookup table for decompression code. Generated by ComputeTable() below. 1002 | * ----------------------------------------------------------------------- 1003 | */ 1004 | 1005 | /* Mapping from i in range [0,4] to a mask to extract the bottom 8*i bits */ 1006 | static const u32 wordmask[] = { 1007 | 0u, 0xffu, 0xffffu, 0xffffffu, 0xffffffffu 1008 | }; 1009 | 1010 | /* 1011 | * Data stored per entry in lookup table: 1012 | * Range Bits-used Description 1013 | * ------------------------------------ 1014 | * 1..64 0..7 Literal/copy length encoded in opcode byte 1015 | * 0..7 8..10 Copy offset encoded in opcode byte / 256 1016 | * 0..4 11..13 Extra bytes after opcode 1017 | * 1018 | * We use eight bits for the length even though 7 would have sufficed 1019 | * because of efficiency reasons: 1020 | * (1) Extracting a byte is faster than a bit-field 1021 | * (2) It properly aligns copy offset so we do not need a <<8 1022 | */ 1023 | static const u16 char_table[256] = { 1024 | 0x0001, 0x0804, 0x1001, 0x2001, 0x0002, 0x0805, 0x1002, 0x2002, 1025 | 0x0003, 0x0806, 0x1003, 0x2003, 0x0004, 0x0807, 0x1004, 0x2004, 1026 | 0x0005, 0x0808, 0x1005, 0x2005, 0x0006, 0x0809, 0x1006, 0x2006, 1027 | 0x0007, 0x080a, 0x1007, 0x2007, 0x0008, 0x080b, 0x1008, 0x2008, 1028 | 0x0009, 0x0904, 0x1009, 0x2009, 0x000a, 0x0905, 0x100a, 0x200a, 1029 | 0x000b, 0x0906, 0x100b, 0x200b, 0x000c, 0x0907, 0x100c, 0x200c, 1030 | 0x000d, 0x0908, 0x100d, 0x200d, 0x000e, 0x0909, 0x100e, 0x200e, 1031 | 0x000f, 0x090a, 0x100f, 0x200f, 0x0010, 0x090b, 0x1010, 0x2010, 1032 | 0x0011, 0x0a04, 0x1011, 0x2011, 0x0012, 0x0a05, 0x1012, 0x2012, 1033 | 0x0013, 0x0a06, 0x1013, 0x2013, 0x0014, 0x0a07, 0x1014, 0x2014, 1034 | 0x0015, 0x0a08, 0x1015, 0x2015, 0x0016, 0x0a09, 0x1016, 0x2016, 1035 | 0x0017, 0x0a0a, 0x1017, 0x2017, 0x0018, 0x0a0b, 0x1018, 0x2018, 1036 | 0x0019, 0x0b04, 0x1019, 0x2019, 0x001a, 0x0b05, 0x101a, 0x201a, 1037 | 0x001b, 0x0b06, 0x101b, 0x201b, 0x001c, 0x0b07, 0x101c, 0x201c, 1038 | 0x001d, 0x0b08, 0x101d, 0x201d, 0x001e, 0x0b09, 0x101e, 0x201e, 1039 | 0x001f, 0x0b0a, 0x101f, 0x201f, 0x0020, 0x0b0b, 0x1020, 0x2020, 1040 | 0x0021, 0x0c04, 0x1021, 0x2021, 0x0022, 0x0c05, 0x1022, 0x2022, 1041 | 0x0023, 0x0c06, 0x1023, 0x2023, 0x0024, 0x0c07, 0x1024, 0x2024, 1042 | 0x0025, 0x0c08, 0x1025, 0x2025, 0x0026, 0x0c09, 0x1026, 0x2026, 1043 | 0x0027, 0x0c0a, 0x1027, 0x2027, 0x0028, 0x0c0b, 0x1028, 0x2028, 1044 | 0x0029, 0x0d04, 0x1029, 0x2029, 0x002a, 0x0d05, 0x102a, 0x202a, 1045 | 0x002b, 0x0d06, 0x102b, 0x202b, 0x002c, 0x0d07, 0x102c, 0x202c, 1046 | 0x002d, 0x0d08, 0x102d, 0x202d, 0x002e, 0x0d09, 0x102e, 0x202e, 1047 | 0x002f, 0x0d0a, 0x102f, 0x202f, 0x0030, 0x0d0b, 0x1030, 0x2030, 1048 | 0x0031, 0x0e04, 0x1031, 0x2031, 0x0032, 0x0e05, 0x1032, 0x2032, 1049 | 0x0033, 0x0e06, 0x1033, 0x2033, 0x0034, 0x0e07, 0x1034, 0x2034, 1050 | 0x0035, 0x0e08, 0x1035, 0x2035, 0x0036, 0x0e09, 0x1036, 0x2036, 1051 | 0x0037, 0x0e0a, 0x1037, 0x2037, 0x0038, 0x0e0b, 0x1038, 0x2038, 1052 | 0x0039, 0x0f04, 0x1039, 0x2039, 0x003a, 0x0f05, 0x103a, 0x203a, 1053 | 0x003b, 0x0f06, 0x103b, 0x203b, 0x003c, 0x0f07, 0x103c, 0x203c, 1054 | 0x0801, 0x0f08, 0x103d, 0x203d, 0x1001, 0x0f09, 0x103e, 0x203e, 1055 | 0x1801, 0x0f0a, 0x103f, 0x203f, 0x2001, 0x0f0b, 0x1040, 0x2040 1056 | }; 1057 | 1058 | struct snappy_decompressor { 1059 | struct source *reader; /* Underlying source of bytes to decompress */ 1060 | const char *ip; /* Points to next buffered byte */ 1061 | const char *ip_limit; /* Points just past buffered bytes */ 1062 | u32 peeked; /* Bytes peeked from reader (need to skip) */ 1063 | bool eof; /* Hit end of input without an error? */ 1064 | char scratch[5]; /* Temporary buffer for peekfast boundaries */ 1065 | }; 1066 | 1067 | static void 1068 | init_snappy_decompressor(struct snappy_decompressor *d, struct source *reader) 1069 | { 1070 | d->reader = reader; 1071 | d->ip = NULL; 1072 | d->ip_limit = NULL; 1073 | d->peeked = 0; 1074 | d->eof = false; 1075 | } 1076 | 1077 | static void exit_snappy_decompressor(struct snappy_decompressor *d) 1078 | { 1079 | skip(d->reader, d->peeked); 1080 | } 1081 | 1082 | /* 1083 | * Read the uncompressed length stored at the start of the compressed data. 1084 | * On succcess, stores the length in *result and returns true. 1085 | * On failure, returns false. 1086 | */ 1087 | static bool read_uncompressed_length(struct snappy_decompressor *d, 1088 | u32 * result) 1089 | { 1090 | DCHECK(d->ip == NULL); /* 1091 | * Must not have read anything yet 1092 | * Length is encoded in 1..5 bytes 1093 | */ 1094 | *result = 0; 1095 | u32 shift = 0; 1096 | while (true) { 1097 | if (shift >= 32) 1098 | return false; 1099 | size_t n; 1100 | const char *ip = peek(d->reader, &n); 1101 | if (n == 0) 1102 | return false; 1103 | const unsigned char c = *(const unsigned char *)(ip); 1104 | skip(d->reader, 1); 1105 | *result |= (u32) (c & 0x7f) << shift; 1106 | if (c < 128) { 1107 | break; 1108 | } 1109 | shift += 7; 1110 | } 1111 | return true; 1112 | } 1113 | 1114 | static bool refill_tag(struct snappy_decompressor *d); 1115 | 1116 | /* 1117 | * Process the next item found in the input. 1118 | * Returns true if successful, false on error or end of input. 1119 | */ 1120 | static void decompress_all_tags(struct snappy_decompressor *d, 1121 | struct writer *writer) 1122 | { 1123 | const char *ip = d->ip; 1124 | 1125 | /* 1126 | * We could have put this refill fragment only at the beginning of the loop. 1127 | * However, duplicating it at the end of each branch gives the compiler more 1128 | * scope to optimize the expression based on the local 1129 | * context, which overall increases speed. 1130 | */ 1131 | #define MAYBE_REFILL() \ 1132 | if (d->ip_limit - ip < 5) { \ 1133 | d->ip = ip; \ 1134 | if (!refill_tag(d)) return; \ 1135 | ip = d->ip; \ 1136 | } 1137 | 1138 | 1139 | MAYBE_REFILL(); 1140 | for (;;) { 1141 | if (d->ip_limit - ip < 5) { 1142 | d->ip = ip; 1143 | if (!refill_tag(d)) 1144 | return; 1145 | ip = d->ip; 1146 | } 1147 | 1148 | const unsigned char c = *(const unsigned char *)(ip++); 1149 | 1150 | if ((c & 0x3) == LITERAL) { 1151 | u32 literal_length = (c >> 2) + 1; 1152 | if (writer_try_fast_append(writer, ip, d->ip_limit - ip, 1153 | literal_length)) { 1154 | DCHECK_LT(literal_length, 61); 1155 | ip += literal_length; 1156 | MAYBE_REFILL(); 1157 | continue; 1158 | } 1159 | if (unlikely(literal_length >= 61)) { 1160 | /* Long literal */ 1161 | const u32 literal_ll = literal_length - 60; 1162 | literal_length = (get_unaligned_le32(ip) & 1163 | wordmask[literal_ll]) + 1; 1164 | ip += literal_ll; 1165 | } 1166 | 1167 | u32 avail = d->ip_limit - ip; 1168 | while (avail < literal_length) { 1169 | if (!writer_append(writer, ip, avail)) 1170 | return; 1171 | literal_length -= avail; 1172 | skip(d->reader, d->peeked); 1173 | size_t n; 1174 | ip = peek(d->reader, &n); 1175 | avail = n; 1176 | d->peeked = avail; 1177 | if (avail == 0) 1178 | return; /* Premature end of input */ 1179 | d->ip_limit = ip + avail; 1180 | } 1181 | if (!writer_append(writer, ip, literal_length)) 1182 | return; 1183 | ip += literal_length; 1184 | MAYBE_REFILL(); 1185 | } else { 1186 | const u32 entry = char_table[c]; 1187 | const u32 trailer = get_unaligned_le32(ip) & 1188 | wordmask[entry >> 11]; 1189 | const u32 length = entry & 0xff; 1190 | ip += entry >> 11; 1191 | 1192 | /* 1193 | * copy_offset/256 is encoded in bits 8..10. 1194 | * By just fetching those bits, we get 1195 | * copy_offset (since the bit-field starts at 1196 | * bit 8). 1197 | */ 1198 | const u32 copy_offset = entry & 0x700; 1199 | if (!writer_append_from_self(writer, 1200 | copy_offset + trailer, 1201 | length)) 1202 | return; 1203 | MAYBE_REFILL(); 1204 | } 1205 | } 1206 | } 1207 | 1208 | #undef MAYBE_REFILL 1209 | 1210 | static bool refill_tag(struct snappy_decompressor *d) 1211 | { 1212 | const char *ip = d->ip; 1213 | 1214 | if (ip == d->ip_limit) { 1215 | size_t n; 1216 | /* Fetch a new fragment from the reader */ 1217 | skip(d->reader, d->peeked); /* All peeked bytes are used up */ 1218 | ip = peek(d->reader, &n); 1219 | d->peeked = n; 1220 | if (n == 0) { 1221 | d->eof = true; 1222 | return false; 1223 | } 1224 | d->ip_limit = ip + n; 1225 | } 1226 | 1227 | /* Read the tag character */ 1228 | DCHECK_LT(ip, d->ip_limit); 1229 | const unsigned char c = *(const unsigned char *)(ip); 1230 | const u32 entry = char_table[c]; 1231 | const u32 needed = (entry >> 11) + 1; /* +1 byte for 'c' */ 1232 | DCHECK_LE(needed, sizeof(d->scratch)); 1233 | 1234 | /* Read more bytes from reader if needed */ 1235 | u32 nbuf = d->ip_limit - ip; 1236 | 1237 | if (nbuf < needed) { 1238 | /* 1239 | * Stitch together bytes from ip and reader to form the word 1240 | * contents. We store the needed bytes in "scratch". They 1241 | * will be consumed immediately by the caller since we do not 1242 | * read more than we need. 1243 | */ 1244 | memmove(d->scratch, ip, nbuf); 1245 | skip(d->reader, d->peeked); /* All peeked bytes are used up */ 1246 | d->peeked = 0; 1247 | while (nbuf < needed) { 1248 | size_t length; 1249 | const char *src = peek(d->reader, &length); 1250 | if (length == 0) 1251 | return false; 1252 | u32 to_add = min_t(u32, needed - nbuf, length); 1253 | memcpy(d->scratch + nbuf, src, to_add); 1254 | nbuf += to_add; 1255 | skip(d->reader, to_add); 1256 | } 1257 | DCHECK_EQ(nbuf, needed); 1258 | d->ip = d->scratch; 1259 | d->ip_limit = d->scratch + needed; 1260 | } else if (nbuf < 5) { 1261 | /* 1262 | * Have enough bytes, but move into scratch so that we do not 1263 | * read past end of input 1264 | */ 1265 | memmove(d->scratch, ip, nbuf); 1266 | skip(d->reader, d->peeked); /* All peeked bytes are used up */ 1267 | d->peeked = 0; 1268 | d->ip = d->scratch; 1269 | d->ip_limit = d->scratch + nbuf; 1270 | } else { 1271 | /* Pass pointer to buffer returned by reader. */ 1272 | d->ip = ip; 1273 | } 1274 | return true; 1275 | } 1276 | 1277 | static int internal_uncompress(struct source *r, 1278 | struct writer *writer, u32 max_len) 1279 | { 1280 | struct snappy_decompressor decompressor; 1281 | u32 uncompressed_len = 0; 1282 | 1283 | init_snappy_decompressor(&decompressor, r); 1284 | 1285 | if (!read_uncompressed_length(&decompressor, &uncompressed_len)) 1286 | return -EIO; 1287 | /* Protect against possible DoS attack */ 1288 | if ((u64) (uncompressed_len) > max_len) 1289 | return -EIO; 1290 | 1291 | writer_set_expected_length(writer, uncompressed_len); 1292 | 1293 | /* Process the entire input */ 1294 | decompress_all_tags(&decompressor, writer); 1295 | 1296 | exit_snappy_decompressor(&decompressor); 1297 | if (decompressor.eof && writer_check_length(writer)) 1298 | return 0; 1299 | return -EIO; 1300 | } 1301 | 1302 | static inline int compress(struct snappy_env *env, struct source *reader, 1303 | struct sink *writer) 1304 | { 1305 | int err; 1306 | size_t written = 0; 1307 | int N = available(reader); 1308 | char ulength[kmax32]; 1309 | char *p = varint_encode32(ulength, N); 1310 | 1311 | append(writer, ulength, p - ulength); 1312 | written += (p - ulength); 1313 | 1314 | while (N > 0) { 1315 | /* Get next block to compress (without copying if possible) */ 1316 | size_t fragment_size; 1317 | const char *fragment = peek(reader, &fragment_size); 1318 | if (fragment_size == 0) { 1319 | err = -EIO; 1320 | goto out; 1321 | } 1322 | const unsigned num_to_read = min_t(int, N, kblock_size); 1323 | size_t bytes_read = fragment_size; 1324 | 1325 | int pending_advance = 0; 1326 | if (bytes_read >= num_to_read) { 1327 | /* Buffer returned by reader is large enough */ 1328 | pending_advance = num_to_read; 1329 | fragment_size = num_to_read; 1330 | } 1331 | else { 1332 | memcpy(env->scratch, fragment, bytes_read); 1333 | skip(reader, bytes_read); 1334 | 1335 | while (bytes_read < num_to_read) { 1336 | fragment = peek(reader, &fragment_size); 1337 | size_t n = 1338 | min_t(size_t, fragment_size, 1339 | num_to_read - bytes_read); 1340 | memcpy((char *)(env->scratch) + bytes_read, fragment, n); 1341 | bytes_read += n; 1342 | skip(reader, n); 1343 | } 1344 | DCHECK_EQ(bytes_read, num_to_read); 1345 | fragment = env->scratch; 1346 | fragment_size = num_to_read; 1347 | } 1348 | if (fragment_size < num_to_read) 1349 | return -EIO; 1350 | 1351 | /* Get encoding table for compression */ 1352 | int table_size; 1353 | u16 *table = get_hash_table(env, num_to_read, &table_size); 1354 | 1355 | /* Compress input_fragment and append to dest */ 1356 | char *dest; 1357 | dest = sink_peek(writer, snappy_max_compressed_length(num_to_read)); 1358 | if (!dest) { 1359 | /* 1360 | * Need a scratch buffer for the output, 1361 | * because the byte sink doesn't have enough 1362 | * in one piece. 1363 | */ 1364 | dest = env->scratch_output; 1365 | } 1366 | char *end = compress_fragment(fragment, fragment_size, 1367 | dest, table, table_size); 1368 | append(writer, dest, end - dest); 1369 | written += (end - dest); 1370 | 1371 | N -= num_to_read; 1372 | skip(reader, pending_advance); 1373 | } 1374 | 1375 | err = 0; 1376 | out: 1377 | return err; 1378 | } 1379 | 1380 | #ifdef SG 1381 | 1382 | int snappy_compress_iov(struct snappy_env *env, 1383 | struct iovec *iov_in, 1384 | int iov_in_len, 1385 | size_t input_length, 1386 | struct iovec *iov_out, 1387 | int *iov_out_len, 1388 | size_t *compressed_length) 1389 | { 1390 | struct source reader = { 1391 | .iov = iov_in, 1392 | .iovlen = iov_in_len, 1393 | .total = input_length 1394 | }; 1395 | struct sink writer = { 1396 | .iov = iov_out, 1397 | .iovlen = *iov_out_len, 1398 | }; 1399 | int err = compress(env, &reader, &writer); 1400 | 1401 | *iov_out_len = writer.curvec + 1; 1402 | 1403 | /* Compute how many bytes were added */ 1404 | *compressed_length = writer.written; 1405 | return err; 1406 | } 1407 | EXPORT_SYMBOL(snappy_compress_iov); 1408 | 1409 | /** 1410 | * snappy_compress - Compress a buffer using the snappy compressor. 1411 | * @env: Preallocated environment 1412 | * @input: Input buffer 1413 | * @input_length: Length of input_buffer 1414 | * @compressed: Output buffer for compressed data 1415 | * @compressed_length: The real length of the output written here. 1416 | * 1417 | * Return 0 on success, otherwise an negative error code. 1418 | * 1419 | * The output buffer must be at least 1420 | * snappy_max_compressed_length(input_length) bytes long. 1421 | * 1422 | * Requires a preallocated environment from snappy_init_env. 1423 | * The environment does not keep state over individual calls 1424 | * of this function, just preallocates the memory. 1425 | */ 1426 | int snappy_compress(struct snappy_env *env, 1427 | const char *input, 1428 | size_t input_length, 1429 | char *compressed, size_t *compressed_length) 1430 | { 1431 | struct iovec iov_in = { 1432 | .iov_base = (char *)input, 1433 | .iov_len = input_length, 1434 | }; 1435 | struct iovec iov_out = { 1436 | .iov_base = compressed, 1437 | .iov_len = 0xffffffff, 1438 | }; 1439 | int out = 1; 1440 | return snappy_compress_iov(env, 1441 | &iov_in, 1, input_length, 1442 | &iov_out, &out, compressed_length); 1443 | } 1444 | EXPORT_SYMBOL(snappy_compress); 1445 | 1446 | int snappy_uncompress_iov(struct iovec *iov_in, int iov_in_len, 1447 | size_t input_len, char *uncompressed) 1448 | { 1449 | struct source reader = { 1450 | .iov = iov_in, 1451 | .iovlen = iov_in_len, 1452 | .total = input_len 1453 | }; 1454 | struct writer output = { 1455 | .base = uncompressed, 1456 | .op = uncompressed 1457 | }; 1458 | return internal_uncompress(&reader, &output, 0xffffffff); 1459 | } 1460 | EXPORT_SYMBOL(snappy_uncompress_iov); 1461 | 1462 | /** 1463 | * snappy_uncompress - Uncompress a snappy compressed buffer 1464 | * @compressed: Input buffer with compressed data 1465 | * @n: length of compressed buffer 1466 | * @uncompressed: buffer for uncompressed data 1467 | * 1468 | * The uncompressed data buffer must be at least 1469 | * snappy_uncompressed_length(compressed) bytes long. 1470 | * 1471 | * Return 0 on success, otherwise an negative error code. 1472 | */ 1473 | int snappy_uncompress(const char *compressed, size_t n, char *uncompressed) 1474 | { 1475 | struct iovec iov = { 1476 | .iov_base = (char *)compressed, 1477 | .iov_len = n 1478 | }; 1479 | return snappy_uncompress_iov(&iov, 1, n, uncompressed); 1480 | } 1481 | EXPORT_SYMBOL(snappy_uncompress); 1482 | 1483 | #else 1484 | /** 1485 | * snappy_compress - Compress a buffer using the snappy compressor. 1486 | * @env: Preallocated environment 1487 | * @input: Input buffer 1488 | * @input_length: Length of input_buffer 1489 | * @compressed: Output buffer for compressed data 1490 | * @compressed_length: The real length of the output written here. 1491 | * 1492 | * Return 0 on success, otherwise an negative error code. 1493 | * 1494 | * The output buffer must be at least 1495 | * snappy_max_compressed_length(input_length) bytes long. 1496 | * 1497 | * Requires a preallocated environment from snappy_init_env. 1498 | * The environment does not keep state over individual calls 1499 | * of this function, just preallocates the memory. 1500 | */ 1501 | int snappy_compress(struct snappy_env *env, 1502 | const char *input, 1503 | size_t input_length, 1504 | char *compressed, size_t *compressed_length) 1505 | { 1506 | struct source reader = { 1507 | .ptr = input, 1508 | .left = input_length 1509 | }; 1510 | struct sink writer = { 1511 | .dest = compressed, 1512 | }; 1513 | int err = compress(env, &reader, &writer); 1514 | 1515 | /* Compute how many bytes were added */ 1516 | *compressed_length = (writer.dest - compressed); 1517 | return err; 1518 | } 1519 | EXPORT_SYMBOL(snappy_compress); 1520 | 1521 | /** 1522 | * snappy_uncompress - Uncompress a snappy compressed buffer 1523 | * @compressed: Input buffer with compressed data 1524 | * @n: length of compressed buffer 1525 | * @uncompressed: buffer for uncompressed data 1526 | * 1527 | * The uncompressed data buffer must be at least 1528 | * snappy_uncompressed_length(compressed) bytes long. 1529 | * 1530 | * Return 0 on success, otherwise an negative error code. 1531 | */ 1532 | int snappy_uncompress(const char *compressed, size_t n, char *uncompressed) 1533 | { 1534 | struct source reader = { 1535 | .ptr = compressed, 1536 | .left = n 1537 | }; 1538 | struct writer output = { 1539 | .base = uncompressed, 1540 | .op = uncompressed 1541 | }; 1542 | return internal_uncompress(&reader, &output, 0xffffffff); 1543 | } 1544 | EXPORT_SYMBOL(snappy_uncompress); 1545 | #endif 1546 | 1547 | static inline void clear_env(struct snappy_env *env) 1548 | { 1549 | memset(env, 0, sizeof(*env)); 1550 | } 1551 | 1552 | #ifdef SG 1553 | /** 1554 | * snappy_init_env_sg - Allocate snappy compression environment 1555 | * @env: Environment to preallocate 1556 | * @sg: Input environment ever does scather gather 1557 | * 1558 | * If false is passed to sg then multiple entries in an iovec 1559 | * are not legal. 1560 | * Returns 0 on success, otherwise negative errno. 1561 | * Must run in process context. 1562 | */ 1563 | int snappy_init_env_sg(struct snappy_env *env, bool sg) 1564 | { 1565 | if (snappy_init_env(env) < 0) 1566 | goto error; 1567 | 1568 | if (sg) { 1569 | env->scratch = vmalloc(kblock_size); 1570 | if (!env->scratch) 1571 | goto error; 1572 | env->scratch_output = 1573 | vmalloc(snappy_max_compressed_length(kblock_size)); 1574 | if (!env->scratch_output) 1575 | goto error; 1576 | } 1577 | return 0; 1578 | error: 1579 | snappy_free_env(env); 1580 | return -ENOMEM; 1581 | } 1582 | EXPORT_SYMBOL(snappy_init_env_sg); 1583 | #endif 1584 | 1585 | /** 1586 | * snappy_init_env - Allocate snappy compression environment 1587 | * @env: Environment to preallocate 1588 | * 1589 | * Passing multiple entries in an iovec is not allowed 1590 | * on the environment allocated here. 1591 | * Returns 0 on success, otherwise negative errno. 1592 | * Must run in process context. 1593 | */ 1594 | int snappy_init_env(struct snappy_env *env) 1595 | { 1596 | clear_env(env); 1597 | env->hash_table = vmalloc(sizeof(u16) * kmax_hash_table_size); 1598 | if (!env->hash_table) 1599 | return -ENOMEM; 1600 | return 0; 1601 | } 1602 | EXPORT_SYMBOL(snappy_init_env); 1603 | 1604 | /** 1605 | * snappy_free_env - Free an snappy compression environment 1606 | * @env: Environment to free. 1607 | * 1608 | * Must run in process context. 1609 | */ 1610 | void snappy_free_env(struct snappy_env *env) 1611 | { 1612 | vfree(env->hash_table); 1613 | #ifdef SG 1614 | vfree(env->scratch); 1615 | vfree(env->scratch_output); 1616 | #endif 1617 | clear_env(env); 1618 | } 1619 | EXPORT_SYMBOL(snappy_free_env); 1620 | -------------------------------------------------------------------------------- /nimsnappyc/private/snappy.h: -------------------------------------------------------------------------------- 1 | #ifndef _LINUX_SNAPPY_H 2 | #define _LINUX_SNAPPY_H 1 3 | 4 | #include 5 | #include 6 | 7 | /* Only needed for compression. This preallocates the worst case */ 8 | struct snappy_env { 9 | unsigned short *hash_table; 10 | void *scratch; 11 | void *scratch_output; 12 | }; 13 | 14 | struct iovec; 15 | int snappy_init_env(struct snappy_env *env); 16 | int snappy_init_env_sg(struct snappy_env *env, bool sg); 17 | void snappy_free_env(struct snappy_env *env); 18 | int snappy_uncompress_iov(struct iovec *iov_in, int iov_in_len, 19 | size_t input_len, char *uncompressed); 20 | int snappy_uncompress(const char *compressed, size_t n, char *uncompressed); 21 | int snappy_compress(struct snappy_env *env, 22 | const char *input, 23 | size_t input_length, 24 | char *compressed, 25 | size_t *compressed_length); 26 | int snappy_compress_iov(struct snappy_env *env, 27 | struct iovec *iov_in, 28 | int iov_in_len, 29 | size_t input_length, 30 | struct iovec *iov_out, 31 | int *iov_out_len, 32 | size_t *compressed_length); 33 | bool snappy_uncompressed_length(const char *buf, size_t len, size_t *result); 34 | size_t snappy_max_compressed_length(size_t source_len); 35 | 36 | 37 | 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /nimsnappyc/snappyc.nim: -------------------------------------------------------------------------------- 1 | {.compile: "private/snappy.c".} 2 | 3 | type 4 | SnappyEnv* = object 5 | hash_table: pointer 6 | scratch: pointer 7 | scratch_output: pointer 8 | PSnappyEnv* = ptr SnappyEnv 9 | 10 | proc snappy_uncompressed_length*(input: pointer, inputLength: csize, result: ptr csize): bool {.importc.} 11 | proc snappy_max_compressed_length*(inputLength: csize): csize {.importc.} 12 | 13 | proc snappy_init_env*(env: PSnappyEnv): cint {.importc.} 14 | proc snappy_free_env*(env: PSnappyEnv) {.importc.} 15 | 16 | proc snappy_compress*(env: PSnappyEnv, input: pointer, inputLength: csize, 17 | compressed: pointer, compressedLength: ptr csize): cint {.importc.} 18 | proc snappy_uncompress*(compressed: pointer, compressedLength: csize, uncompressed: pointer): cint {.importc.} -------------------------------------------------------------------------------- /tests/filecompress.nim: -------------------------------------------------------------------------------- 1 | import os, memfiles, streams 2 | import nimsnappyc 3 | 4 | if paramCount() < 1: 5 | echo "Usage: ", paramStr(0), " " 6 | quit(QuitFailure) 7 | 8 | let fileName = paramStr(1) 9 | let compressedFileName = fileName.changeFileExt("snapped") 10 | let uncompressedFileName = fileName.changeFileExt("unsnapped") 11 | 12 | var memFile = memfiles.open(fileName) 13 | echo "Size of ", fileName, " = ", memFile.size 14 | 15 | let compressedData = snappyCompress(memFile.mem, memFile.size) 16 | memfiles.close(memFile) 17 | 18 | echo "Compressed size = ", compressedData.len 19 | 20 | var stream = newFileStream(compressedFileName, fmWrite) 21 | stream.writeData(compressedData[0].unsafeAddr, compressedData.len) 22 | stream.close 23 | 24 | let uncompressedData = snappyUncompress(compressedData) 25 | 26 | echo "Uncompressed size = ", uncompressedData.len 27 | 28 | stream = newFileStream(uncompressedFilename, fmWrite) 29 | stream.writeData(uncompressedData[0].unsafeAddr, uncompressedData.len) 30 | stream.close 31 | 32 | if sameFileContent(fileName, uncompressedFilename): 33 | echo "test is OK" 34 | else: 35 | echo "Content of ", fileName, " and ", uncompressedFilename, " isn't equal!" 36 | -------------------------------------------------------------------------------- /tests/filecompress.nim.cfg: -------------------------------------------------------------------------------- 1 | --path:".." -------------------------------------------------------------------------------- /tests/test.nim: -------------------------------------------------------------------------------- 1 | import nimsnappyc 2 | import unittest 3 | 4 | echo "hostOS: ", hostOS 5 | echo "hostCPU: ", hostCPU 6 | 7 | suite "Snappy tests": 8 | 9 | test "Seq compress/uncompress": 10 | let data = @[48'u8, 49, 50, 51, 52, 53, 54, 55, 56, 57] 11 | let compressed = snappyCompress(data) 12 | let uncompressed = snappyUncompress(compressed) 13 | check data == uncompressed 14 | 15 | test "OpenArray compress/uncompress": 16 | let data = @[48'u8, 49, 50, 51, 52, 53, 54, 55, 56, 57] 17 | let compressed = snappyCompress(data.toOpenArray(0, data.high)) 18 | let uncompressed = snappyUncompress(compressed.toOpenArray(0, compressed.high)) 19 | check data == uncompressed 20 | 21 | test "String compress/uncompress": 22 | let data = "0123456789" 23 | let compressed = snappyCompress(data) 24 | let uncompressed = snappyUncompress(compressed) 25 | check data == uncompressed 26 | 27 | test "Empty seq compress/uncompress": 28 | let data = newSeq[byte]() 29 | let compressed = snappyCompress(data) 30 | let uncompressed = snappyUncompress(compressed) 31 | check data == uncompressed 32 | 33 | test "Epmty string compress/uncompress": 34 | let data = "" 35 | let compressed = snappyCompress(data) 36 | let uncompressed = snappyUncompress(compressed) 37 | check data == uncompressed -------------------------------------------------------------------------------- /tests/test.nim.cfg: -------------------------------------------------------------------------------- 1 | --path:".." -------------------------------------------------------------------------------- /tests/test2.nim: -------------------------------------------------------------------------------- 1 | import nimsnappyc 2 | 3 | let data = "0123456789abcdef" 4 | let compressed = snappyCompress(data) 5 | 6 | echo "Original data = ", data 7 | echo "data length = ", data.len 8 | echo "compressed.len = ", compressed.len 9 | echo "Max. compressed length = ", snappyMaxCompressedLength(data.len) 10 | echo "uncompressed length = ", snappyUncompressedLength(compressed) 11 | 12 | let uncompressed = snappyUncompress(compressed) 13 | 14 | echo "uncompressed.len = ", uncompressed.len 15 | echo "uncompressed data = ", uncompressed 16 | -------------------------------------------------------------------------------- /tests/test2.nim.cfg: -------------------------------------------------------------------------------- 1 | --path:".." --------------------------------------------------------------------------------