1 /*
2  * compress_lz4.c:  LZ4 data compression routines
3  *
4  * ====================================================================
5  *    Licensed to the Apache Software Foundation (ASF) under one
6  *    or more contributor license agreements.  See the NOTICE file
7  *    distributed with this work for additional information
8  *    regarding copyright ownership.  The ASF licenses this file
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23 
24 #include <assert.h>
25 
26 #include "private/svn_subr_private.h"
27 
28 #include "svn_private_config.h"
29 
30 #ifdef SVN_INTERNAL_LZ4
31 #include "lz4/lz4internal.h"
32 #else
33 #include <lz4.h>
34 #endif
35 
36 svn_error_t *
svn__compress_lz4(const void * data,apr_size_t len,svn_stringbuf_t * out)37 svn__compress_lz4(const void *data, apr_size_t len,
38                   svn_stringbuf_t *out)
39 {
40   apr_size_t hdrlen;
41   unsigned char buf[SVN__MAX_ENCODED_UINT_LEN];
42   unsigned char *p;
43   int compressed_data_len;
44   int max_compressed_data_len;
45 
46   assert(len <= LZ4_MAX_INPUT_SIZE);
47 
48   p = svn__encode_uint(buf, (apr_uint64_t)len);
49   hdrlen = p - buf;
50   max_compressed_data_len = LZ4_compressBound((int)len);
51   svn_stringbuf_setempty(out);
52   svn_stringbuf_ensure(out, max_compressed_data_len + hdrlen);
53   svn_stringbuf_appendbytes(out, (const char *)buf, hdrlen);
54   compressed_data_len = LZ4_compress_default(data, out->data + out->len,
55                                              (int)len, max_compressed_data_len);
56   if (!compressed_data_len)
57     return svn_error_create(SVN_ERR_LZ4_COMPRESSION_FAILED, NULL, NULL);
58 
59   if (compressed_data_len >= (int)len)
60     {
61       /* Compression didn't help :(, just append the original text */
62       svn_stringbuf_appendbytes(out, data, len);
63     }
64   else
65     {
66       out->len += compressed_data_len;
67       out->data[out->len] = 0;
68     }
69 
70   return SVN_NO_ERROR;
71 }
72 
73 svn_error_t *
svn__decompress_lz4(const void * data,apr_size_t len,svn_stringbuf_t * out,apr_size_t limit)74 svn__decompress_lz4(const void *data, apr_size_t len,
75                     svn_stringbuf_t *out,
76                     apr_size_t limit)
77 {
78   apr_size_t hdrlen;
79   int compressed_data_len;
80   int decompressed_data_len;
81   apr_uint64_t u64;
82   const unsigned char *p = data;
83   int rv;
84 
85   assert(len <= INT_MAX);
86   assert(limit <= INT_MAX);
87 
88   /* First thing in the string is the original length.  */
89   p = svn__decode_uint(&u64, p, p + len);
90   if (p == NULL)
91     return svn_error_create(SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA, NULL,
92                             _("Decompression of compressed data failed: "
93                               "no size"));
94   if (u64 > limit)
95     return svn_error_create(SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA, NULL,
96                             _("Decompression of compressed data failed: "
97                               "size too large"));
98   decompressed_data_len = (int)u64;
99   hdrlen = p - (const unsigned char *)data;
100   compressed_data_len = (int)(len - hdrlen);
101 
102   svn_stringbuf_setempty(out);
103   svn_stringbuf_ensure(out, decompressed_data_len);
104 
105   if (compressed_data_len == decompressed_data_len)
106     {
107       /* Data is in the original, uncompressed form. */
108       memcpy(out->data, p, decompressed_data_len);
109     }
110   else
111     {
112       rv = LZ4_decompress_safe((const char *)p, out->data, compressed_data_len,
113                                decompressed_data_len);
114       if (rv < 0)
115         return svn_error_create(SVN_ERR_LZ4_DECOMPRESSION_FAILED, NULL, NULL);
116 
117       if (rv != decompressed_data_len)
118         return svn_error_create(SVN_ERR_SVNDIFF_INVALID_COMPRESSED_DATA,
119                                 NULL,
120                                 _("Size of uncompressed data "
121                                   "does not match stored original length"));
122     }
123 
124   out->data[decompressed_data_len] = 0;
125   out->len = decompressed_data_len;
126 
127   return SVN_NO_ERROR;
128 }
129 
130 const char *
svn_lz4__compiled_version(void)131 svn_lz4__compiled_version(void)
132 {
133   static const char lz4_version_str[] = APR_STRINGIFY(LZ4_VERSION_MAJOR) "." \
134                                         APR_STRINGIFY(LZ4_VERSION_MINOR) "." \
135                                         APR_STRINGIFY(LZ4_VERSION_RELEASE);
136 
137   return lz4_version_str;
138 }
139 
140 int
svn_lz4__runtime_version(void)141 svn_lz4__runtime_version(void)
142 {
143   return LZ4_versionNumber();
144 }
145