-
zlib 라이브러리 - compress, uncompress 사용하기zlib 2022. 10. 23. 16:49
강의 주소 : https://youtu.be/2es4zG-8qCE
공부를 글로 남기는 습관을 들이려 내용들을 기록하는 중입니다.
체계적이지 않으며, 전문적이지 않습니다.
의식의 흐름으로 기록합니다.지금까지 zlib를 통해서 다양한 형태의 압축을 해보았습니다.
zlib를 통해서 대용량의 파일을 압축할 수 있구요, 때에 따라서 굉장히 적은 파일을 압축할 수 있습니다.대용량의 파일을 압축할때는 지금까지 배웠던 방식 do-while문을 이용해서 코딩을 작성해야 하는데,
그러지않고 아주 적은 버퍼를 사용하는 경우도 있습니다.예를 들면 이미지 데이터 같은 경우에 대부분 5MB안쪽에 데이터를 가지고 있습니다 //그렇습니까?
이런 경우에서는 다른방법으로 접근하면 때에 따라서 굉장히 편할 것 입니다.적은 버퍼의 데이터를 압축하거나 압축해제하는데 있어서 zlib는 기본함수를 제공하고 있습니다.
zlib source파일들을 보면 compress.c, uncompress.c파일이 있습니다.
compress.c, uncompress.c 이 두 파일내에는 버퍼가 굉장히 적은 파일에 대해서
코드 한 줄, 함수 한 번에 압축/해제 될 수있는 선언이 되어져 있습니다.
관련 함수를 살펴보도록 하겠습니다.compress.c
compress.c 파일을 살펴보면 내부에 3개의 함수가 선언되어 있습니다.
compress2, compress, compressBoundint ZEXPORT compress2(dest, destLen, source, sourceLen, level) Bytef *dest; uLongf *destLen; const Bytef *source; uLong sourceLen; int level; { z_stream stream; int err; const uInt max = (uInt)-1; uLong left; left = *destLen; *destLen = 0; stream.zalloc = (alloc_func)0; stream.zfree = (free_func)0; stream.opaque = (voidpf)0; err = deflateInit(&stream, level); if (err != Z_OK) return err; stream.next_out = dest; stream.avail_out = 0; stream.next_in = (z_const Bytef *)source; stream.avail_in = 0; do { if (stream.avail_out == 0) { stream.avail_out = left > (uLong)max ? max : (uInt)left; left -= stream.avail_out; } if (stream.avail_in == 0) { stream.avail_in = sourceLen > (uLong)max ? max : (uInt)sourceLen; sourceLen -= stream.avail_in; } err = deflate(&stream, sourceLen ? Z_NO_FLUSH : Z_FINISH); } while (err == Z_OK); *destLen = stream.total_out; deflateEnd(&stream); return err == Z_STREAM_END ? Z_OK : err;
int ZEXPORT compress(dest, destLen, source, sourceLen) Bytef *dest; uLongf *destLen; const Bytef *source; uLong sourceLen; { return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION);
uLong ZEXPORT compressBound(sourceLen) uLong sourceLen; { return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + (sourceLen >> 25) + 13; }
compress함수는 내부에서 마지막 인수만 Z_DEFAULT_COMPRESSION(기본압축레벨)로 전달하여
compress2함수를 호출하고 있습니다.요는 compress는 압축레벨을 기본값으로 설정하는 압축함수이고,
compress2는 압축레벨을 지정할 수 있는 압축함수 입니다.compress2함수 내부를 살펴보면 매개변수 목록들로 시작된다.
Bytef *dest : 출력버퍼
uLongf *destLen : 출력버퍼의 길이
const Bytef *source : 입력버퍼
uLong sourceLen : 입력버퍼의 길이
int level : 압축 레벨에 대한 설정
기본적인 압축과정을 진행한다.
deflate( , Z_FINISH) flush모드를 Z_FINISH로 해서 한번에 하겠다는 의미를 가진다.
*destLen = stream.total_out // 출력버퍼의 길이(최종적으로 압축된 데이터의 길이)
deflate( ,Z_FINISH)과정 중 리턴값이 Z_OK가 나오는 경우는 출력버퍼가 부족한 경우이다.
compress,2 함수는 충분한 출력버퍼를 제공해야만 정상적으로 작동하는 함수이다.
(압축한 이후의 데이터의 크기를 알고 있어야 한다)
compress 함수를 사용하기 위해선 입력버퍼에 대해서 얼마만큼의 출력버퍼가 필요한지를 예상해야 하는데,
그 예상할 때 사용하는 함수가 compressBound이다.compressBound함수는 입력으로 사용되는 버퍼의 길이를 제공하면 최대출력 버퍼를 알려준다.
내용을 둘러보면 (소스길이) + (소스길이 >>12 (/ 2^12)) + (소스길이 >> 14 (/ 2^14)) + (소스길이 >> 25 (/ 2^25)) + 13이다
대략적으로 (소스길이 * 2) + 13 정도로 보면 된다.uncompress.c
compress.c 파일을 살펴보면 내부에 2개의 함수가 선언되어 있습니다.
uncompress2, uncompressint ZEXPORT uncompress2(dest, destLen, source, sourceLen) Bytef *dest; uLongf *destLen; const Bytef *source; uLong *sourceLen; { z_stream stream; int err; const uInt max = (uInt)-1; uLong len, left; Byte buf[1]; /* for detection of incomplete stream when *destLen == 0 */ len = *sourceLen; if (*destLen) { left = *destLen; *destLen = 0; } else { left = 1; dest = buf; } stream.next_in = (z_const Bytef *)source; stream.avail_in = 0; stream.zalloc = (alloc_func)0; stream.zfree = (free_func)0; stream.opaque = (voidpf)0; err = inflateInit(&stream); if (err != Z_OK) return err; stream.next_out = dest; stream.avail_out = 0; do { if (stream.avail_out == 0) { stream.avail_out = left > (uLong)max ? max : (uInt)left; left -= stream.avail_out; } if (stream.avail_in == 0) { stream.avail_in = len > (uLong)max ? max : (uInt)len; len -= stream.avail_in; } err = inflate(&stream, Z_NO_FLUSH); } while (err == Z_OK); *sourceLen -= len + stream.avail_in; if (dest != buf) *destLen = stream.total_out; else if (stream.total_out && err == Z_BUF_ERROR) left = 1; inflateEnd(&stream); return err == Z_STREAM_END ? Z_OK : err == Z_NEED_DICT ? Z_DATA_ERROR : err == Z_BUF_ERROR && left + stream.avail_out ? Z_DATA_ERROR : err; }
int ZEXPORT uncompress(dest, destLen, source, sourceLen) Bytef *dest; uLongf *destLen; const Bytef *source; uLong sourceLen; { return uncompress2(dest, destLen, source, &sourceLen); }
uncompress 함수의 내용도 비슷하게 진행이 된다.
이 함수는 compress함수를 통해서 압축된 데이터들만 압축해제 할 수 있다.
uncompress 함수의 고민점은 compress함수의 고민점과 마찬가지로 출력버퍼의 길이의 예상이다.
원본의 데이터의 크기를 대략적으로 알고있어야 하는 조건이 붙는다.이제 코드를 적어보면서 공부해보자.
프로젝트 생성.
zlib 라이브러리 visual studio 연결.
프로젝트와 zlib와 연결을 해주자. zlib 관련 .h 파일들이 있는 폴더를 포함. lib파일이 있는 폴더와 연결 실제 사용하는 파일명을 연결 Debug모드는 파일명이 다르니까 재설정
91song4.tistory.com
샘플을 진행할 때에 compress 함수와 uncompress 함수의 사용을 위해서 원본데이터의 크기를 알고있다고 가정하고,
원본데이터의 최대입력, 최대출력 버퍼를 고정한다.const int BUF = 1024; const int DBUF = BUF * 2 + 13; //compressBound의 대략적 반환값
입력데이터를 생성한다. (입력데이터 < 1024 == 성립)
deflate 출력버퍼를 생성한다.Byte raw_data[] = "안녕하세요."; Byte deflate_data[DBUF];
자료형이 uLong인 입력데이터의 사이즈를 저장한다.
deflate out buffer size를 담을 변수를 설정한다.uLong raw_size = strlen((const char*)raw_data); uLong deflate_size = DBUF;
압축 후 출력을 해본다.
compress(deflate_data, &deflate_size, raw_data, raw_size); std::cout << "Raw Data Size: " << raw_size << std::endl; std::cout << "Deflate Data Size: " << deflate_size << std::endl;
inflate out 데이터를 담을 버퍼를 생성한다.
inflate out buffer size를 담을 변수를 설정한다.Bytef inflate_data[BUF]; uLong inflate_size = BUF;
압축해제 후 출력(deflate size, inflate size, inflate data)
uncompress(inflate_data, &inflate_size, deflate_data, deflate_size) inflate_data[inflate_size] = NULL; std::cout << "Deflate Data Size: " << deflate_size << std::endl; std::cout << "Inflate Data Size: " << inflate_size << std::endl; std::cout << "Inflate Data: " << (const char*)inflate_data << std::endl;
실행해보자.
몇번 더 사용해봐야 익숙해질것 같다.
'zlib' 카테고리의 다른 글
zlib 라이브러리 - zip 파일 간단히 생성해 보기 (0) 2022.10.24 zlib 라이브러리 minizip 추가하기 (0) 2022.10.23 zlib 라이브러리 - block 관련 샘플 (0) 2022.10.23 zlib 라이브러리 - Dictionary 사용하기 (0) 2022.10.23 zlib 라이브러리 - inflate 예제 (0) 2022.10.22