1
0
mirror of https://github.com/Ylianst/MeshAgent synced 2025-12-16 00:03:45 +00:00

Added support for compressed db entries

This commit is contained in:
Bryan Roe
2020-06-22 19:10:54 -07:00
parent 660777406e
commit e82255959a
3 changed files with 301 additions and 6 deletions

View File

@@ -326,15 +326,186 @@ duk_ret_t ILibDuktape_CompressedStream_decompressor(duk_context *ctx)
duk_events_newListener2(ctx, -1, "data", ILibDuktape_CompressedStream_resume_newListener); duk_events_newListener2(ctx, -1, "data", ILibDuktape_CompressedStream_resume_newListener);
return(1); return(1);
} }
duk_ret_t ILibDuktape_CompressedStream_deflate_dataSink(duk_context *ctx)
{
duk_push_this(ctx); // [stream]
duk_push_array(ctx); // [stream][array]
if (duk_has_prop_string(ctx, -2, "_buf"))
{
duk_get_prop_string(ctx, -2, "_buf"); // [stream][array][buffer]
duk_array_push(ctx, -2); // [stream][array]
}
duk_dup(ctx, 0); // [stream][array][buffer]
duk_array_push(ctx, -2); // [stream][array]
duk_buffer_concat(ctx); // [stream][buffer]
duk_put_prop_string(ctx, -2, "_buf"); // [stream]
return(0);
}
duk_ret_t ILibDuktape_CompressedStream_deflate(duk_context *ctx)
{
duk_eval_string(ctx, "require('compressed-stream').createCompressor({WBIT: -15});");// [decompressor]
ILibDuktape_EventEmitter_AddOnEx(ctx, -1, "data", ILibDuktape_CompressedStream_deflate_dataSink);
duk_prepare_method_call(ctx, -1, "end"); // [decompressor][end][this]
duk_dup(ctx, 0); // [decompressor][end][this][buffer]
duk_pcall_method(ctx, 1); // [decompressor][val]
duk_get_prop_string(ctx, -2, "_buf"); // [decompressor][val][buffer]
return(1);
}
duk_ret_t ILibDuktape_CompressedStream_inflate(duk_context *ctx)
{
duk_eval_string(ctx, "require('compressed-stream').createDecompressor({WBIT: -15});");// [decompressor]
ILibDuktape_EventEmitter_AddOnEx(ctx, -1, "data", ILibDuktape_CompressedStream_deflate_dataSink);
duk_prepare_method_call(ctx, -1, "end"); // [decompressor][end][this]
duk_dup(ctx, 0); // [decompressor][end][this][buffer]
duk_pcall_method(ctx, 1); // [decompressor][val]
duk_get_prop_string(ctx, -2, "_buf"); // [decompressor][val][buffer]
return(1);
}
void ILibDuktape_CompressedStream_PUSH(duk_context *ctx, void *chain) void ILibDuktape_CompressedStream_PUSH(duk_context *ctx, void *chain)
{ {
duk_push_object(ctx); // [compressed-stream] duk_push_object(ctx); // [compressed-stream]
ILibDuktape_WriteID(ctx, "compressedStream"); ILibDuktape_WriteID(ctx, "compressedStream");
ILibDuktape_CreateInstanceMethod(ctx, "createCompressor", ILibDuktape_CompressedStream_compressor, DUK_VARARGS); ILibDuktape_CreateInstanceMethod(ctx, "createCompressor", ILibDuktape_CompressedStream_compressor, DUK_VARARGS);
ILibDuktape_CreateInstanceMethod(ctx, "createDecompressor", ILibDuktape_CompressedStream_decompressor, DUK_VARARGS); ILibDuktape_CreateInstanceMethod(ctx, "createDecompressor", ILibDuktape_CompressedStream_decompressor, DUK_VARARGS);
ILibDuktape_CreateInstanceMethod(ctx, "deflate", ILibDuktape_CompressedStream_deflate, DUK_VARARGS);
ILibDuktape_CreateInstanceMethod(ctx, "inflate", ILibDuktape_CompressedStream_inflate, DUK_VARARGS);
} }
void ILibDuktape_CompressedStream_init(duk_context * ctx) void ILibDuktape_CompressedStream_init(duk_context * ctx)
{ {
ILibDuktape_ModSearch_AddHandler(ctx, "compressed-stream", ILibDuktape_CompressedStream_PUSH); ILibDuktape_ModSearch_AddHandler(ctx, "compressed-stream", ILibDuktape_CompressedStream_PUSH);
} }
int ILibDeflate(char *buffer, size_t bufferLen, char *compressed, size_t *compressedLen, uint32_t *crc)
{
int ret = 0;
z_stream Z;
char tmp[16384];
size_t avail;
size_t len = 0;
uint32_t rescrc = 0;
if (compressed != NULL) { len = *compressedLen; *compressedLen = 0; }
memset(&Z, 0, sizeof(Z));
Z.zalloc = Z_NULL;
Z.zfree = Z_NULL;
Z.opaque = Z_NULL;
if (deflateInit2(&Z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY) != Z_OK) { return(1); }
Z.avail_in = (uInt)bufferLen;
Z.next_in = (Bytef*)buffer;
do
{
Z.avail_out = sizeof(tmp);
Z.next_out = (Bytef*)tmp;
ignore_result(deflate(&(Z), Z_NO_FLUSH));
avail = sizeof(tmp) - Z.avail_out;
if (avail > 0)
{
rescrc = crc32(rescrc, (unsigned char*)tmp, (unsigned int)avail);
if (compressed != NULL)
{
if ((len - *compressedLen) < avail) { ret = 1; break; }
memcpy_s(compressed + *compressedLen, len - *compressedLen, (char*)tmp, avail);
}
*compressedLen += avail;
}
} while (Z.avail_out == 0);
Z.avail_in = 0;
Z.next_in = (Bytef*)ILibScratchPad;
if (ret == 0)
{
do
{
Z.avail_out = sizeof(tmp);
Z.next_out = (Bytef*)tmp;
ignore_result(deflate(&Z, Z_FINISH));
avail = sizeof(tmp) - Z.avail_out;
if (avail > 0)
{
rescrc = crc32(rescrc, (unsigned char*)tmp, (unsigned int)avail);
if (compressed != NULL)
{
if ((len - *compressedLen) < avail) { ret = 1; break; }
memcpy_s(compressed + *compressedLen, len - *compressedLen, (char*)tmp, avail);
}
*compressedLen += avail;
}
} while (Z.avail_out == 0);
}
ignore_result(deflateEnd(&Z));
if (crc != NULL) { *crc = rescrc; }
return(ret);
}
int ILibInflate(char *buffer, size_t bufferLen, char *decompressed, size_t *decompressedLen, uint32_t crc)
{
int ret = 0;
z_stream Z;
char tmp[16384];
size_t avail;
size_t len = 0;
uint32_t rescrc = 0;
if (decompressed != NULL) { len = *decompressedLen; *decompressedLen = 0; }
memset(&Z, 0, sizeof(Z));
Z.zalloc = Z_NULL;
Z.zfree = Z_NULL;
Z.opaque = Z_NULL;
if (inflateInit2(&Z, -MAX_WBITS) != Z_OK) { return(1); }
Z.avail_in = (uInt)bufferLen;
Z.next_in = (Bytef*)buffer;
do
{
Z.avail_out = sizeof(tmp);
Z.next_out = (Bytef*)tmp;
ignore_result(inflate(&Z, Z_NO_FLUSH));
avail = sizeof(tmp) - Z.avail_out;
if (avail > 0)
{
rescrc = crc32(rescrc, (unsigned char*)tmp, (unsigned int)avail);
if (decompressed != NULL)
{
if (avail > (len - *decompressedLen)) { ret = 1; break; }
memcpy_s(decompressed + *decompressedLen, len - *decompressedLen, tmp, avail);
}
*decompressedLen += avail;
}
} while (Z.avail_out == 0);
if (ret == 0)
{
Z.avail_in = 0;
Z.next_in = (Bytef*)ILibScratchPad;
do
{
Z.avail_out = sizeof(tmp);
Z.next_out = (Bytef*)tmp;
ignore_result(inflate(&Z, Z_FINISH));
avail = sizeof(tmp) - Z.avail_out;
if (avail > 0)
{
rescrc = crc32(rescrc, (unsigned char*)tmp, (unsigned int)avail);
if (decompressed != NULL)
{
if (avail > (len - *decompressedLen)) { ret = 1; break; }
memcpy_s(decompressed + *decompressedLen, len - *decompressedLen, tmp, avail);
}
*decompressedLen += avail;
}
} while (Z.avail_out == 0);
}
ignore_result(inflateEnd(&Z));
if (crc != 0 && crc != rescrc) { ret = 2; }
return(ret);
}

