1% Licensed under the Apache License, Version 2.0 (the "License"); you may not 2% use this file except in compliance with the License. You may obtain a copy of 3% the License at 4% 5% http://www.apache.org/licenses/LICENSE-2.0 6% 7% Unless required by applicable law or agreed to in writing, software 8% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10% License for the specific language governing permissions and limitations under 11% the License. 12 13-module(couch_compress). 14 15-export([compress/2, decompress/1, is_compressed/2]). 16-export([get_compression_method/0]). 17-export([uncompressed_size/1]). 18 19-include_lib("couch/include/couch_db.hrl"). 20 21% binaries compressed with snappy have their first byte set to this value 22-define(SNAPPY_PREFIX, 1). 23% Term prefixes documented at: 24% http://www.erlang.org/doc/apps/erts/erl_ext_dist.html 25-define(TERM_PREFIX, 131). 26-define(COMPRESSED_TERM_PREFIX, 131, 80). 27 28 29get_compression_method() -> 30 case config:get("couchdb", "file_compression") of 31 undefined -> 32 ?DEFAULT_COMPRESSION; 33 Method1 -> 34 case string:tokens(Method1, "_") of 35 [Method] -> 36 list_to_existing_atom(Method); 37 [Method, Level] -> 38 {list_to_existing_atom(Method), list_to_integer(Level)} 39 end 40 end. 41 42 43compress(<<?SNAPPY_PREFIX, _/binary>> = Bin, snappy) -> 44 Bin; 45compress(<<?SNAPPY_PREFIX, _/binary>> = Bin, Method) -> 46 compress(decompress(Bin), Method); 47compress(<<?COMPRESSED_TERM_PREFIX, _/binary>> = Bin, {deflate, _Level}) -> 48 Bin; 49compress(<<?TERM_PREFIX, _/binary>> = Bin, Method) -> 50 compress(decompress(Bin), Method); 51compress(Term, none) -> 52 ?term_to_bin(Term); 53compress(Term, {deflate, Level}) -> 54 term_to_binary(Term, [{minor_version, 1}, {compressed, Level}]); 55compress(Term, snappy) -> 56 Bin = ?term_to_bin(Term), 57 try 58 {ok, CompressedBin} = snappy:compress(Bin), 59 <<?SNAPPY_PREFIX, CompressedBin/binary>> 60 catch exit:snappy_nif_not_loaded -> 61 Bin 62 end. 63 64 65decompress(<<?SNAPPY_PREFIX, Rest/binary>>) -> 66 {ok, TermBin} = snappy:decompress(Rest), 67 binary_to_term(TermBin); 68decompress(<<?TERM_PREFIX, _/binary>> = Bin) -> 69 binary_to_term(Bin); 70decompress(_) -> 71 error(invalid_compression). 72 73 74is_compressed(<<?SNAPPY_PREFIX, _/binary>>, Method) -> 75 Method =:= snappy; 76is_compressed(<<?COMPRESSED_TERM_PREFIX, _/binary>>, {deflate, _Level}) -> 77 true; 78is_compressed(<<?COMPRESSED_TERM_PREFIX, _/binary>>, _Method) -> 79 false; 80is_compressed(<<?TERM_PREFIX, _/binary>>, Method) -> 81 Method =:= none; 82is_compressed(Term, _Method) when not is_binary(Term) -> 83 false; 84is_compressed(_, _) -> 85 error(invalid_compression). 86 87 88uncompressed_size(<<?SNAPPY_PREFIX, Rest/binary>>) -> 89 {ok, Size} = snappy:uncompressed_length(Rest), 90 Size; 91uncompressed_size(<<?COMPRESSED_TERM_PREFIX, Size:32, _/binary>> = _Bin) -> 92 % See http://erlang.org/doc/apps/erts/erl_ext_dist.html 93 % The uncompressed binary would be encoded with <<131, Rest/binary>> 94 % so need to add 1 for 131 95 Size + 1; 96uncompressed_size(<<?TERM_PREFIX, _/binary>> = Bin) -> 97 byte_size(Bin); 98uncompressed_size(_) -> 99 error(invalid_compression). 100