Skip to content

Commit 3e14664

Browse files
committed
Improve C API
1 parent f02e50d commit 3e14664

File tree

5 files changed

+266
-152
lines changed

5 files changed

+266
-152
lines changed

src/api/Compressor.cpp

Lines changed: 116 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,19 @@ limitations under the License.
2222
#include "../entropy/EntropyEncoderFactory.hpp"
2323

2424

25-
#ifdef _MSC_VER
25+
// Note stat64/lstat64 are deprecated on MacOS/Linux
26+
// Use _FILE_OFFSET_BITS and stat/lstat instead
27+
28+
#ifdef _WIN32
2629
#define FSTAT _fstat64
2730
#define STAT _stat64
2831
#else
29-
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__) || defined(__MINGW32__)
30-
#define FSTAT fstat
31-
#define STAT stat
32-
#else
33-
#define FSTAT fstat64
34-
#define STAT stat64
35-
#endif
32+
#define _FILE_OFFSET_BITS 64
33+
#define FSTAT fstat
34+
#define STAT stat
3635
#endif
3736

3837

39-
4038
#ifdef _MSC_VER
4139
#include <io.h>
4240
#define FILENO(f) _fileno(f)
@@ -52,30 +50,94 @@ limitations under the License.
5250
using namespace std;
5351
using namespace kanzi;
5452

