-1

I'm trying to compact a raster file in a way that is easy to read without GDAL library (my web server cannot install GDAL). Following this question, I'm doing the following to convert a raster's bytes (only 0 and 1 values) to bits:

int main(int argc,char *argv[]) {
    if (argc < 3) {
        return 1;
    }
    GDALDataset *poDataset;
    GDALAllRegister();
    poDataset = (GDALDataset*)GDALOpen(argv[1],GA_ReadOnly);
    if (poDataset == NULL) {
        return 2;
    }
    int tx=poDataset->GetRasterXSize(), ty=poDataset->GetRasterYSize();
    GDALRasterBand *poBand;
    int nBlockXSize,nBlockYSize;
    poBand = poDataset->GetRasterBand(1);
    printf("Type: %s\n",GDALGetDataTypeName(poBand->GetRasterDataType()));
    // Type: Byte
    poBand->GetBlockSize(&nBlockXSize,&nBlockYSize);
    int i, nX = tx/nBlockXSize, nY = ty/nBlockYSize;
    char *data = (char*)CPLMalloc(nBlockXSize*nBlockYSize + 1);
    uint32_t out[nBlockXSize*nBlockYSize/32];
    char temp;
    CPLErr erro;
    FILE* pFile;
    pFile = fopen(argv[2],"wb");
    for (y=0; y<nY; y++) {
        for (x=0; x<nX; x++) {
            erro = poBand->ReadBlock(x,y,data);
            if (erro > 0) {
                return 3;
            }
            for (i=0; i<nBlockXSize*nBlockYSize; i+=32) {
                temp = data[i+32];
                data[i+32] = 0;
                out[i/32] = strtoul(&data[i],0,2);
                if (data[i] != 0) {
                    printf("%u/%u ",data[i],out[i/32]);
                }
                data[i+32] = temp;
            }
            ch = getchar(); // for debugging
        }
        fwrite(out,4,nBlockXSize*nBlockYSize/32,pFile);
    }
    fclose(pFile);
    CPLFree(data);
    return 0;
}

After the first set of bytes is read (for (i=0; i<nBlockXSize*nBlockYSize; i+=32)), I can see that printf("%u/%u ",data[i],out[i/32]); is printing some "1/0", meaning that, where my raster has a 1 value, this is being passed to strtoul, which is returning 0. Obviously I'm messing with something (pointers, probably), but can't find where. What am I doing wrong?

Rodrigo
  • 4,706
  • 6
  • 51
  • 94
  • `%d` is the wrong format specifier for `out[i/32]`, this causes undefined behaviour – M.M Dec 03 '18 at 00:41
  • `strtoul` converts a sequence of characters to a number, so the first character being `1` doesn't really tell you much, e.g. maybe the string was `100000000000000000000`. In that case it should really return `ULONG_MAX` (since the input is a correct binary number but it is out of range) and not `0`; the return value of `0` is supposed to indicate that no digits were found, or the value was actually `0`. – M.M Dec 03 '18 at 00:42
  • You could try changing your debug print to show more characters , e.g. `printf("%.20s", &data[i])` (assuming that it all contains printable characters), or inspect that in a debugger – M.M Dec 03 '18 at 00:45
  • @M.M Changed it to `printf("%u/%u ",data[i],out[i/32]);` but still getting "1/0". Using `printf("%s/%u ",&data[i],out[i/32]);` produces "/0 /0 /0 /0..." (with a different number of non-printable characters before "/0" each time). – Rodrigo Dec 03 '18 at 01:11
  • 2
    OK, that makes more sense... `data[i]` is `1` (not `'1'`) so there are no matching characters for `strtoul` and therefore `0` returned. Do you understand the difference between `char x = 1;` and `char x = '1';` ? – M.M Dec 03 '18 at 01:45
  • @M.M Ah, of course! Thank you! I'm using the wrong function... – Rodrigo Dec 03 '18 at 02:08
  • 1
    You allso have an undefined behaviour. Unless `CPLMalloc` gives you one more byte than you ask for, then `data[i+32]` will reach outside the memory allocated to data – HAL9000 Dec 04 '18 at 21:00
  • @HAL9000 You are right! This silent bug hasn't bitten yet, but one day it would. I guess `CPLMalloc(nBlockXSize*nBlockYSize+1)` solve it. Thank you! – Rodrigo Dec 04 '18 at 23:42

1 Answers1

2

strtoul is for converting printable character data to an integer. The string should contain character codes for digits, e.g. '0', '1' etc.

Apparently in your case the source data is actually the integer value 1 and so strtoul finds there are no characters of the expected form and returns 0 .

M.M
  • 138,810
  • 21
  • 208
  • 365