How to properly split up NSData? - ios

I'm trying to split up NSData into smaller <100 length chunks so I can send them over CoreBluetooth but for some reason, it decides to mess up occasionally, proven by the fact that trying to combine the data and decoding the object from within the same method fails. Because of this, I'm assuming I'm splitting up the NSData wrong?
Here is the code I'm using to split it up (Taken from some kind stranger on stackoverflow!)
// Split up the data and put into Array
NSUInteger length = [data length];
NSUInteger chunkSize = 100;
NSUInteger offset = 0;
do {
NSUInteger thisChunkSize = length - offset > chunkSize ? chunkSize : length - offset;
NSData* chunk = [NSData dataWithBytesNoCopy:(char *)[data bytes] + offset
length:thisChunkSize freeWhenDone:NO];
offset += thisChunkSize;
[orderQueue addObject:chunk];
} while (offset < length);
The data is then recombined and the object unarchived as so:
NSMutableData *finishedData = [[NSMutableData alloc] init];
for (NSData *dataChunk in orderQueue) {
[finishedData appendData:dataChunk];
}
Order *order = [NSKeyedUnarchiver unarchiveObjectWithData:finishedData]; // ERRORS OUT ON THIS LINE
finishedData = [[NSMutableData alloc] init];
dataChunks = [[NSMutableArray alloc] init];
On paper, I honestly think I'm doing it right but it still bugs out occasionally. Any ideas why this might be? :< I get the following error:
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '* -[NSKeyedUnarchiver initForReadingWithData:]: incomprehensible archive (0x62, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x30, 0x30)'

