summaryrefslogtreecommitdiff
path: root/src/lib/emile/emile_compress.c
blob: e4b2570675f6cea9e5a3c5043ebdaf569535d687 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <zlib.h>

#ifdef ENABLE_LIBLZ4
#include <lz4.h>
#include <lz4hc.h>
#else
#include "lz4.h"
#include "lz4hc.h"
#endif

#include <Eina.h>

#include "Emile.h"

static int
_emile_compress_buffer_size(const Eina_Binbuf *data, Emile_Compressor_Type t)
{
   switch (t)
     {
      case EMILE_ZLIB:
        return 12 + ((eina_binbuf_length_get(data) * 101) / 100);

      case EMILE_LZ4:
      case EMILE_LZ4HC:
        return LZ4_compressBound(eina_binbuf_length_get(data));

      default:
        return -1;
     }
}

EMILE_API Eina_Binbuf *
emile_compress(const Eina_Binbuf *data,
               Emile_Compressor_Type t,
               Emile_Compressor_Level l)
{
   void *compact, *temp;
   int length;
   int level = l;
   Eina_Bool ok = EINA_FALSE;

   length = _emile_compress_buffer_size(data, t);

   compact = malloc(length);
   if (!compact)
     return NULL;

   switch (t)
     {
      case EMILE_LZ4:
        length = LZ4_compress_default
          ((const char *)eina_binbuf_string_get(data), compact,
           eina_binbuf_length_get(data), length);
        /* It is going to be smaller and should never fail, if it does you are in deep poo. */
        temp = realloc(compact, length);
        if (temp) compact = temp;

        if (length > 0)
          ok = EINA_TRUE;
        break;

      case EMILE_LZ4HC:
        length = LZ4_compress_HC
          ((const char *)eina_binbuf_string_get(data), compact,
           eina_binbuf_length_get(data), length, 16);
        temp = realloc(compact, length);
        if (temp) compact = temp;

        if (length > 0)
          ok = EINA_TRUE;
        break;

      case EMILE_ZLIB:
      {
         uLongf buflen = (uLongf)length;

         if (compress2((Bytef *)compact, &buflen, (Bytef *)eina_binbuf_string_get(data), (uLong)eina_binbuf_length_get(data), level) == Z_OK)
           ok = EINA_TRUE;
         length = (int)buflen;
      }
     }

   if (!ok)
     {
        free(compact);
        return NULL;
     }

   return eina_binbuf_manage_new(compact, length, EINA_FALSE);
}

EMILE_API Eina_Bool
emile_expand(const Eina_Binbuf *in, Eina_Binbuf *out, Emile_Compressor_Type t)
{
   if (!in || !out)
     return EINA_FALSE;

   switch (t)
     {
      case EMILE_LZ4:
      case EMILE_LZ4HC:
      {
         int ret;

         ret = LZ4_decompress_safe((const char *)eina_binbuf_string_get(in),
                                   (char *)eina_binbuf_string_get(out),
                                   eina_binbuf_length_get(in),
                                   eina_binbuf_length_get(out));
         if ((unsigned int)ret != eina_binbuf_length_get(out))
           return EINA_FALSE;
         break;
      }

      case EMILE_ZLIB:
      {
         uLongf dlen = eina_binbuf_length_get(out);

         if (uncompress((Bytef *)eina_binbuf_string_get(out), &dlen, eina_binbuf_string_get(in), (uLongf)eina_binbuf_length_get(in)) != Z_OK)
           return EINA_FALSE;
         break;
      }

      default:
        return EINA_FALSE;
     }

   return EINA_TRUE;
}

EMILE_API Eina_Binbuf *
emile_decompress(const Eina_Binbuf *data,
                 Emile_Compressor_Type t,
                 unsigned int dest_length)
{
   Eina_Binbuf *out;
   void *expanded;

   expanded = malloc(dest_length);
   if (!expanded)
     return NULL;

   out = eina_binbuf_manage_new(expanded, dest_length, EINA_FALSE);
   if (!out)
     goto on_error;

   if (!emile_expand(data, out, t))
     goto on_error;

   return out;

on_error:
   if (!out)
     free(expanded);
   if (out)
     eina_binbuf_free(out);
   return NULL;
}