View File

@@ -112,7 +112,9 @@ typedef struct ILibSimpleDataStore_CacheEntry
const int ILibMemory_SimpleDataStore_CONTAINERSIZE = sizeof(ILibSimpleDataStore_Root); const int ILibMemory_SimpleDataStore_CONTAINERSIZE = sizeof(ILibSimpleDataStore_Root);
void ILibSimpleDataStore_RebuildKeyTable(ILibSimpleDataStore_Root *root); void ILibSimpleDataStore_RebuildKeyTable(ILibSimpleDataStore_Root *root);
extern int ILibInflate(char *buffer, size_t bufferLen, char *decompressed, size_t *decompressedLen, uint32_t crc);
extern int ILibDeflate(char *buffer, size_t bufferLen, char *compressed, size_t *compressedLen, uint32_t *crc);
extern uint32_t crc32c(uint32_t crci, const unsigned char *buf, uint32_t len);
// Perform a SHA384 hash of some data // Perform a SHA384 hash of some data
void ILibSimpleDataStore_SHA384(char *data, int datalen, char* result) { util_sha384(data, datalen, result); } void ILibSimpleDataStore_SHA384(char *data, int datalen, char* result) { util_sha384(data, datalen, result); }
@@ -353,6 +355,16 @@ ILibSimpleDataStore_RecordHeader_NG* ILibSimpleDataStore_ReadNextRecord(ILibSimp
{ {
// Check the hash // Check the hash
if (memcmp(node->hash, result, SHA384HASHSIZE) == 0) { return node; } // Data is correct if (memcmp(node->hash, result, SHA384HASHSIZE) == 0) { return node; } // Data is correct
// Before we assume this is a bad hash check, we need to verify it's not a compressed node
if (node->keyLen > sizeof(uint32_t))
{
if (crc32c(0, node->key, node->keyLen - sizeof(uint32_t)) == ((uint32_t*)(node->key + node->keyLen - sizeof(uint32_t)))[0])
{
return(node);
}
}
return NULL; // Data is corrupt return NULL; // Data is corrupt
} }
return node; return node;
@@ -608,7 +620,7 @@ __EXPORT_TYPE void ILibSimpleDataStore_Close(ILibSimpleDataStore dataStore)
} }
// Store a key/value pair in the data store // Store a key/value pair in the data store
__EXPORT_TYPE int ILibSimpleDataStore_PutEx(ILibSimpleDataStore dataStore, char* key, int keyLen, char* value, int valueLen) __EXPORT_TYPE int ILibSimpleDataStore_PutEx2(ILibSimpleDataStore dataStore, char* key, int keyLen, char* value, int valueLen, char *vhash)
{ {
int ret; int ret;
char hash[SHA384HASHSIZE]; char hash[SHA384HASHSIZE];
@@ -623,8 +635,18 @@ __EXPORT_TYPE int ILibSimpleDataStore_PutEx(ILibSimpleDataStore dataStore, char*
} }
if (keyLen > 1 && key[keyLen - 1] == 0) { keyLen -= 1; } if (keyLen > 1 && key[keyLen - 1] == 0) { keyLen -= 1; }
if (vhash != NULL)
{
char *tmpkey = (char*)ILibMemory_SmartAllocate(keyLen + sizeof(int));
memcpy_s(tmpkey, ILibMemory_Size(tmpkey), key, keyLen);
((uint32_t*)(tmpkey + keyLen))[0] = crc32c(0, tmpkey, keyLen);
key = tmpkey;
keyLen = (int)ILibMemory_Size(key);
memcpy_s(hash, sizeof(hash), vhash, SHA384HASHSIZE);
}
entry = (ILibSimpleDataStore_TableEntry*)ILibHashtable_Get(root->keyTable, NULL, key, keyLen); entry = (ILibSimpleDataStore_TableEntry*)ILibHashtable_Get(root->keyTable, NULL, key, keyLen);
ILibSimpleDataStore_SHA384(value, valueLen, hash); // Hash the value if (vhash == NULL) { ILibSimpleDataStore_SHA384(value, valueLen, hash); } // Hash the value
// Create a new record for the key and value // Create a new record for the key and value
if (entry == NULL) if (entry == NULL)
@@ -651,6 +673,28 @@ __EXPORT_TYPE int ILibSimpleDataStore_PutEx(ILibSimpleDataStore dataStore, char*
return(ret); return(ret);
} }
int ILibSimpleDataStore_PutCompressed(ILibSimpleDataStore dataStore, char* key, int keyLen, char* value, int valueLen)
{
int ret = 1;
char hash[SHA384HASHSIZE];
char *tmp = NULL;
size_t tmpLen = 0;
uint32_t crc = 0;
if (ILibDeflate(value, valueLen, NULL, &tmpLen, NULL) == 0)
{
tmp = (char*)ILibMemory_SmartAllocate(tmpLen);
if (ILibDeflate(value, valueLen, tmp, &tmpLen, NULL) == 0)
{
ILibSimpleDataStore_SHA384(value, valueLen, hash); // Hash the Uncompressed Data
ILibSimpleDataStore_PutEx2(dataStore, key, keyLen, tmp, (int)tmpLen, hash);
ret = 0;
}
ILibMemory_Free(tmp);
}
return(ret);
}
__EXPORT_TYPE int ILibSimpleDataStore_GetInt(ILibSimpleDataStore dataStore, char* key, int defaultValue) __EXPORT_TYPE int ILibSimpleDataStore_GetInt(ILibSimpleDataStore dataStore, char* key, int defaultValue)
{ {
int bufLen = ILibSimpleDataStore_Get(dataStore, key, ILibScratchPad, sizeof(ILibScratchPad)); int bufLen = ILibSimpleDataStore_Get(dataStore, key, ILibScratchPad, sizeof(ILibScratchPad));
@@ -661,6 +705,7 @@ __EXPORT_TYPE int ILibSimpleDataStore_GetInt(ILibSimpleDataStore dataStore, char
// Get a value from the data store given a key // Get a value from the data store given a key
__EXPORT_TYPE int ILibSimpleDataStore_GetEx(ILibSimpleDataStore dataStore, char* key, int keyLen, char *buffer, int bufferLen) __EXPORT_TYPE int ILibSimpleDataStore_GetEx(ILibSimpleDataStore dataStore, char* key, int keyLen, char *buffer, int bufferLen)
{ {
int isCompressed = 0;
char hash[SHA384HASHSIZE]; char hash[SHA384HASHSIZE];
ILibSimpleDataStore_Root *root = (ILibSimpleDataStore_Root*)dataStore; ILibSimpleDataStore_Root *root = (ILibSimpleDataStore_Root*)dataStore;
ILibSimpleDataStore_TableEntry *entry; ILibSimpleDataStore_TableEntry *entry;
@@ -688,9 +733,19 @@ __EXPORT_TYPE int ILibSimpleDataStore_GetEx(ILibSimpleDataStore dataStore, char*
} }
entry = (ILibSimpleDataStore_TableEntry*)ILibHashtable_Get(root->keyTable, NULL, key, keyLen); entry = (ILibSimpleDataStore_TableEntry*)ILibHashtable_Get(root->keyTable, NULL, key, keyLen);
if (entry == NULL)
{
// Before returning an error, check if this is a compressed record
char *tmpkey = (char*)ILibMemory_SmartAllocate(keyLen + sizeof(uint32_t));
memcpy_s(tmpkey, ILibMemory_Size(tmpkey), key, keyLen);
((uint32_t*)(tmpkey + keyLen))[0] = crc32c(0, tmpkey, keyLen);
entry = (ILibSimpleDataStore_TableEntry*)ILibHashtable_Get(root->keyTable, NULL, tmpkey, (int)ILibMemory_Size(tmpkey));
ILibMemory_Free(tmpkey);
if (entry != NULL) { isCompressed = 1; }
}
if (entry == NULL) return 0; // If there is no in-memory entry for this key, return zero now. if (entry == NULL) return 0; // If there is no in-memory entry for this key, return zero now.
if ((buffer != NULL) && (bufferLen >= entry->valueLength)) // If the buffer is not null and can hold the value, place the value in the buffer. if ((buffer != NULL) && (bufferLen >= entry->valueLength) && isCompressed == 0) // If the buffer is not null and can hold the value, place the value in the buffer.
{ {
if (ILibSimpleDataStore_SeekPosition(root->dataFile, entry->valueOffset, SEEK_SET) != 0) return 0; // Seek to the position of the value in the data store if (ILibSimpleDataStore_SeekPosition(root->dataFile, entry->valueOffset, SEEK_SET) != 0) return 0; // Seek to the position of the value in the data store
if (fread(buffer, 1, entry->valueLength, root->dataFile) == 0) return 0; // Read the value into the buffer if (fread(buffer, 1, entry->valueLength, root->dataFile) == 0) return 0; // Read the value into the buffer
@@ -698,6 +753,34 @@ __EXPORT_TYPE int ILibSimpleDataStore_GetEx(ILibSimpleDataStore dataStore, char*
if (memcmp(hash, entry->valueHash, SHA384HASHSIZE) != 0) return 0; // Check the hash, return 0 if not valid if (memcmp(hash, entry->valueHash, SHA384HASHSIZE) != 0) return 0; // Check the hash, return 0 if not valid
if (bufferLen > entry->valueLength) { buffer[entry->valueLength] = 0; } // Add a zero at the end to be nice, if the buffer can take it. if (bufferLen > entry->valueLength) { buffer[entry->valueLength] = 0; } // Add a zero at the end to be nice, if the buffer can take it.
} }
else if (isCompressed != 0)
{
// This is a compressed record
char *compressed = ILibMemory_SmartAllocate(entry->valueLength);
size_t tmplen = bufferLen;
if (ILibSimpleDataStore_SeekPosition(root->dataFile, entry->valueOffset, SEEK_SET) != 0) return 0; // Seek to the position of the value in the data store
if (fread(compressed, 1, entry->valueLength, root->dataFile) == 0) return 0; // Read the value into the buffer
if (ILibInflate(compressed, entry->valueLength, buffer, &tmplen, 0) == 0)
{
if (buffer == NULL) { return((int)tmplen); }
// Before we return, we need to check the HASH of the uncompressed data
char hash[SHA384HASHSIZE];
ILibSimpleDataStore_SHA384(buffer, (int)tmplen, hash);
if (memcmp(hash, entry->valueHash, SHA384HASHSIZE) == 0)
{
return((int)tmplen);
}
else
{
return(0);
}
}
else
{
return(0);
}
}
return entry->valueLength; return entry->valueLength;
} }
@@ -709,6 +792,15 @@ __EXPORT_TYPE char* ILibSimpleDataStore_GetHashEx(ILibSimpleDataStore dataStore,
if (root == NULL) return NULL; if (root == NULL) return NULL;
entry = (ILibSimpleDataStore_TableEntry*)ILibHashtable_Get(root->keyTable, NULL, key, keyLen); entry = (ILibSimpleDataStore_TableEntry*)ILibHashtable_Get(root->keyTable, NULL, key, keyLen);
if (entry == NULL)
{
// Before we return an error, let's check if this is a compressed record
char* tmpkey = (char*)ILibMemory_SmartAllocate(keyLen + sizeof(uint32_t));
memcpy_s(tmpkey, ILibMemory_Size(tmpkey), key, keyLen);
((uint32_t*)(tmpkey + keyLen))[0] = crc32c(0, key, (uint32_t)keyLen);
entry = (ILibSimpleDataStore_TableEntry*)ILibHashtable_Get(root->keyTable, NULL, tmpkey, (int)ILibMemory_Size(tmpkey));
ILibMemory_Free(tmpkey);
}
if (entry == NULL) return NULL; // If there is no in-memory entry for this key, return zero now. if (entry == NULL) return NULL; // If there is no in-memory entry for this key, return zero now.
return entry->valueHash; return entry->valueHash;
@@ -726,7 +818,28 @@ __EXPORT_TYPE int ILibSimpleDataStore_DeleteEx(ILibSimpleDataStore dataStore, ch
if (root == NULL) return 0; if (root == NULL) return 0;
entry = (ILibSimpleDataStore_TableEntry*)ILibHashtable_Remove(root->keyTable, NULL, key, keyLen); entry = (ILibSimpleDataStore_TableEntry*)ILibHashtable_Remove(root->keyTable, NULL, key, keyLen);
if (entry != NULL) { ILibSimpleDataStore_WriteRecord(root->dataFile, key, keyLen, NULL, 0, NULL); free(entry); return 1; } if (entry == NULL)
{
// Check to see if this is a compressed record, before we return an error
char *tmpkey = (char*)ILibMemory_SmartAllocate(keyLen + sizeof(uint32_t));
memcpy_s(tmpkey, ILibMemory_Size(tmpkey), key, keyLen);
((uint32_t*)(tmpkey + keyLen))[0] = crc32c(0, key, keyLen);
entry = (ILibSimpleDataStore_TableEntry*)ILibHashtable_Remove(root->keyTable, NULL, tmpkey, (int)ILibMemory_Size(tmpkey));
if (entry != NULL)
{
ILibSimpleDataStore_WriteRecord(root->dataFile, tmpkey, (int)ILibMemory_Size(tmpkey), NULL, 0, NULL);
free(entry);
ILibMemory_Free(tmpkey);
return 1;
}
ILibMemory_Free(tmpkey);
}
else
{
ILibSimpleDataStore_WriteRecord(root->dataFile, key, keyLen, NULL, 0, NULL);
free(entry);
return 1;
}
return 0; return 0;
} }
@@ -808,6 +921,15 @@ void ILibSimpleDataStore_EnumerateKeysSink(ILibHashtable sender, void *Key1, cha
UNREFERENCED_PARAMETER(Key2); UNREFERENCED_PARAMETER(Key2);
UNREFERENCED_PARAMETER(Key2Len); UNREFERENCED_PARAMETER(Key2Len);
if (Key2Len > sizeof(uint32_t))
{
// Check if this is a compressed entry
if (crc32c(0, Key2, Key2Len - sizeof(uint32_t)) == ((uint32_t*)(Key2 + Key2Len - sizeof(uint32_t)))[0])
{
Key2Len -= sizeof(uint32_t);
}
}
handler(dataStore, Key2, Key2Len, userX); // Call the user handler(dataStore, Key2, Key2Len, userX); // Call the user
} }

View File

@@ -59,8 +59,10 @@ __EXPORT_TYPE int ILibSimpleDataStore_Cached_GetJSONEx(ILibSimpleDataStore dataS
__EXPORT_TYPE void ILibSimpleDataStore_ConfigCompact(ILibSimpleDataStore dataStore, uint64_t minimumDirtySize); __EXPORT_TYPE void ILibSimpleDataStore_ConfigCompact(ILibSimpleDataStore dataStore, uint64_t minimumDirtySize);
__EXPORT_TYPE void ILibSimpleDataStore_ConfigSizeLimit(ILibSimpleDataStore dataStore, uint64_t sizeLimit, ILibSimpleDataStore_SizeWarningHandler handler, void *user); __EXPORT_TYPE void ILibSimpleDataStore_ConfigSizeLimit(ILibSimpleDataStore dataStore, uint64_t sizeLimit, ILibSimpleDataStore_SizeWarningHandler handler, void *user);
__EXPORT_TYPE int ILibSimpleDataStore_PutEx(ILibSimpleDataStore dataStore, char* key, int keyLen, char* value, int valueLen); __EXPORT_TYPE int ILibSimpleDataStore_PutEx2(ILibSimpleDataStore dataStore, char* key, int keyLen, char* value, int valueLen, char *hash);
#define ILibSimpleDataStore_Put(dataStore, key, value) ILibSimpleDataStore_PutEx(dataStore, key, (int)strnlen_s(key, ILibSimpleDataStore_MaxKeyLength), value, (int)strnlen_s(value, ILibSimpleDataStore_MaxUnspecifiedValueLen)) #define ILibSimpleDataStore_Put(dataStore, key, value) ILibSimpleDataStore_PutEx(dataStore, key, (int)strnlen_s(key, ILibSimpleDataStore_MaxKeyLength), value, (int)strnlen_s(value, ILibSimpleDataStore_MaxUnspecifiedValueLen))
#define ILibSimpleDataStore_PutEx(dataStore, key, keyLen, value, valueLen) ILibSimpleDataStore_PutEx2(dataStore, key, keyLen, value, valueLen, NULL)
int ILibSimpleDataStore_PutCompressed(ILibSimpleDataStore dataStore, char* key, int keyLen, char* value, int valueLen);
// Get a value from the datastore of given a key. // Get a value from the datastore of given a key.
__EXPORT_TYPE int ILibSimpleDataStore_GetEx(ILibSimpleDataStore dataStore, char* key, int keyLen, char* buffer, int bufferLen); __EXPORT_TYPE int ILibSimpleDataStore_GetEx(ILibSimpleDataStore dataStore, char* key, int keyLen, char* buffer, int bufferLen);