I just ran this code to check your logic and it's correct.
NSMutableArray *orderQueue = [[NSMutableArray alloc] init];
NSString *originalString = #"Here are some strings for you.";
for (NSInteger i = 0; i < 1000; ++i)
{
#autoreleasepool {
originalString = [originalString stringByAppendingString:#"\nHere are some strings for you."];
}
}
NSData *data = [originalString dataUsingEncoding:NSUTF8StringEncoding];
// Split up the data and put into Array
NSUInteger length = [data length];
NSUInteger chunkSize = 100;
NSUInteger offset = 0;
do {
NSUInteger thisChunkSize = length - offset > chunkSize ? chunkSize : length - offset;
NSData* chunk = [NSData dataWithBytesNoCopy:(char *)[data bytes] + offset
length:thisChunkSize freeWhenDone:NO];
offset += thisChunkSize;
[orderQueue addObject:chunk];
} while (offset < length);
NSMutableData *finishedData = [[NSMutableData alloc] init];
for (NSData *dataChunk in orderQueue) {
[finishedData appendData:dataChunk];
}
NSString *recreatedString = [[NSString alloc] initWithData:finishedData encoding:NSUTF8StringEncoding];
NSLog(#"%#", recreatedString);
NSLog(#"%#", [originalString isEqualToString:recreatedString] ? #"Equal" : #"Error");
Your error must be somewhere else, potentially in the way you're responding to the <NSCoding> protocol in the Order class.

Related

We are facing problem while creating compressed file of transferred socket data in hex string to converting to .tgz file in iOS App

We are facing problem while creating compressed file at iOS Device Document Directory, .tgz file is in Hex string transferring from pin-pad device to iPad iOS App at TCP socket layer. We used below HexToString function to convert that hex string and make file with .tgz. but at the end file is corrupted.
Can anyone please help us here, how to create compress file at iOS level with below hex string ? Please suggest us any code changes.
Note :- we had tried multiple NSStringEncoding technique, like ASCII, Unicode, Utf8, etc.
HEX String:-
1F8B08003058A8620203EDCA3B0A80301045D1594A5660265FB7E036065422A8453282CB57B4B2B112419CD3DCE2BD6966DD8F54925E4A975B62D22551EE741A2A5E199E80BBE8F1681DFDA5270BC6DB60D1398735A0092E0650082F580A53566A6F36F7BFFBFDA39A01841042FCD0062C8057FA00080000
we are using Xcode Version:13.1 and IOS Version 15.1 and above.
//Below function we used for creating .tgz file
//fileName here is abc.tgz which is compress file type
//content here is hex string mention aboved
+ (void)writeToLogFile:(NSString*)content fileName:(NSString*)fileNameString{
content = [NSString stringWithFormat:#"%#",content];
NSString *documentsDirectory = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents"];
NSString *fileName = [documentsDirectory stringByAppendingPathComponent:fileNameString];
NSData *fileOriginalString = [self HextoString:content];
NSData *fileData = [fileOriginalString dataUsingEncoding:NSASCIIStringEncoding];
***//In alternative we also tried direct hex string to NSData type by calling below commentented method but it still failing
//NSData *fileData = [self dataFromHexString:content];***
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSError *error = nil;
[fileData writeToFile:fileName options:NSDataWritingAtomic error:&error];
NSLog(#"Write returned error: %#", [error localizedDescription]);
});
}
//Below function we used for Hex to String conversion
+(NSString*)HextoString:(NSString*)string{
#try{
NSMutableString * StrResult = [[NSMutableString alloc] init];
int i = 0;
while (i < [string length]){
NSString * hexChar = [string substringWithRange: NSMakeRange(i, 2)];
int value = 0;
sscanf([hexChar cStringUsingEncoding:NSASCIIStringEncoding], "%x", &value);
[StrResult appendFormat:#"%c", (char)value];
i+=2;
}
return StrResult;
}
#catch (NSException *exception){
[AELoggerManager info:[NSString stringWithFormat:#" %s EXCEPTION ::%#",__FUNCTION__,exception]];
}
}
+ (NSData *)dataFromHexString:(NSString *) string {
if([string length] % 2 == 1){
string = [#"0"stringByAppendingString:string];
}
const char *chars = [string UTF8String];
int i = 0, len = (int)[string length];
NSMutableData *data = [NSMutableData dataWithCapacity:len / 2];
char byteChars[3] = {'\0','\0','\0'};
unsigned long wholeByte;
while (i < len) {
byteChars[0] = chars[i++];
byteChars[1] = chars[i++];
wholeByte = strtoul(byteChars, NULL, 16);
[data appendBytes:&wholeByte length:2];
}
return data;
}

How to split byte array and send it in small packs in Objective-c iOS

How to split byteArray in iOS
Iam getting 160 length of arrayByte data..
I need to split into 4 parts..each part contain 40 arrayByte.that data I need to copy and use for decoding..I tried to converted it but its not working..Can some one help to do this..
Finally i got solution Below is updated working code
-(NSMutableData*)decodeOpusData:(NSData*)data
{
NSMutableData *audioData = [[NSMutableData alloc] init];
for (NSUInteger i = 0; i < 4; i ++)
{
int bufferLength = 40;
if([data length]>= 40){
NSData *subData = [data subdataWithRange:NSMakeRange(i*bufferLength, bufferLength)];
Byte *byteData = (Byte*)malloc(sizeof(Byte)*bufferLength);
memcpy(byteData, [subData bytes], bufferLength);
//You can do anything here with data..........
//Below iam decoding audio data using OPUS library
short decodedBuffer[WB_FRAME_SIZE];
int nDecodedByte = sizeof(short) * [self decode:byteData length:bufferLength output:decodedBuffer];
NSData *PCMData = [NSData dataWithBytes:(Byte *)decodedBuffer length:nDecodedByte ];
[audioData appendData:PCMData];
//Decoding audio data using OPUS library
}
}
return audioData;
}
Below code is android.i want to do like this..
ArrayByte length = 160
BUFFER_LENGTH = 40
public fun opusDataDecoder(data:ByteArray){
for (i in 0..3){
val byteArray = ByteArray(BUFFER_LENGTH)
System.arraycopy(data,i * BUFFER_LENGTH,byteArray,0, BUFFER_LENGTH) //BUFFER_LENGTH = 40
val decodeBufferArray = ShortArray(byteArray.size * 8) // decodeBufferArray = 320
val size = tntOpusUtils.decode(decoderHandler, byteArray, decodeBufferArray)
if (size > 0) {
val decodeArray = ShortArray(size)
System.arraycopy(decodeBufferArray, 0, decodeArray, 0, size)
opusDecode(decodeArray)
} else {
Log.e(TAG, "opusDecode error : $size")
}
}
}
Iam getting only first 40 bytes..i want like first 0-40 bytes then 40-80 bytes,then 80-120bytes then 120-160bytes..
But here iam getting always 40 bytes...
Can some one help me how to fix this?
Finally i got solution for split byte array and send it in small packs
Below is updated working code..
-(NSMutableData*)decodeOpusData:(NSData*)data
{
NSMutableData *audioData = [[NSMutableData alloc] init];
for (NSUInteger i = 0; i < 4; i ++)
{
int bufferLength = 40;
if([data length]>= 40){
NSData *subData = [data subdataWithRange:NSMakeRange(i*bufferLength, bufferLength)];
Byte *byteData = (Byte*)malloc(sizeof(Byte)*bufferLength);
memcpy(byteData, [subData bytes], bufferLength);
//You can do anything here with data..........
//Below iam decoding audio data using OPUS library
short decodedBuffer[WB_FRAME_SIZE];
int nDecodedByte = sizeof(short) * [self decode:byteData length:bufferLength output:decodedBuffer];
NSData *PCMData = [NSData dataWithBytes:(Byte *)decodedBuffer length:nDecodedByte ];
[audioData appendData:PCMData];
//Decoding audio data using OPUS library
}
}
return audioData;
}

How to split bytes from NSData?

So.. I have encrypted data from server that need to be decrypted so that I can get the full response JSON. The thing is I need to split the first 16 bytes of data to get the IV for decryption and the rest of the bytes is the encrypted data. I tried below method:
NSData *wholeData = [[NSData alloc] initWithBase64EncodedString:#"IYSaYh92saFT5t/ueQQtlTaFT1oW33FXPLDUsRMATBLUrY/6Z1VGK1KFmyeRwHpbi85T7ZNzDQAl5v8cu60DcJLwVQDI6KdwbmCq0+L62IM7Ixw60+G8gTkm+6+MLtyE" options:0];
NSData *d1 = [wholeData subdataWithRange:NSMakeRange(0, 16)];
NSData *d2 = [wholeData subdataWithRange:NSMakeRange(17, wholeData.length)];
NSData *enc = d2;
NSData *key = [[NSData alloc] initWithBase64EncodedString:#"alskd81039aisdf/tusd8341iasldkjfY=" options:0];
NSData *enciv = d1;
NSData *decrypted = [FBEncryptorAES decryptData:enc key:key iv:enciv];
Then I got below error:
Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSConcreteData subdataWithRange:]: range {17, 96} exceeds data length 96'
How can I fix my issue?
NSData *d2 = [wholeData subdataWithRange:NSMakeRange(17, wholeData.length - 17)];
you can try this
NSData* blob;
NSUInteger length = [blob length];
NSUInteger chunkSize = 100 * 1024;
NSUInteger offset = 0;
do {
NSUInteger thisChunkSize = length - offset > chunkSize ? chunkSize : length - offset;
NSData* chunk = [NSData dataWithBytesNoCopy:(char *)[blob bytes] + offset
length:thisChunkSize
freeWhenDone:NO];
offset += thisChunkSize;
// do something with chunk
} while (offset < length);

who can tell me what the function's affect?

The first method is:
-(NSData *)stringToAddBytes:(NSString*)addString
{
int length = (int)[addString length];
if(length < 2)
{
return nil;
}
Byte buf[length / 2];
for(int i = 0 ;i < length/2 ;i++)
{
NSString *str = [addString substringWithRange:NSMakeRange(i * 2, 2)];
Byte b = [self hexStringToByte:str];
buf[i]=b;
}
NSData * myD = [[NSData alloc]initWithBytes:buf length:length/2];
return myD;
}
THe method that the first method called.
-(Byte)hexStringToByte:(NSString*)str
{
NSArray *charArray = [[NSArray alloc]initWithObjects:#"0",#"1",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",
#"A",#"B",#"C",#"D",#"E",#"F",nil];
NSString *str1 = [str substringWithRange:NSMakeRange(0, 1)];
int num1 = (int)[charArray indexOfObject:str1];
NSString *str2 = [str substringWithRange:NSMakeRange(1, 1)];
int num2 = (int)[charArray indexOfObject:str2];
Byte b = num1*16+num2;
return b;
}
Thank you for your answer.It looks change large char to small char.
hexStringToByte: wil convert string with hexadecimal number representation (example #"FF") to Byte value (in this example 255).
stringToAddBytes: uses hexStringToByte: to create NSData of bytes breaking addString into two letter peases and converting them to Byte values.
In other words, this is string serialization.
Example:
// 255 = 0xFF
// 170 = 0xAA
// 136 = 0x88
NSString* addString = #"FFAA88";
NSData* data = [self stringToAddBytes:addString];
// data will be [255, 170, 136]
Be aware that NSData is not an array, instead, it represents a raw object.

Converting an Sha256 hashed value into NSString

I have a requirement to integrate with a web service that serves as a login. The hash needs to be generated on the client. I am able to produce the correct hash as NSMutableData, but then I need to convert it to a string, without the spaces or brackets produced when the NSMutableData object is rendered as a string in the output console. I have read several posts, all seeming to say the same thing:
NSString *newstring = [[NSString alloc] initWithDSata:dataToConvert encoding:NSUTF8StringEncoding];
Unfortunately, this doesnt work for me. Using NSUTF8StringEncoding returns null. NSASCIIStringEncoding is even worse.
Here is my code:
NSString *password = [NSString stringWithFormat:#"%#%#", kPrefix, [self.txtPassword text]];
NSLog(#"PLAIN: %#", password);
NSData *data = [password dataUsingEncoding:NSASCIIStringEncoding];
NSMutableData *sha256Out = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];
CC_SHA256(data.bytes, data.length, sha256Out.mutableBytes);
NSString *preppedPassword = [[NSString alloc] initWithData:sha256Out encoding:NSASCIIStringEncoding];
NSLog(#"HASH: %#\n", preppedPassword);
How can I convert the NSMutableData to string?
My problem is that I need to from this
<7e8df5b3 17c99263 e4fe6220 bb75b798 4a41de45 44464ba8 06266397 f165742e>
to this
7e8df5b317c99263e4fe6220bb75b7984a41de4544464ba806266397f165742e
See How to convert an NSData into an NSString Hex string?
I use a slightly modified version myself:
#implementation NSData (Hex)
- (NSString *)hexRepresentationWithSpaces:(BOOL)spaces uppercase:(BOOL)uppercase {
const unsigned char *bytes = (const unsigned char *)[self bytes];
NSUInteger nbBytes = [self length];
// If spaces is true, insert a space every this many input bytes (twice this many output characters).
static const NSUInteger spaceEveryThisManyBytes = 4UL;
// If spaces is true, insert a line-break instead of a space every this many spaces.
static const NSUInteger lineBreakEveryThisManySpaces = 4UL;
const NSUInteger lineBreakEveryThisManyBytes = spaceEveryThisManyBytes * lineBreakEveryThisManySpaces;
NSUInteger strLen = 2 * nbBytes + (spaces ? nbBytes / spaceEveryThisManyBytes : 0);
NSMutableString *hex = [[NSMutableString alloc] initWithCapacity:strLen];
for (NSUInteger i = 0; i < nbBytes; ) {
if (uppercase) {
[hex appendFormat:#"%02X", bytes[i]];
} else {
[hex appendFormat:#"%02x", bytes[i]];
}
// We need to increment here so that the every-n-bytes computations are right.
++i;
if (spaces) {
if (i % lineBreakEveryThisManyBytes == 0) {
[hex appendString:#"\n"];
} else if (i % spaceEveryThisManyBytes == 0) {
[hex appendString:#" "];
}
}
}
return hex;
}
#end

Resources