0

I have an NSDATA object which I create and then send over the network. I have having trouble getting the correct values out of the received NSDATA stream.

Here is some quick code to reproduce my problem. No network transmission required.

    NSMutableData *data = [[NSMutableData alloc] initWithCapacity:150];

    // put data in the array
    [data woaAppendInt8:4];
    [data woaAppendInt32:2525];
    [data woaAppendInt8:6];
    [data woaAppendInt32:1616];

    // get data out of array
    size_t offset  = 0;

    int x1 = [data woaInt8AtOffset:offset];
    offset += 1;  // move to next spot
    NSLog(@"Should be 4 = %i",x1);

    int x2 = [data woaInt32AtOffset:offset];
    offset = offset + 4; // Int's are 4 bytes
    NSLog(@"Should be 2525 = %i",x2);

    int x3 = [data woaInt8AtOffset:offset];
    offset += 1;  // move to next spot
    NSLog(@"Should be 6 = %i",x3);

    int x4 = [data woaInt32AtOffset:offset];
    offset = offset + 4; // Int's are 4 bytes
    NSLog(@"Should be 1616 = %i",x4);

I am using NSDATA categories to make the process easier. Here is the category code:

    @implementation NSData (woaAdditions)

- (int)woaInt32AtOffset:(size_t)offset
{
    const int *intBytes = (const int *)[self bytes];
    return  ntohl(intBytes[offset / 4]);
}

- (char)woaInt8AtOffset:(size_t)offset
{
    const char *charBytes = (const char *)[self bytes];
    return charBytes[offset];
}

@end

@implementation NSMutableData (waoAdditions)

- (void)woaAppendInt32:(int)value
{
    value = htonl(value);
    [self appendBytes:&value length:4];
}

- (void)woaAppendInt8:(char)value
{
    [self appendBytes:&value length:1];
}

@end

The woaInt8AtOffset works great and displays the 4 & 6. The woaInt32AtOffset displays some HUGE number.

What is the problem with the code?

jww
  • 97,681
  • 90
  • 411
  • 885
Johnne
  • 143
  • 10
  • 1
    Suggestion - when working with values with specific sizes, use more appropriate types. Example - use `uint8_t` for 8-bit values instead of `char`. Use `uint32_t` for 32-bit values instead of `int`, etc. – rmaddy May 30 '14 at 16:12
  • `[self getBytes:&buf range:NSMakeRange(offset, 4)];` or a `memcpy()` make more sense than trying to pull values out of the byte buffer yourself. Problem is that you aren't pulling values of the correct size, you're just accessing an (incorrect) offset and thinking it'll promote properly. – CodaFi May 30 '14 at 16:16
  • 1
    The real bug is with `ntohl(intBytes[offset / 4]);`. Get rid of the division. You want `ntohl(intBytes[offset]);`. – rmaddy May 30 '14 at 16:17
  • I get the impression you are all partially correct. I have updated the category to use the int32_t as suggested. Still the same issue. I have noticed that if I use all the same sizes it works (ie: all 32 values) which may be result of the /4. I am not sure how I would update the code to use the [self getbytes:range] option as I keep getting access violations when I tried it. – Johnne May 30 '14 at 16:34
  • If the pseudo-code wasn't enough, then [here's a gist](https://gist.github.com/CodaFi/287245f9199a3fb41b8d) of a working version of that method. – CodaFi May 30 '14 at 16:37
  • Sorry, I went down the wrong track. I assumed since it was returning range that buf needed to be NSData or NSMutableData. Your code worked perfectly. Thanks. – Johnne May 30 '14 at 16:56

1 Answers1

1

I have updated the code to use the int32_t and modified the category as follows:

- (int)woaInt32AtOffset:(size_t)offset
{
    int32_t buf;
    [self getBytes:&buf range:NSMakeRange(offset, 4)];
    return ntohl(buf);
}

The code works correctly now. Awesome thanks.

Johnne
  • 143
  • 10
  • That's the correct and portable way. Will work on any processor, whether 32 or 64 bit. Has no problems if offset is an odd number. – gnasher729 Mar 01 '15 at 20:45