summaryrefslogtreecommitdiff
path: root/src/lib/emile/emile_compress.c
blob: 8c3cb9011a57f49a4c0e49b815b40b220a1dd7d7 (plain) (blame)
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
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif /* ifdef HAVE_CONFIG_H */

#include <zlib.h>

#include <Eina.h>
#include "Emile.h"

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

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;
     }
}

EAPI Eina_Binbuf *
emile_binbuf_compress(const Eina_Binbuf *data, Emile_Compressor_Type t, int level)
{
   void *compact;
   int length;
   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((const char *) eina_binbuf_string_get(data), compact, eina_binbuf_length_get(data));
         if (length > 0) ok = EINA_TRUE;
         compact = realloc(compact, length); // It is going to be smaller and should never fail, if it does you are in deep poo.
         break;
      case EMILE_LZ4HC:
         length = LZ4_compressHC((const char *) eina_binbuf_string_get(data), compact, eina_binbuf_length_get(data));
         if (length > 0) ok = EINA_TRUE;
         compact = realloc(compact, length);
         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_length(compact, length);
}

EAPI Eina_Bool
emile_binbuf_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_fast((const char*) eina_binbuf_string_get(in),
                                     (char*) eina_binbuf_string_get(out),
                                     eina_binbuf_length_get(out));
           if ((unsigned int) ret != eina_binbuf_length_get(in))
             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;
}

EAPI Eina_Binbuf *
emile_binbuf_uncompress(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_length(expanded, dest_length);
   if (!out) goto on_error;

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

   return out;

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