53+
54+
struct cContext {
55+
kanzi::CompressedOutputStream* pCos;
56+
unsigned int blockSize;
57+
void* fos;
58+
};
59+
60+
5561
namespace kanzi {
5662
// Utility classes to map C FILEs to C++ streams
57-
class ofstreambuf : public streambuf
58-
{
59-
public:
60-
ofstreambuf (int fd) : _fd(fd) { }
63+
class ofstreambuf : public streambuf
64+
{
65+
public:
66+
ofstreambuf(int fd) : _fd(fd), _buffer(65536) {
67+
// Initialize put pointers to the beginning of the buffer
68+
setp(_buffer.data(), _buffer.data() + _buffer.size());
69+
}
6170

62-
private:
63-
int _fd;
71+
virtual ~ofstreambuf() {
72+
sync();
73+
}
6474

75+
protected:
76+
// Called when the buffer is full
6577
virtual int_type overflow(int_type c) {
66-
if (c == EOF)
67-
return EOF;
78+
if (flush() == EOF) return EOF;
6879

69-
char d = char(c);
70-
return (WRITE(_fd, &d, 1) == 1) ? c : EOF;
80+
if (c != EOF) {
81+
*pptr() = char(c);
82+
pbump(1);
83+
}
84+
return c;
7185
}
7286

73-
virtual streamsize xsputn(const char* s, streamsize sz) {
74-
return WRITE(_fd, s, sz);
87+
// Called for explicit sync/flush
88+
virtual int sync() {
89+
return (flush() == EOF) ? -1 : 0;
7590
}
7691

77-
int sync() {
78-
// No internal buffer — nothing to flush. Indicate success
92+
// Optimized block write
93+
virtual streamsize xsputn(const char* s, streamsize n) {
94+
streamsize remaining = n;
95+
const char* src = s;
96+
97+
while (remaining > 0) {
98+
streamsize avail = epptr() - pptr();
99+
100+
if (avail >= remaining) {
101+
// Fits in current buffer
102+
memcpy(pptr(), src, remaining);
103+
pbump(int(remaining));
104+
return n;
105+
}
106+
107+
if (avail > 0) {
108+
// Fill the rest of the buffer
109+
memcpy(pptr(), src, avail);
110+
pbump(int(avail));
111+
src += avail;
112+
remaining -= avail;
113+
}
114+
115+
// Flush full buffer
116+
if (flush() == EOF) return n - remaining;
117+
118+
// If the remaining chunk is large, write directly to FD to avoid double copy
119+
if (remaining >= streamsize(_buffer.size())) {
120+
if (WRITE(_fd, src, remaining) != remaining) {
121+
return n - remaining; // Error
122+
}
123+
124+
remaining = 0;
125+
}
126+
}
127+
128+
return n;
129+
}
130+
131+
private:
132+
int _fd;
133+
std::vector<char> _buffer;
134+
135+
int flush() {
136+
ptrdiff_t n = pptr() - pbase();
137+
if (n > 0) {
138+
if (WRITE(_fd, pbase(), n) != n) return EOF;
139+
pbump(-int(n)); // Reset pbump by subtracting the amount written
140+
}
79141
return 0;
80142
}
81143
};
@@ -94,7 +156,7 @@ namespace kanzi {
94156

95157

96158
// Create internal cContext and CompressedOutputStream
97-
int CDECL initCompressor(struct cData* pData, FILE* dst, struct cContext** pCtx)
159+
ARCHIVER_API int CDECL initCompressor(struct cData* pData, FILE* dst, struct cContext** pCtx)
98160
{
99161
if ((pData == nullptr) || (pCtx == nullptr) || (dst == nullptr))
100162
return Error::ERR_INVALID_PARAM;
@@ -112,19 +174,24 @@ int CDECL initCompressor(struct cData* pData, FILE* dst, struct cContext** pCtx)
112174
string transform = TransformFactory<byte>::getName(TransformFactory<byte>::getType(pData->transform));
113175
string entropy = EntropyEncoderFactory::getName(EntropyEncoderFactory::getType(pData->entropy));
114176

115-
if ((transform.length() >= 63) || (entropy.length() >= 15))
177+
if ((transform.length() >= sizeof(pData->transform)) ||
178+
(entropy.length() >= sizeof(pData->entropy))) {
116179
return Error::ERR_INVALID_PARAM;
180+
}
181+
182+
memset(pData->transform, 0, sizeof(pData->transform));
183+
strncpy(pData->transform, transform.c_str(), sizeof(pData->transform) - 1);
184+
memset(pData->entropy, 0, sizeof(pData->entropy));
185+
strncpy(pData->entropy, entropy.c_str(), sizeof(pData->entropy) - 1);
117186

118-
snprintf(pData->transform, sizeof(pData->transform), "%s", transform.c_str());
119-
snprintf(pData->entropy, sizeof(pData->entropy), "%s", entropy.c_str());
120187
pData->blockSize = (pData->blockSize + 15) & -16;
121188

122189
*pCtx = nullptr;
123-
uint64 fileSize = 0;
190+
size_t fileSize = 0;
124191
struct STAT sbuf;
125192

126193
if (FSTAT(fd, &sbuf) == 0) {
127-
fileSize = uint64(sbuf.st_size);
194+
fileSize = sbuf.st_size;
128195
}
129196

130197
// Create compression stream and update context
@@ -134,7 +201,7 @@ int CDECL initCompressor(struct cData* pData, FILE* dst, struct cContext** pCtx)
134201
cctx->pCos = new CompressedOutputStream(*fos, pData->jobs,
135202
pData->entropy, pData->transform,
136203
pData->blockSize, pData->checksum,
137-
fileSize,
204+
uint64(fileSize),
138205
#ifdef CONCURRENCY_ENABLED
139206
nullptr,
140207
#endif
@@ -157,72 +224,71 @@ int CDECL initCompressor(struct cData* pData, FILE* dst, struct cContext** pCtx)
157224
return 0;
158225
}
159226

160-
int CDECL compress(struct cContext* pCtx, const unsigned char* src, int* inSize, int* outSize)
227+
ARCHIVER_API int CDECL compress(struct cContext* pCtx, const unsigned char* src, size_t inSize, size_t* outSize)
161228
{
162-
if ((pCtx == nullptr) || (inSize == nullptr) || (outSize == nullptr) ||
163-
(*inSize < 0) || (*inSize > int(pCtx->blockSize))) {
229+
if ((pCtx == nullptr) || (outSize == nullptr)) {
230+
return Error::ERR_INVALID_PARAM;
231+
}
232+
233+
if (inSize > size_t(pCtx->blockSize)) {
164234
return Error::ERR_INVALID_PARAM;
165235
}
166236

167237
*outSize = 0;
168238
int res = 0;
169-
CompressedOutputStream* pCos = static_cast<CompressedOutputStream*>(pCtx->pCos);
239+
CompressedOutputStream* pCos = pCtx->pCos;
170240

171241
if (pCos == nullptr) {
172-
*outSize = 0;
173242
return Error::ERR_INVALID_PARAM;
174243
}
175244

176245
try {
177246
const uint64 w = pCos->getWritten();
178-
pCos->write((const char*)src, streamsize(*inSize));
247+
pCos->write((const char*)src, streamsize(inSize));
179248
res = pCos->good() ? 0 : Error::ERR_WRITE_FILE;
180249
*outSize = int(pCos->getWritten() - w);
181250
}
182251
catch (const exception&) {
183-
*outSize = 0;
184252
return Error::ERR_UNKNOWN;
185253
}
186254

187255
return res;
188256
}
189257

190258
// Cleanup allocated internal data structures
191-
int CDECL disposeCompressor(struct cContext* pCtx, int* outSize)
259+
ARCHIVER_API int CDECL disposeCompressor(struct cContext** ppCtx, size_t* outSize)
192260
{
193261
*outSize = 0;
194262

195-
if (pCtx == nullptr)
263+
if ((ppCtx == nullptr) || (*ppCtx == nullptr))
196264
return Error::ERR_INVALID_PARAM;
197265

198-
CompressedOutputStream* pCos = (CompressedOutputStream*)pCtx->pCos;
266+
cContext* pCtx = *ppCtx;
267+
CompressedOutputStream* pCos = pCtx->pCos;
199268

200269
try {
201270
if (pCos != nullptr) {
202271
const uint64 w = pCos->getWritten();
203272
pCos->close();
204-
*outSize = int(pCos->getWritten() - w);
205-
}
206-
}
207-
catch (const exception&) {
208-
return Error::ERR_UNKNOWN;
209-
}
210273

211-
try {
212-
if (pCos != nullptr)
274+
if (outSize)
275+
*outSize = int(pCos->getWritten() - w);
276+
213277
delete pCos;
278+
}
214279

215280
if (pCtx->fos != nullptr)
216-
delete (FileOutputStream*)pCtx->fos;
281+
delete static_cast<FileOutputStream*>(pCtx->fos);
217282

218283
pCtx->fos = nullptr;
219284
delete pCtx;
220285
}
221286
catch (const exception&) {
222287
if (pCtx->fos != nullptr)
223-
delete (FileOutputStream*)pCtx->fos;
288+
delete static_cast<FileOutputStream*>(pCtx->fos);
224289

225290
delete pCtx;
291+
*ppCtx = 0;
226292
return Error::ERR_UNKNOWN;
227293
}
228294

src/api/Compressor.hpp

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,63 +19,77 @@ limitations under the License.
1919

2020
#ifdef _WIN32
2121
#define CDECL __cdecl
22+
23+
#ifdef ARCHIVER_EXPORTS
24+
#define ARCHIVER_API __declspec(dllexport)
25+
#else
26+
#define ARCHIVER_API __declspec(dllimport)
27+
#endif
2228
#else
2329
#define CDECL
30+
#define ARCHIVER_API
2431
#endif
2532

2633
#include <stdio.h>
2734

2835

36+
#define ARCHIVER_VERSION_STRING "1.0.0"
37+
38+
2939
#ifdef __cplusplus
3040
extern "C" {
3141
#endif
3242

43+
/**
44+
* Compression context: encapsulates compressor state (opaque: could change in future versions)
45+
*/
46+
struct cContext;
47+
48+
3349
/**
3450
* Compression parameters
3551
*/
3652
struct cData {
3753
char transform[64]; /* name of transforms [None|PACK|BWT|BWTS|LZ|LZX|LZP|ROLZ|ROLZX]
3854
[RLT|ZRLT|MTFT|RANK|SRT|TEXT|MM|EXE|UTF|DNA] */
3955
char entropy[16]; /* name of entropy codec [None|Huffman|ANS0|ANS1|Range|FPAQ|TPAQ|TPAQX|CM] */
40-
unsigned int blockSize; /* size of block in bytes */
56+
size_t blockSize; /* size of block in bytes */
4157
unsigned int jobs; /* max number of concurrent tasks */
4258
int checksum; /* 0, 32 or 64 to indicate size of block checksum */
4359
int headerless; /* bool to indicate if the bitstream has a header (usually set to 0) */
4460
};
4561

62+
4663
/**
47-
* Compression context: encapsulates compressor state (opaque: could change in future versions)
64+
* @return the version number of the library.
65+
* Useful for checking for compatibility at runtime.
4866
*/
49-
struct cContext {
50-
void* pCos;
51-
unsigned int blockSize;
52-
void* fos;
53-
};
67+
ARCHIVER_API unsigned int CDECL Archiver_GetVersion(void);
5468

5569

56-
/**
70+
/**
5771
* Initialize the compressor internal states.
5872
*
5973
* @param cParam [IN|OUT] - the compression parameters, transform and enropy are validated and rewritten
6074
* @param dst [IN] - the destination stream of compressed data
6175
* @param ctx [IN|OUT] - pointer to the compression context created by the call
6276
*
63-
* @return 0 in case of success
77+
* @return 0 in case of success, else see error code in Error.hpp
6478
*/
65-
int CDECL initCompressor(struct cData* cParam, FILE* dst, struct cContext** ctx);
79+
ARCHIVER_API int CDECL initCompressor(struct cData* cParam, FILE* dst, struct cContext** ctx);
6680

6781
/**
6882
* Compress a block of data. The compressor must have been initialized.
6983
*
7084
* @param ctx [IN] - the compression context created during initialization
7185
* @param src [IN] - the source block of data to compress
72-
* @param inSize [IN|OUT] - the size of the source block to compress.
73-
Updated to reflect the number bytes written to the destination.
74-
* @param outSize [OUT] - the size of the compressed data
86+
* @param inSize [IN] - the size of the source block to compress.
87+
* @param outSize [IN|OUT] - the size of the compressed data
88+
Updated to reflect the number bytes written to the destination.
7589
*
76-
* @return 0 in case of success
90+
* @return 0 in case of success, else see error code in Error.hpp
7791
*/
78-
int CDECL compress(struct cContext* ctx, const unsigned char* src, int* inSize, int* outSize);
92+
ARCHIVER_API int CDECL compress(struct cContext* ctx, const unsigned char* src, size_t inSize, size_t* outSize);
7993

8094
/**
8195
* Dispose the compressor and cleanup memory resources.
@@ -84,9 +98,9 @@ limitations under the License.
8498
* @param outSize [IN|OUT] - the number of bytes written to the destination
8599
* (the compressor may flush internal data)
86100
*
87-
* @return 0 in case of success
101+
* @return 0 in case of success, else see error code in Error.hpp
88102
*/
89-
int CDECL disposeCompressor(struct cContext* ctx, int* outSize);
103+
ARCHIVER_API int CDECL disposeCompressor(struct cContext** ctx, size_t* outSize);
90104

91105
#ifdef __cplusplus
92106
}

0 commit comments

Comments
 (0)