I haven't found any get equivalent to appendBytes. If I've understood getBytes correctly, that method always returns bytes from the start of the buffer. It will not increment.
What's the reason for this?
I'm not sure what you mean, equivalent? appendBytes adds while getBytes gets, they're complete opposites. This is a nice method I use to get bytes when there's different data in the buffer. It shows a nice use of getBytes:range:
UInt16 anUInt16;
float aFloat;
int position = 0;
[data getBytes:&anUInt16 range:NSMakeRange(position, sizeof(UInt16))];
position += sizeof(UInt16);
[data getBytes:&aFloat range:NSMakeRange(position, sizeof(float))];
position += sizeof(float);
and so on...
There are several methods that give you access to some part of the data in a NSData object. For example, you can use -getBytes:range: to copy the data specified by a NSRange into a buffer. Or you can use -subdataWithRange: to get a NSData that contains just a portion of another.
If you want to read sequentially through the contents of a NSData object, you can create a NSInputStream and initialize it with your NSData, and then read from the stream using -getBuffer:length: or -read:maxLength:.
NSInputStream *stream = [[NSInputStream alloc] initWithData:myData];
[stream open];
while ([stream read:&buffer maxLength:bufferLength] > 0) {
// do something with buffer
}
Note: you obviously don't need the while loop in the code above if you're not doing the same thing to each chunk of data that you read -- I just included it because it's a common way to read data.
Your question is not clear.
If you need to get bytes from a specific offset, simply use the getBytes:range: method.
- ( void )getBytes: ( void * )buffer range: ( NSRange )range;
For instance:
char buf[ 4 ];
// Get 4 bytes from offset 2
[ dataObject getBytes: buf range: NSMakeRange( 2, 4 ) ];
If you're looking for appendBytes, then use NSMutableData, which inherits from NSData and adds mutability options, just as NSArray and NSMutableArray.
- ( void )appendBytes: ( const void * )bytes length: ( NSUInteger )length;
You can get a NSMutableData from a NSData using the mutableCopy method. Note that you own the resulting object.
NSMutableData * mData;
mData = [ dataObject mutableCopy ];
[ mData appendBytes: ( const void * )"abcd" length: 4 ];
[ mData release ]
You may be looking for -[NSData getBytes:range:], which lets you specify the range of data that you want to read from the buffer. Another option is -[NSData subdataWithRange:], which actually returns another NSData instance that contains only the bytes in the specified range.
Related
I have some NSData instance with 2 bytes of different value.
Will calling getBytes: of any kind (length, range...) also advance the current position in the buffer?
Example:
NSData *data = ...; // 2 bytes data
[data getBytes:&whatever1 length:1]; // reading first byte
[data getBytes:&whatever2 length:1]; // reading first OR second byte?
Will the contents that I get on each time getBytes:length: is called be the same first byte in the NSData instance or will the first call advance the next call to read from the second byte?
Thanks!
No, it does not.
In the example it will access the same byte (first).
To access the "next" byte you'll need to use getBytes:range: but, like #hot-licks commented, NSData is immutable and therefore won't be modified.
I need to do bit operations on representations of arbitrary precision numbers in Objective C. So far I have been using NSData objects to hold the numbers - is there a way to bit shift the content of those? If not, is there a different way to achieve this?
Using NSMutableData you can fetch the byte in a char, shift your bits and replace it with -replaceBytesInRange:withBytes:.
I don't see any other solution except for writing your own date holder class using a char * buffer to hold the raw data.
As you'll have spotted, Apple doesn't provide arbitrary precision support. Nothing is provided larger than the 1024-bit integers in vecLib.
I also don't think NSData provides shifts and rolls. So you're going to have to roll your own. E.g. a very naive version, which may have some small errors as I'm typing it directly here:
#interface NSData (Shifts)
- (NSData *)dataByShiftingLeft:(NSUInteger)bitCount
{
// we'll work byte by byte
int wholeBytes = bitCount >> 3;
int extraBits = bitCount&7;
NSMutableData *newData = [NSMutableData dataWithLength:self.length + wholeBytes + (extraBits ? 1 : 0)];
if(extraBits)
{
uint8_t *sourceBytes = [self bytes];
uint8_t *destinationBytes = [newData mutableBytes];
for(int index = 0; index < self.length-1; index++)
{
destinationBytes[index] =
(sourceBytes[index] >> (8-extraBits)) |
(sourceBytes[index+1] << extraBits);
}
destinationBytes[index] = roll >> (8-extraBits);
}
else
/* just copy all of self into the beginning of newData */
return newData;
}
#end
Of course, that assumes the number of bits you want to shift by is itself expressible as an NSUInteger, amongst other sins.
I'm trying to learn objective-c. I'm trying to parse a binary file doing the following (simplified):
#interface DatFile : NSObject {
NSData* _data;
}
-(id)initWithFilePath:(NSString *)filePath;
-(void) readFile;
-(void) auxiliaryMethod;
#implementation DatFile
- (id) initWithFilePath:(NSString *)filePath {
if ( self = [super init] ) {
_data = [NSData dataWithContentsOfFile:filePath];
}
return self;
}
-(void) readFile {
int header;
[_data getBytes: &header range: NSMakeRange(0, 4)];
NSLog(#"header: %u", header);
short key;
[_data getBytes: &key range: NSMakeRange(4, 2)];
NSLog(#"key: %u", key);
short value;
[_data getBytes: &value range: NSMakeRange(6, 1)];
NSLog(#"value: %u", value);
[self auxiliaryMethod];
}
-(void) auxiliaryMethod {
short value;
[_data getBytes: &value range: NSMakeRange(6, 1)];
NSLog(#"value: %u", value);
}
My problem is that the code inside the auxiliaryMethod does not compute the same value computed by readFile method. Since the _data object is the same, why the method are computing different values? And, as you can see, the logic inside the auxiliaryMethod is just a copy of the other one...
In other languages (java) I usually separate some logic in smaller methods in order to make the code mode readable/maintainable. This is why I'm trying to mimic it with ObjC.
Of course that probably missing something, but after some hours, I gave up. I don't see where is my mistake. Probably I should erase my project and start it again...
TIA,
Bob
%u is for printing a 32 bit unsigned value. A short is 16 bits. Therefore, you are printing the parsed value plus some stack garbage.
Try %hu; the h modifies %u to print a short value.
If that doesn't work:
make sure your data objects are the same between the two methods
(don't see how they can't be... but...)
show the actual values printed
decode into an unsigned long that is explicitly initialized to 0 before decoding
With this kind of bug, it is pretty much guaranteed it is either that you aren't dealing with the input you think you are or you are dealing with some C-ism related to data types and the implied conversions in this kind of code.
I.e. assuming _data is consistent, then it indicates that the code you think is working is not actually working, but only appearing to by coincidence.
The problem is that you're passing a range of size 1 to getBytes:range:, but a short is 2 bytes in size. So getBytes:range: is only setting one of value's bytes, and the other contains random garbage.
If you really only want to get one byte from the data, change the type of value to int8_t. If you want to get two bytes, pass 2 as the second argument of NSMakeRange (and I recommend also changing the type of value to int16_t).
I'm sort of out of my depths here, but I have the following code (the real code actually has a point of course):
- (NSData*) dataTheseBytes:(Byte[]) bytes {
return [NSData dataWithBytes:bytes length:sizeof(bytes)];
}
The compiler warning is
Sizeof on array function parameter will return size of 'Byte *' (aka
'unsigned char *') instead of 'Byte []'
How can I eliminate this warning (or rather, what am I not understanding about my array of bytes)?
Additionally, why doesn't the error happen with this code? Must have something to do with the method signature...?
Byte bytes[3] = { byte1, byte2, byte3 };
NSData *retVal = [NSData dataWithBytes:bytes length:sizeof(bytes)];
When you pass a C array as a method or C function argument, it "decays" to a pointer to the underlying type (i.e. Byte[] is actually passed as Byte *.) So the called method/function has no idea how many elements are present in the array.
You must also pass the length of the array in order for the called code to know what you want. That's why +[NSData dataWithBytes:length:] has that second argument.
c arrays do not embed their element count.
this is how you would declare a method with an unspecified element count. this is not generally usable:
`- (NSData*) dataTheseBytes:(const Byte*)bytes;`
// or
`- (NSData*) dataTheseBytes:(const Byte[])bytes;`
a more rigid implementation could specify the element count. this is ok if you are always using the same size. example:
enum { MONByteBufferElementCount = 23 };
...
`- (NSData*) dataTheseBytes:(const Byte[MONByteBufferElementCount])bytes
{
return [NSData dataWithBytes:&bytes[0] length:MONByteBufferElementCount * sizeof(bytes[0])];
}
the problem with using objc messaging in this case is that the compiler may not be able to determine the appropriate selector and produce an error or warning if you have declared a selector with the same name but uses different parameters or element counts. therefore, it's safer to use a c function:
`NSData* DataTheseBytes(const Byte bytes[MONByteBufferElementCount]) {
return [NSData dataWithBytes:&bytes[0] length:MONByteBufferElementCount * sizeof(bytes[0])];
}
or use a more verbose name:
`- (NSData*) dataWithMONByteBuffer:(const Byte[MONByteBufferElementCount])bytes
{
return [NSData dataWithBytes:&bytes[0] length:MONByteBufferElementCount * sizeof(bytes[0])];
}
in objc, it's most common to pass the length as an argument, similar to the NSData constructor you call. some part of your program will be able to determine this value (whether it is NSData, a c array or something else).
- (NSData*) dataTheseBytes:(const Byte*)bytes length:(NSUInteger)length
{
return [NSData dataWithBytes:bytes length:length];
}
it's also common to see the element count, like so:
- (NSData*) dataTheseFloats:(const float*)floats length:(NSUInteger)count
{
return [NSData dataWithBytes:floats length:count * sizeof(float)];
}
finally, there are of course a few corner cases. the obvious being a null terminated string:
- (NSData*) dataWithASCIIString:(const char*)chars
{
return [NSData dataWithBytes:chars length:strlen(chars)];
}
You cannot pass arrays to a function. You're passing a pointer to the first element in the array of the caller.
If you need the length of that array, you need to pass that length as a separate argument to your function, and use that instead of sizeof
Why does the following code produce the logging at the bottom ?
Here is the anomaly- my second NSLog should print the chrStr but produces nothing, empty, which is verified by this debug command:
(gdb) po chrStr
object returns empty description
However, the third NSString where I re-convert the NSString back to NSData object DOES display the the data, the same value as in the first NSLog, as it should. This would indicate to me that chrStr must have actual contents. But it seems not to be so from the NSLOG or the po command. Why ?
NSString *login;
NSString *pass;
// Purpose: NSString *loginString = [NSString stringWithFormat:#"\000%#\000%#", login, pass];
login = #"Loginname"; // text string1
pass = #"Password"; // text string2
// convert text strings to data objects
NSData *subData1 = [login dataUsingEncoding:NSUTF8StringEncoding];
NSData *subData2 = [pass dataUsingEncoding:NSUTF8StringEncoding];
// embed a NULL into new NSData object
NSMutableData *data = [NSMutableData data];
unsigned char zeroByte = 0;
[data appendBytes:&zeroByte length:1];
// append string1, NULL, string2 to data object
[data appendData:subData1];
[data appendBytes:&zeroByte length:1];
[data appendData:subData2];
NSLog(#"1.NSData: %#", data); // print data object
// create a character string from data object
NSString *chrStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"2.NSString: %#", chrStr); // print character string
// create data object from string object
NSData *chrData = [chrStr dataUsingEncoding:NSUTF8StringEncoding];
NSLog(#"3.NSDATA: %#", chrData); // print data object
Produces:
[1071:207] 1.NSData: 004c6f67 696e6e61 6d650050 61737377 6f7264
[1071:207] 2.NSString:
[1071:207] 3.NSDATA: 004c6f67 696e6e61 6d650050 61737377 6f7264
This is a real mystery to me. If chrStr is empty then 3-NSDATA could not display its info, but it does !
What am I trying to accomplish ? Well, check my very first comment line: // purpose:
That line when uncommented produces a warning, even though it actually works, so I was trying to do it another way that allowed me to have a clean compile. If you see a better way to accomplish that objective, I all eyes and ears. But please don't dwell on why that #"\000%#\000%#" string is necessary, start out accepting that it is. Thanks.
In C (and therefore objective-c), a null byte is used to represent the end of a string. When you create the string object, it takes all of the data you have given it without parsing, which is why you can convert it back to data successfully. However, when you display the string, the system reads the string up to the first null byte, which is the first byte. Therefore, the string contains data, but any system functions which read byte by byte instead of using the strings returned length will think it is empty. When you work with non-displayable characters, you should try to use data objects over string objects as often as possible.