Skip to content

Commit 9c80091

Browse files
committed
Fix Android build.
Add Brotli support to Compression, IOStreamInflate and HTTP client.
1 parent 7d09138 commit 9c80091

6 files changed

Lines changed: 257 additions & 15 deletions

File tree

include/eepp/system/compression.hpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace EE { namespace System {
99

1010
class EE_API Compression {
1111
public:
12-
enum Mode { MODE_DEFLATE, MODE_GZIP };
12+
enum Mode { MODE_DEFLATE, MODE_GZIP, MODE_BROTLI };
1313

1414
enum Status {
1515
OK = 0,
@@ -29,10 +29,16 @@ class EE_API Compression {
2929
int level = -1;
3030
};
3131

32+
struct BrotliConfig {
33+
int quality = -1;
34+
int windowBits = -1;
35+
};
36+
3237
struct Config {
3338
Config() {}
3439
ZlibConfig zlib;
3540
GzipConfig gzip;
41+
BrotliConfig brotli;
3642
};
3743

3844
static Status compress( Uint8* dst, Uint64 dstMaxSize, const Uint8* src, Uint64 srcSize,

projects/android-project/app/jni/eepp.mk

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ EEPP_C_INCLUDES := \
2828
$(EEPP_THIRD_PARTY_PATH)/oniguruma \
2929
$(EEPP_THIRD_PARTY_PATH)/SheenBidi/Headers \
3030
$(EEPP_THIRD_PARTY_PATH)/efsw/include \
31+
$(EEPP_THIRD_PARTY_PATH)/brotli/include \
3132
$(EEPP_BASE_PATH)/modules/eterm/include \
3233
$(EEPP_BASE_PATH)/modules/eterm/src \
3334
$(EEPP_BASE_PATH)/modules/maps/include \
@@ -111,7 +112,7 @@ LOCAL_C_INCLUDES := $(EEPP_C_INCLUDES)
111112

112113
LOCAL_SRC_FILES := $(foreach F, $(CODE_SRCS), $(addprefix $(dir $(F)),$(notdir $(wildcard $(LOCAL_PATH)/$(F)))))
113114

114-
LOCAL_STATIC_LIBRARIES := freetype libpng libwebp md4c pcre2 oniguruma harfbuzz sheenbidi gumbo-parser
115+
LOCAL_STATIC_LIBRARIES := freetype libpng libwebp md4c pcre2 oniguruma harfbuzz sheenbidi gumbo-parser brotli
115116

116117
LOCAL_SHARED_LIBRARIES := SDL2
117118

@@ -152,7 +153,7 @@ LOCAL_MODULE := freetype
152153

153154
APP_SUBDIRS := $(patsubst $(LOCAL_PATH)/%, %, $(shell find $(LOCAL_PATH)/src -type d))
154155

155-
LOCAL_C_INCLUDES := $(foreach D, $(APP_SUBDIRS), $(LOCAL_PATH)/$(D)) $(LOCAL_PATH)/include $(EEPP_THIRD_PARTY_PATH)/libpng
156+
LOCAL_C_INCLUDES := $(foreach D, $(APP_SUBDIRS), $(LOCAL_PATH)/$(D)) $(LOCAL_PATH)/include $(EEPP_THIRD_PARTY_PATH)/libpng $(EEPP_THIRD_PARTY_PATH)/brotli/include
156157
LOCAL_CFLAGS := -Os -DFT2_BUILD_LIBRARY
157158

158159
LOCAL_SRC_FILES += $(foreach F, $(APP_SUBDIRS), $(addprefix $(F)/,$(notdir $(wildcard $(LOCAL_PATH)/$(F)/*.c))))
@@ -384,6 +385,23 @@ LOCAL_SRC_FILES := $(foreach F, $(GUMBOPARSER_SRCS), $(addprefix $(dir $(F)),$
384385
include $(BUILD_STATIC_LIBRARY)
385386
#*************** GUMBOPARSER ***************
386387

388+
#*************** BROTLI ***************
389+
include $(CLEAR_VARS)
390+
391+
LOCAL_PATH := $(EEPP_THIRD_PARTY_PATH)
392+
393+
LOCAL_MODULE := brotli
394+
395+
BROTLI_SRCS := brotli/common/*.c brotli/dec/*.c
396+
397+
LOCAL_C_INCLUDES := $(LOCAL_PATH)/brotli $(LOCAL_PATH)/brotli/include
398+
LOCAL_CFLAGS := -Os
399+
400+
LOCAL_SRC_FILES := $(foreach F, $(BROTLI_SRCS), $(addprefix $(dir $(F)),$(notdir $(wildcard $(LOCAL_PATH)/$(F)))))
401+
402+
include $(BUILD_STATIC_LIBRARY)
403+
#*************** BROTLI ***************
404+
387405
#*************** MD4C ***************
388406
include $(CLEAR_VARS)
389407

src/eepp/network/http.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,12 +1028,15 @@ Http::Response Http::downloadRequest( const Http::Request& request, IOStream& wr
10281028

10291029
// Check if the content is compressed
10301030
std::string encoding( received.getField( "content-encoding" ) );
1031-
compressed = encoding == "gzip" || encoding == "deflate";
1031+
compressed = encoding == "gzip" || encoding == "deflate" ||
1032+
encoding == "br";
10321033

10331034
if ( compressed ) {
10341035
Compression::Mode compressionMode =
1035-
"gzip" == encoding ? Compression::MODE_GZIP
1036-
: Compression::MODE_DEFLATE;
1036+
"gzip" == encoding
1037+
? Compression::MODE_GZIP
1038+
: ( "br" == encoding ? Compression::MODE_BROTLI
1039+
: Compression::MODE_DEFLATE );
10371040

10381041
inflateStream =
10391042
IOStreamInflate::New( writeTo, compressionMode );
@@ -1339,7 +1342,7 @@ Http::Request Http::prepareFields( const Http::Request& request ) {
13391342
}
13401343

13411344
if ( request.isCompressedResponse() )
1342-
toSend.setField( "Accept-Encoding", "gzip, deflate" );
1345+
toSend.setField( "Accept-Encoding", "gzip, deflate, br" );
13431346

13441347
return toSend;
13451348
}

src/eepp/system/compression.cpp

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
#include <eepp/system/iostreammemory.hpp>
44
#include <eepp/system/scopedbuffer.hpp>
55

6+
#include <brotli/decode.h>
7+
// eepp only brings decoder implementation, encoding won't be available for the moment
8+
// #include <brotli/encode.h>
69
#include <zlib.h>
710

811
#define DEFLATE_CHUNK_SIZE ( 16384 )
@@ -19,6 +22,62 @@ Compression::Status Compression::compress( Uint8* dst, Uint64 dstMaxSize, const
1922
Compression::Status Compression::compress( IOStream& dst, IOStream& src, Compression::Mode mode,
2023
const Config& config ) {
2124
switch ( mode ) {
25+
case MODE_BROTLI: {
26+
/*
27+
BrotliEncoderState* state = BrotliEncoderCreateInstance( nullptr, nullptr, nullptr );
28+
if ( !state )
29+
return Status::MEM_ERROR;
30+
31+
int quality =
32+
config.brotli.quality == -1 ? BROTLI_DEFAULT_QUALITY : config.brotli.quality;
33+
int windowBits =
34+
config.brotli.windowBits == -1 ? BROTLI_DEFAULT_WINDOW : config.brotli.windowBits;
35+
36+
BrotliEncoderSetParameter( state, BROTLI_PARAM_QUALITY, quality );
37+
BrotliEncoderSetParameter( state, BROTLI_PARAM_LGWIN, windowBits );
38+
39+
src.seek( 0 );
40+
41+
char in[DEFLATE_CHUNK_SIZE];
42+
char out[DEFLATE_CHUNK_SIZE];
43+
44+
bool isEof = false;
45+
46+
while ( !isEof ) {
47+
size_t bytesRead = src.read( in, DEFLATE_CHUNK_SIZE );
48+
isEof = src.tell() == src.getSize();
49+
50+
const uint8_t* next_in = reinterpret_cast<const uint8_t*>( in );
51+
size_t avail_in = bytesRead;
52+
53+
while ( avail_in > 0 || ( isEof && !BrotliEncoderIsFinished( state ) ) ) {
54+
uint8_t* next_out = reinterpret_cast<uint8_t*>( out );
55+
size_t avail_out = DEFLATE_CHUNK_SIZE;
56+
57+
BrotliEncoderOperation op =
58+
isEof ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS;
59+
60+
if ( !BrotliEncoderCompressStream( state, op, &avail_in, &next_in, &avail_out,
61+
&next_out, nullptr ) ) {
62+
BrotliEncoderDestroyInstance( state );
63+
return Status::STREAM_ERROR;
64+
}
65+
66+
size_t have = DEFLATE_CHUNK_SIZE - avail_out;
67+
if ( have > 0 ) {
68+
if ( dst.write( out, have ) != (ios_size)have ) {
69+
BrotliEncoderDestroyInstance( state );
70+
return Status::ERRNO;
71+
}
72+
}
73+
}
74+
}
75+
76+
BrotliEncoderDestroyInstance( state );
77+
return Status::OK;
78+
*/
79+
return Status::VERSION_ERROR;
80+
}
2281
case MODE_DEFLATE:
2382
case MODE_GZIP: {
2483
int ret, flush;
@@ -74,6 +133,10 @@ Compression::Status Compression::compress( IOStream& dst, IOStream& src, Compres
74133

75134
int Compression::getMaxCompressedBufferSize( Uint64 srcSize, Mode mode, const Config& ) {
76135
switch ( mode ) {
136+
case MODE_BROTLI: {
137+
// return BrotliEncoderMaxCompressedSize( srcSize );
138+
break;
139+
}
77140
case MODE_DEFLATE:
78141
case MODE_GZIP: {
79142
int windowBits = mode == MODE_DEFLATE ? MAX_WBITS : MAX_WBITS | 16;
@@ -101,6 +164,61 @@ Compression::Status Compression::decompress( Uint8* dst, Uint64 dstMaxSize, cons
101164

102165
Compression::Status Compression::decompress( IOStream& dst, IOStream& src, Mode mode ) {
103166
switch ( mode ) {
167+
case MODE_BROTLI: {
168+
BrotliDecoderState* state = BrotliDecoderCreateInstance( nullptr, nullptr, nullptr );
169+
if ( !state )
170+
return Status::MEM_ERROR;
171+
172+
ScopedBuffer buffer( DEFLATE_CHUNK_SIZE );
173+
ScopedBuffer bufferDst( DEFLATE_CHUNK_SIZE );
174+
175+
src.seek( 0 );
176+
177+
BrotliDecoderResult result = BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;
178+
size_t totalSize = src.getSize();
179+
size_t totalRead = 0;
180+
181+
while ( totalRead < totalSize || result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT ) {
182+
size_t bytesRead = 0;
183+
if ( result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT ) {
184+
bytesRead = src.read( (char*)buffer.get(), buffer.length() );
185+
totalRead += bytesRead;
186+
}
187+
188+
const uint8_t* next_in = reinterpret_cast<const uint8_t*>( buffer.get() );
189+
size_t avail_in = bytesRead;
190+
191+
while ( avail_in > 0 || result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT ) {
192+
uint8_t* next_out = reinterpret_cast<uint8_t*>( bufferDst.get() );
193+
size_t avail_out = bufferDst.length();
194+
195+
result = BrotliDecoderDecompressStream( state, &avail_in, &next_in, &avail_out,
196+
&next_out, nullptr );
197+
198+
if ( result == BROTLI_DECODER_RESULT_ERROR ) {
199+
BrotliDecoderDestroyInstance( state );
200+
return Status::DATA_ERROR;
201+
}
202+
203+
size_t have = bufferDst.length() - avail_out;
204+
if ( have > 0 ) {
205+
dst.write( (const char*)bufferDst.get(), have );
206+
}
207+
208+
if ( result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT ||
209+
result == BROTLI_DECODER_RESULT_SUCCESS ) {
210+
break;
211+
}
212+
}
213+
214+
if ( result == BROTLI_DECODER_RESULT_SUCCESS ) {
215+
break;
216+
}
217+
}
218+
219+
BrotliDecoderDestroyInstance( state );
220+
return Status::OK;
221+
}
104222
case MODE_DEFLATE:
105223
case MODE_GZIP: {
106224
ScopedBuffer buffer( DEFLATE_CHUNK_SIZE );

src/eepp/system/iostreaminflate.cpp

Lines changed: 95 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
#include <eepp/system/iostreaminflate.hpp>
22

3+
#include <brotli/decode.h>
34
#include <zlib.h>
45

56
namespace EE { namespace System {
67

78
struct LocalStreamData {
89
z_stream strm;
910
int state;
11+
BrotliDecoderState* brotliState;
12+
BrotliDecoderResult brotliResult;
13+
size_t brotliAvailIn;
14+
const uint8_t* brotliNextIn;
1015
};
1116

1217
IOStreamInflate* IOStreamInflate::New( IOStream& inOutStream, Compression::Mode mode ) {
@@ -18,23 +23,70 @@ IOStreamInflate::IOStreamInflate( IOStream& inOutStream, Compression::Mode mode
1823
mMode( mode ),
1924
mBuffer( Compression::getModeDefaultChunkSize( mode ) ),
2025
mLocalStream( eeNew( LocalStreamData, () ) ) {
21-
int windowBits = mode == Compression::MODE_DEFLATE ? MAX_WBITS : MAX_WBITS | 16;
2226

23-
mLocalStream->strm = z_stream{};
24-
25-
mLocalStream->state = inflateInit2( &mLocalStream->strm, windowBits );
27+
if ( mode == Compression::MODE_BROTLI ) {
28+
mLocalStream->brotliState = BrotliDecoderCreateInstance( nullptr, nullptr, nullptr );
29+
mLocalStream->brotliResult = BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;
30+
mLocalStream->brotliAvailIn = 0;
31+
mLocalStream->brotliNextIn = nullptr;
32+
mLocalStream->state = mLocalStream->brotliState ? Z_OK : Z_MEM_ERROR;
33+
} else {
34+
int windowBits = mode == Compression::MODE_DEFLATE ? MAX_WBITS : MAX_WBITS | 16;
35+
mLocalStream->strm = z_stream{};
36+
mLocalStream->state = inflateInit2( &mLocalStream->strm, windowBits );
37+
}
2638
}
2739

2840
IOStreamInflate::~IOStreamInflate() {
29-
inflateEnd( &mLocalStream->strm );
41+
if ( mMode == Compression::MODE_BROTLI ) {
42+
if ( mLocalStream->brotliState )
43+
BrotliDecoderDestroyInstance( mLocalStream->brotliState );
44+
} else {
45+
inflateEnd( &mLocalStream->strm );
46+
}
3047

3148
eeSAFE_DELETE( mLocalStream );
3249
}
3350

3451
ios_size IOStreamInflate::read( char* buffer, ios_size length ) {
35-
if ( mLocalStream->state != Z_OK || !mStream.isOpen() )
52+
if ( mLocalStream->state != Z_OK || !mStream.isOpen() || length == 0 )
3653
return 0;
3754

55+
if ( mMode == Compression::MODE_BROTLI ) {
56+
size_t avail_out = length;
57+
uint8_t* next_out = reinterpret_cast<uint8_t*>( buffer );
58+
59+
while ( avail_out > 0 ) {
60+
if ( mLocalStream->brotliAvailIn == 0 &&
61+
mLocalStream->brotliResult != BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT ) {
62+
ios_size n = 0;
63+
if ( mStream.isOpen() ) {
64+
n = mStream.read( (char*)mBuffer.get(), mBuffer.length() );
65+
}
66+
if ( n == 0 )
67+
break;
68+
mLocalStream->brotliAvailIn = n;
69+
mLocalStream->brotliNextIn = reinterpret_cast<const uint8_t*>( mBuffer.get() );
70+
}
71+
72+
mLocalStream->brotliResult = BrotliDecoderDecompressStream(
73+
mLocalStream->brotliState, &mLocalStream->brotliAvailIn,
74+
&mLocalStream->brotliNextIn, &avail_out, &next_out, nullptr );
75+
76+
if ( mLocalStream->brotliResult == BROTLI_DECODER_RESULT_ERROR ) {
77+
mLocalStream->state = Z_DATA_ERROR;
78+
return 0;
79+
}
80+
81+
if ( mLocalStream->brotliResult == BROTLI_DECODER_RESULT_SUCCESS ) {
82+
mLocalStream->state = Z_STREAM_END;
83+
break;
84+
}
85+
}
86+
87+
return length - avail_out;
88+
}
89+
3890
z_stream& zstr = mLocalStream->strm;
3991

4092
if ( zstr.avail_in == 0 ) {
@@ -94,6 +146,43 @@ ios_size IOStreamInflate::write( const char* buffer, ios_size length ) {
94146
if ( mLocalStream->state != Z_OK || !mStream.isOpen() || length == 0 )
95147
return 0;
96148

149+
if ( mMode == Compression::MODE_BROTLI ) {
150+
size_t avail_in = length;
151+
const uint8_t* next_in = reinterpret_cast<const uint8_t*>( buffer );
152+
153+
while ( avail_in > 0 ||
154+
mLocalStream->brotliResult == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT ) {
155+
size_t avail_out = mBuffer.length();
156+
uint8_t* next_out = reinterpret_cast<uint8_t*>( mBuffer.get() );
157+
158+
mLocalStream->brotliResult = BrotliDecoderDecompressStream(
159+
mLocalStream->brotliState, &avail_in, &next_in, &avail_out, &next_out, nullptr );
160+
161+
if ( mLocalStream->brotliResult == BROTLI_DECODER_RESULT_ERROR ) {
162+
mLocalStream->state = Z_DATA_ERROR;
163+
return 0;
164+
}
165+
166+
size_t have = mBuffer.length() - avail_out;
167+
if ( have > 0 ) {
168+
if ( mStream.write( (const char*)mBuffer.get(), have ) != (ios_size)have ) {
169+
return 0;
170+
}
171+
}
172+
173+
if ( mLocalStream->brotliResult == BROTLI_DECODER_RESULT_SUCCESS ) {
174+
mLocalStream->state = Z_STREAM_END;
175+
break;
176+
}
177+
178+
if ( mLocalStream->brotliResult == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT ) {
179+
break;
180+
}
181+
}
182+
183+
return length;
184+
}
185+
97186
z_stream& zstr = mLocalStream->strm;
98187

99188
zstr.next_in = (unsigned char*)buffer;

0 commit comments

Comments
 (0)