Search code examples
sqlited

sqlite3 compress function in D


I'm trying to adapt the C code to compress data from http://sqlite.1065341.n5.nabble.com/Compress-function-tp11998p11999.html in D without any success, which is giving me Segmentation fault (core dumped) when run. I've tried debugging this using gdb, which seems to hint something is wrong with my call to sqlite3_exec, which is being used correctly to my knowledge.

import etc.c.sqlite3, etc.c.zlib;

void main(){
  sqlite3* db;
  int rc;
  char* errmsg;

  rc = sqlite3_open(":memory:", &db);
  rc = sqlite3_create_function(db, "compress", 1, SQLITE_UTF8, null, 
                               &sqlite_compress, null, null);
  rc = sqlite3_create_function(db, "decompress", 1, SQLITE_UTF8, null, 
                               &sqlite_decompress, null, null);
  rc = sqlite3_exec(db,
        "select decompress(compress('may the force be with you'))="
        "'may the force be with you'",
        null, null, &errmsg);
  assert(rc == SQLITE_OK);

  sqlite3_close(db);
}
extern(C){
static void sqlite_compress(sqlite3_context *context, int argc, sqlite3_value **argv){
  ulong rc, nIn, nOut;
  const(ubyte)* inBuf;
  ubyte* outBuf;

  inBuf = cast(ubyte*)sqlite3_value_blob(argv[0]);
  nIn = sqlite3_value_bytes(argv[0]);
  nOut = 13 + nIn + (nIn+999)/1000;
  outBuf = cast(ubyte*)sqlite3_malloc(cast(int)nOut+4);
  outBuf[0] = nIn>>24 & 0xff;
  outBuf[1] = nIn>>16 & 0xff;
  outBuf[2] = nIn>>8 & 0xff;
  outBuf[3] = nIn & 0xff;
  rc = compress(&outBuf[4], &nOut, inBuf, nIn);

  if( rc==Z_OK ){
    sqlite3_result_blob(context, &outBuf, cast(int)nOut+4, &sqlite3_free);
  }else{
    sqlite3_free(&outBuf);
    sqlite3_result_error(context, "uncompressable data", -1);
  }
}

static void sqlite_decompress(sqlite3_context *context, int argc, sqlite3_value **argv){
  ulong rc, nIn, nOut;
  const(ubyte)* inBuf;
  ubyte* outBuf;

  nIn = sqlite3_value_bytes(argv[0]);
  if( nIn<=4 )
    return;

  inBuf = cast(ubyte*)sqlite3_value_blob(argv[0]);
  nOut = (inBuf[0]<<24) + (inBuf[1]<<16) + (inBuf[2]<<8) + inBuf[3];
  outBuf = cast(ubyte*)sqlite3_malloc( cast(int)nOut+1 );
  rc = uncompress(outBuf, &nOut, &inBuf[4], nIn);
  if( rc==Z_OK ){
    sqlite3_result_blob(context, outBuf, cast(int)nOut, &sqlite3_free);
  }else{
    sqlite3_free(&outBuf);
    sqlite3_result_error(context, "data is not compressed", -1);
  }
}
}

What might be causing the problem and how do I fix it?


Solution

  • This line in sqlite_compress is the culprit:

    sqlite3_result_blob(context, &outBuf, cast(int)nOut+4, &sqlite3_free);
    

    Here's its correct counterpart in sqlite_decompress:

    sqlite3_result_blob(context, outBuf, cast(int)nOut, &sqlite3_free);
    

    Notice the difference? In sqlite_compress you're passing &outBuf when you should be passing outBuf. With &outBuf, sqlite3_free tries to free a pointer to the local variable outBuf which must fail.